Event Integration

Partner Event API Authentication

Learn how to authenticate requests to the CHeKT Partner Event API

Partner Event API Authentication

The Partner Event API uses OAuth 2.0 Client Credentials Grant with JWT assertions for M2M (Machine-to-Machine) authentication. This guide explains how to set up authentication for sending events to CHeKT.

Different from Partner API

While both Partner API and Partner Event API use OAuth 2.0, they have different authentication requirements:

  • Partner API: Requires Authorization Code Flow for Dealer Connect + M2M for other endpoints
  • Partner Event API: Only requires M2M authentication (no Authorization Code Flow)

See Partner API Authentication for Partner API details.


Overview

The Partner Event API authentication is simpler than the Partner API because it only requires M2M authentication. There's no need for the Authorization Code Flow.

Authentication Method: OAuth 2.0 Client Credentials Grant with JWT Bearer Assertions


Authentication Flow

sequenceDiagram
    participant Partner as Your Platform
    participant Auth as CHeKT Auth Server
    participant EventAPI as Event API

    Partner->>Partner: Create JWT Assertion
    Partner->>Partner: Sign with M2M Secret
    Partner->>Auth: POST /oauth/token (with assertion)
    Auth->>Auth: Verify signature
    Auth->>Partner: Return access_token
    Partner->>EventAPI: POST /partner/v1/events (Bearer token)
    EventAPI->>Partner: Return upload URLs

Step 1: Register Auth Application

Before using the Partner Event API, you must register an Auth Application for event ingestion with CHeKT.

Registration Requirements

Contact CHeKT support to register your Auth Application. You will need to provide:

  • Application Name: Your platform or company name
  • Public Key: Your RSA public key for JWT signing verification
  • Additional Information:
    • Event types you'll send (alarm, trouble, arming)
    • Expected event volume
    • Platform technical details

What You Receive

After registration, you will receive:

  • Client ID: Your application identifier
  • Secret Key: Your private secret for signing JWT assertions
  • Auth Server URL: The token endpoint for authentication

Keep Credentials Secure

  • Store your secret key securely - never commit it to version control
  • Treat credentials like passwords
  • Use separate credentials for development and production
  • Rotate keys periodically for enhanced security

Step 2: Create JWT Assertion Token

Generate a JWT assertion token signed with your M2M secret key.

JWT Claims

Your JWT must include these claims:

{
  "iss": "YOUR_CLIENT_ID",
  "sub": "YOUR_CLIENT_ID",
  "aud": "https://auth.chekt.com",
  "exp": 1234567890,
  "iat": 1234567800,
  "jti": "unique-jwt-id"
}

Claim Descriptions:

ClaimDescription
issIssuer - your Client ID
subSubject - your Client ID
audAudience - CHeKT auth server URL
expExpiration time (Unix timestamp, typically 5 minutes from iat)
iatIssued at time (Unix timestamp)
jtiJWT ID - unique identifier to prevent replay attacks

Sign with Secret Key

Sign the JWT using your M2M secret key with the RS256 algorithm:

Node.js Example:

const jwt = require('jsonwebtoken');
const crypto = require('crypto');

// Your M2M credentials
const CLIENT_ID = process.env.CHEKT_EVENT_CLIENT_ID;
const SECRET_KEY = process.env.CHEKT_EVENT_SECRET_KEY;

function createJWTAssertion() {
  const now = Math.floor(Date.now() / 1000);

  const claims = {
    iss: CLIENT_ID,
    sub: CLIENT_ID,
    aud: 'https://auth.chekt.com',
    exp: now + 300,  // Expires in 5 minutes
    iat: now,
    jti: crypto.randomUUID()
  };

  return jwt.sign(claims, SECRET_KEY, { algorithm: 'RS256' });
}

const assertion = createJWTAssertion();
console.log('JWT Assertion:', assertion);

Python Example:

import jwt
import time
import uuid
import os

# Your M2M credentials
CLIENT_ID = os.getenv('CHEKT_EVENT_CLIENT_ID')
SECRET_KEY = os.getenv('CHEKT_EVENT_SECRET_KEY')

def create_jwt_assertion():
    now = int(time.time())

    claims = {
        "iss": CLIENT_ID,
        "sub": CLIENT_ID,
        "aud": "https://auth.chekt.com",
        "exp": now + 300,  # Expires in 5 minutes
        "iat": now,
        "jti": str(uuid.uuid4())
    }

    return jwt.encode(claims, SECRET_KEY, algorithm="RS256")

assertion = create_jwt_assertion()
print(f"JWT Assertion: {assertion}")

Assertion Expiration

JWT assertions should have a short expiration time (5 minutes recommended). This limits the window for potential replay attacks.


Step 3: Exchange Assertion for Access Token

Send the JWT assertion to CHeKT's token endpoint to receive an access token.

Request

curl -X POST https://auth.chekt.com/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
  -d "assertion=YOUR_JWT_ASSERTION"

Parameters:

ParameterValue
grant_typeurn:ietf:params:oauth:grant-type:jwt-bearer
assertionYour signed JWT assertion token

Response

Success (200 OK):

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600
}

Response Fields:

FieldDescription
access_tokenThe access token to use for API requests
token_typeAlways "Bearer"
expires_inToken lifetime in seconds (typically 3600 = 1 hour)

Step 4: Use Access Token for API Requests

Include the access token in the Authorization header for all Event API requests.

Request Headers

Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

Example: Send Event

curl -X POST https://api.chekt.com/partner/v1/events \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "version": "1.0",
    "location_id": "Z6RSOG",
    "recorder_id": "Z6RANV",
    "camera_id": "2fe519b4-2438-4aa1-b051-87cf22bb",
    "event_id": "1763603245534315",
    "event_type": "alarm",
    "event_status": "active",
    "event_time": "2025-11-20T01:46:25.534Z",
    "sent_at": "2025-11-20T01:46:28.534Z",
    "should_signal_to_monitoring_center": true,
    "upload_snapshot_count": 1,
    "upload_short_mp4": false,
    "upload_event_mp4": true,
    "payload": {}
  }'

Token Management

Token Caching

Access tokens are valid for a limited time (typically 1 hour). To optimize performance:

  • Cache the access token in memory
  • Reuse the token until it expires
  • Generate a new token only when the current one expires

Implementation Example:

class EventAPIClient {
  constructor(clientId, secretKey) {
    this.clientId = clientId;
    this.secretKey = secretKey;
    this.accessToken = null;
    this.tokenExpiry = 0;
  }

  async getAccessToken() {
    // Check if current token is still valid
    if (this.accessToken && Date.now() < this.tokenExpiry) {
      return this.accessToken;
    }

    // Generate new JWT assertion
    const assertion = this.createJWTAssertion();

    // Exchange for access token
    const response = await fetch('https://auth.chekt.com/oauth/token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${assertion}`
    });

    const data = await response.json();

    // Cache token with safety margin (refresh 1 minute early)
    this.accessToken = data.access_token;
    this.tokenExpiry = Date.now() + (data.expires_in * 1000) - 60000;

    return this.accessToken;
  }

  createJWTAssertion() {
    // Implementation from Step 2
    // ...
  }

  async sendEvent(eventData) {
    const token = await this.getAccessToken();

    return fetch('https://api.chekt.com/partner/v1/events', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(eventData)
    });
  }
}

Token Refresh Strategy

When an access token expires:

  1. You'll receive a 401 Unauthorized response
  2. Generate a new JWT assertion
  3. Request a new access token
  4. Retry the API request with the new token

Automatic Retry

Implement automatic token refresh and request retry in your API client to handle token expiration gracefully.


Authentication Errors

Common Error Responses

401 Unauthorized

{
  "error": "Unauthorized",
  "message": "Invalid or expired access token"
}

Solution: Generate a new access token using a fresh JWT assertion

403 Forbidden

{
  "error": "Forbidden",
  "message": "Insufficient permissions for event ingestion"
}

Solution: Verify your Auth Application has event ingestion permissions

400 Bad Request - Invalid Assertion

{
  "error": "invalid_grant",
  "error_description": "Invalid JWT assertion"
}

Possible Causes:

  • JWT signature is invalid (wrong secret key)
  • JWT has expired
  • JWT claims are malformed
  • aud claim doesn't match auth server

Security Best Practices


Complete Authentication Example

Here's a complete example of authenticating and sending an event:

const jwt = require('jsonwebtoken');
const axios = require('axios');

// Configuration
const CLIENT_ID = process.env.CHEKT_EVENT_CLIENT_ID;
const SECRET_KEY = process.env.CHEKT_EVENT_SECRET_KEY;
const AUTH_SERVER = 'https://auth.chekt.com';
const API_BASE = 'https://api.chekt.com';

// Token cache
let cachedToken = null;
let tokenExpiry = 0;

// Generate JWT assertion
function createAssertion() {
  return jwt.sign(
    {
      iss: CLIENT_ID,
      sub: CLIENT_ID,
      aud: AUTH_SERVER,
      exp: Math.floor(Date.now() / 1000) + 300,
      iat: Math.floor(Date.now() / 1000),
      jti: require('crypto').randomUUID()
    },
    SECRET_KEY,
    { algorithm: 'RS256' }
  );
}

// Get access token (with caching)
async function getAccessToken() {
  // Return cached token if still valid
  if (cachedToken && Date.now() < tokenExpiry) {
    return cachedToken;
  }

  // Generate new assertion
  const assertion = createAssertion();

  // Exchange for access token
  const response = await axios.post(
    `${AUTH_SERVER}/oauth/token`,
    new URLSearchParams({
      grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
      assertion: assertion
    }),
    {
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
    }
  );

  // Cache token
  cachedToken = response.data.access_token;
  tokenExpiry = Date.now() + (response.data.expires_in * 1000) - 60000; // Refresh 1 min early

  return cachedToken;
}

// Send event
async function sendEvent(eventData) {
  const accessToken = await getAccessToken();

  try {
    const response = await axios.post(
      `${API_BASE}/partner/v1/events`,
      eventData,
      {
        headers: {
          'Authorization': `Bearer ${accessToken}`,
          'Content-Type': 'application/json'
        }
      }
    );

    return response.data;
  } catch (error) {
    // Handle 401 by refreshing token and retrying
    if (error.response?.status === 401) {
      cachedToken = null; // Invalidate cache
      const newToken = await getAccessToken();

      const retryResponse = await axios.post(
        `${API_BASE}/partner/v1/events`,
        eventData,
        {
          headers: {
            'Authorization': `Bearer ${newToken}`,
            'Content-Type': 'application/json'
          }
        }
      );

      return retryResponse.data;
    }

    throw error;
  }
}

// Usage
const eventData = {
  version: "1.0",
  location_id: "Z6RSOG",
  recorder_id: "Z6RANV",
  camera_id: "2fe519b4-2438-4aa1-b051-87cf22bb",
  event_id: "1763603245534315",
  event_type: "alarm",
  event_status: "active",
  event_time: "2025-11-20T01:46:25.534Z",
  sent_at: "2025-11-20T01:46:28.534Z",
  should_signal_to_monitoring_center: true,
  upload_snapshot_count: 1,
  upload_short_mp4: false,
  upload_event_mp4: true,
  payload: {}
};

sendEvent(eventData)
  .then(result => console.log('Event sent:', result))
  .catch(error => console.error('Error:', error.response?.data || error.message));

Next Steps

Now that you understand authentication, you can start sending events:

Next Steps


Support

For authentication issues or questions: