Webhooks
Nội dung này hiện chưa có sẵn bằng ngôn ngữ của bạn.
Webhooks
Section titled “Webhooks”Webhooks push events to your server as they happen, instead of you polling the API. Manage endpoints from Organization Settings → Webhooks in the dashboard, or via /api/v1/webhooks with a write:webhooks-scoped key.
Registering an endpoint
Section titled “Registering an endpoint”POST /api/v1/webhooksContent-Type: application/jsonIdempotency-Key: <uuid>
{ "url": "https://example.com/hooks/clienta", "events": ["conversation.created", "message.received"]}{ "success": true, "data": { "id": "wh_endpoint_123", "url": "https://example.com/hooks/clienta", "events": ["conversation.created", "message.received"], "status": "active", "consecutiveFailures": 0, "createdAt": "2026-07-16T04:00:00.000Z", "secret": "3n9qYVs8h1kLp0..." }}Registration requires POST /api/v1/webhooks to carry an Idempotency-Key — see Idempotency.
Event types
Section titled “Event types”| Event | Fires when |
|---|---|
conversation.created | A new conversation starts |
message.received | A new message is received in a conversation |
handoff.requested | A conversation is escalated for human handoff |
ticket.created | A fallback ticket is created |
conversation.resolved | A conversation is marked resolved |
An endpoint only receives events it subscribed to via its events list — no wildcard subscription. A typo’d event name is rejected at registration time (400), not silently accepted and never fired.
Payload format
Section titled “Payload format”Every delivery body is a versioned envelope:
{ "type": "conversation.created", "schemaVersion": 1, "orgId": "org_abc123", "createdAt": "2026-07-16T04:00:00.000Z", "data": { "conversationId": "conv_456", "channelId": "chan_789", "externalUserId": "ext_user_1" }}data is a strict per-event-type allowlist — only the fields documented below are ever included, regardless of what the underlying record contains internally.
| Event | data fields |
|---|---|
conversation.created | conversationId, channelId, externalUserId |
message.received | conversationId, messageId, role (user|assistant|agent|system) |
handoff.requested | conversationId, escalationLogId, reason? |
ticket.created | ticketId, conversationId?, reason? |
conversation.resolved | conversationId, resolvedBy |
Always verify type against the event types you actually handle, and check schemaVersion if you maintain multiple payload-shape handlers going forward.
Delivery guarantees
Section titled “Delivery guarantees”Delivery is at-least-once — design for idempotent handling on your end (key off the delivery’s event, not “did I receive exactly one call”).
- An event is written durably first, decoupled from delivery — a crash between “event happened” and “webhook sent” cannot lose the event.
- It’s fanned out into one delivery per subscribed, active endpoint.
- Each delivery is attempted with a leased worker — if the worker crashes mid-attempt, the lease expires and another worker safely picks it up. You will never see a delivery silently vanish because of a worker crash, though you may occasionally see the same delivery attempted twice in that specific crash window — treat delivery
id/eventIdas your dedupe key if that matters to you.
Retry policy
Section titled “Retry policy”| Attribute | Value |
|---|---|
| Max attempts per delivery | 8 |
| Backoff | Exponential — 30s base, doubling each attempt, capped at 1 hour |
| Terminal states | succeeded, exhausted (max attempts reached), skipped (endpoint disabled) |
An endpoint that racks up 10 consecutive delivery failures is automatically disabled (status: "auto_disabled") — further events are marked skipped for that endpoint rather than retried forever against a dead receiver. Re-enable it by re-registering (or contact support if you need the existing endpoint reactivated).
Delivery history
Section titled “Delivery history”GET /api/v1/webhooks/{id}/deliveries?limit=20{ "success": true, "data": [ { "id": "whd_1", "eventId": "evt_1", "status": "succeeded", "nextRetryAt": null, "terminalAt": "2026-07-16T04:00:02.000Z", "terminalReason": null, "lastError": null, "createdAt": "2026-07-16T04:00:00.000Z" } ], "nextCursor": null}Cursor-paginated — see Versioning → Pagination.
Sending a test event
Section titled “Sending a test event”POST /api/v1/webhooks/{id}/testIdempotency-Key: <uuid>Sends a real webhook.test event through the actual signing and delivery path to that one endpoint — useful for verifying your receiver’s signature verification before subscribing to real events.
Removing an endpoint
Section titled “Removing an endpoint”DELETE /api/v1/webhooks/{id}Deleting an endpoint that’s already gone returns 204 — it’s a no-op, not an error, so retries are always safe.
Rotating a webhook secret
Section titled “Rotating a webhook secret”There’s no in-place “rotate secret” call — a secret is bound to the endpoint it was issued for. To rotate:
- Register a new endpoint with the same
urlandevents. - Verify the new endpoint receives and correctly verifies a test event.
- Delete the old endpoint.
Content Change Notifications
Section titled “Content Change Notifications”In addition to webhooks, clienta.ai can send email notifications when imported content changes significantly. This applies to URL imports, Google Sheets sync, and RSS feeds.
- Trigger: Content diff exceeds the configured threshold (default: 20%)
- Recipients: All organization admins
- Rate limit: Maximum 1 notification per source URL per 24 hours
- Configuration: Adjustable per organization via organization settings
Content change notifications are separate from webhook events — they use email delivery via the platform’s email service, not HTTP callbacks.