KLIQ|Developers

Webhooks

Receive real-time notifications when events occur in your KLIQ tenant. Webhooks are the recommended way to react to observation processing, CV job completion, and other events.

Setup

POST/v1/tenants/{tenantId}/webhooksCreate a webhook subscription
GET/v1/tenants/{tenantId}/webhooksList webhook subscriptions
DELETE/v1/tenants/{tenantId}/webhooks/{id}Delete a webhook subscription
const webhook = await tenant.webhooks.create({
url: 'https://your-server.com/api/kliq-webhook',
events: ['observation.created', 'cv.job.completed', 'cv.job.failed'],
secret: 'whsec_your_signing_secret',
});

console.log(webhook.id); // "wh_abc123"

Event types

EventDescription
observation.createdA new observation was submitted
observation.updatedAn observation was modified
cv.job.completedA CV job finished successfully
cv.job.failedA CV job failed
visit.submittedA field visit was submitted

Payload format

All webhook payloads follow this structure:

{
  "id": "evt_abc123",
  "type": "cv.job.completed",
  "tenantId": "t_xyz789",
  "timestamp": "2026-04-28T12:00:00Z",
  "data": {
    "jobId": "job_xyz789",
    "observationId": "obs_abc123",
    "model": "shelf-detection-v2",
    "status": "completed",
    "result": {
      "detections": [...],
      "summary": {...}
    }
  }
}

Signature verification

Every webhook request includes an X-Kliq-Signature header containing an HMAC-SHA256 signature of the request body. Always verify this signature to ensure the webhook came from KLIQ.

Express (Node.js)

import express from 'express';
import crypto from 'crypto';

const app = express();
app.use(express.json({ verify: (req: any, _res, buf) => { req.rawBody = buf; } }));

app.post('/api/kliq-webhook', (req: any, res) => {
const signature = req.headers['x-kliq-signature'] as string;
const expected = crypto
  .createHmac('sha256', process.env.WEBHOOK_SECRET!)
  .update(req.rawBody)
  .digest('hex');

if (signature !== expected) {
  return res.status(401).json({ error: 'Invalid signature' });
}

const event = req.body;

switch (event.type) {
  case 'cv.job.completed':
    console.log('CV job completed:', event.data.jobId);
    // Process results...
    break;
  case 'cv.job.failed':
    console.error('CV job failed:', event.data.jobId);
    break;
}

res.status(200).json({ received: true });
});

Retry policy

If your endpoint returns a non-2xx status code or times out (30 seconds), KLIQ retries with exponential backoff:

RetryDelay
1st1 minute
2nd5 minutes
3rd30 minutes
4th2 hours
5th24 hours

After 5 failed retries, the webhook is disabled and you will receive an email notification.

Testing locally

Use tools like ngrok or webhook.site to test webhooks during development:

# Start ngrok tunnel
ngrok http 3000

# Use the ngrok URL as your webhook endpoint
# https://abc123.ngrok.io/api/kliq-webhook

Next steps