Webhook Reference
Complete reference for the JARAI webhook system. Covers the event envelope, signature verification, all 9 event types with payload schemas, delivery behaviour, and schema stability guarantees.
Event Envelope
Every webhook delivery sends a JSON POST request with the following envelope structure:
| Field | Type | Description |
|---|---|---|
eventId | UUID | Unique identifier for this event delivery. |
eventType | string | Event type identifier (e.g. production.published). |
productionId | UUID | The production this event relates to. Nil UUID for webhook.test. |
accountId | UUID | The account that owns the production. Nil UUID for webhook.test. |
timestamp | string | ISO 8601 UTC timestamp of when the event occurred. |
sequenceNumber | integer | Per-production event counter. Use with productionId for idempotency. |
correlationId | string | Trace identifier for debugging with JARAI support. |
truncated | boolean | true only on deliverable.ready when the deliverables list exceeds 100 items. |
data | object | Event-specific payload. Structure varies by event type. |
Signature Verification
Every webhook delivery includes an X-JARAI-Signature header for authenticating that the request originated from JARAI.
Header Format
X-JARAI-Signature: sha256=a1b2c3d4e5f6789012345678abcdef... Verification Steps
- Read the raw HTTP request body as bytes. Do not parse and re-serialize the JSON — re-serialization may alter whitespace or field ordering.
- Compute HMAC-SHA256 using your
signingSecret(base64url-decoded) as the key and the raw body bytes as the message. - Hex-encode the resulting digest.
- Compare with the value after
sha256=in the header using a constant-time comparison to prevent timing attacks.
If verification fails, return a 4xx status code and do not process the event.
Event Types
production.queued
Trigger: Production submitted to the pipeline.
| Field | Type | Description |
|---|---|---|
data.status | string | Always "Queued". |
data.expiresAt | string | null | ISO 8601 UTC expiry timestamp for API-triggered productions. null for non-API productions. |
production.step.complete
Trigger: A pipeline step finished processing.
| Field | Type | Description |
|---|---|---|
data.status | string | Always "Producing". |
data.stepsComplete | integer | Number of pipeline steps completed so far. |
data.currentStep | string | Human-readable label of the step that just completed. |
production.awaiting_approval
Trigger: All pipeline gates passed. Deliverables available for review, awaiting operator release.
| Field | Type | Description |
|---|---|---|
data.status | string | Always "AwaitingApproval". |
data.stepsComplete | integer | Total pipeline steps completed. |
production.distributing
Trigger: First platform publication started.
| Field | Type | Description |
|---|---|---|
data.status | string | Always "Distributing". |
data.platforms | array | Per-platform publication status. |
data.platforms[].platform | string | Platform name. |
data.platforms[].status | string | Platform-level status (e.g. "Publishing", "Pending"). |
production.published
Trigger: All platforms published successfully. This is a terminal state.
| Field | Type | Description |
|---|---|---|
data.status | string | Always "Published". |
data.platforms | array | Final per-platform publication status. |
data.platforms[].platform | string | Platform name. |
data.platforms[].status | string | Always "Published" at this stage. |
data.platforms[].platformPostId | string | null | Platform-assigned post/video identifier. null if the platform does not provide one. |
production.failed
Trigger: Production failed terminally. This is a terminal state.
| Field | Type | Description |
|---|---|---|
data.status | string | Always "Failed". |
data.errorCode | string | Machine-readable error code. See Production Error Codes. |
data.errorMessage | string | Human-readable error description. |
production.cancelled
Trigger: Production cancelled by partner or operator. This is a terminal state.
| Field | Type | Description |
|---|---|---|
data.status | string | Always "Cancelled". |
deliverable.ready
Trigger: Deliverable files are available for download.
| Field | Type | Description |
|---|---|---|
data.status | string | Current production status (typically "AwaitingApproval"). |
data.deliverables | array | List of available deliverable files. |
data.deliverables[].deliverableId | UUID | Unique identifier for this deliverable. |
data.deliverables[].type | string | One of: Video, ClosedCaptions, AudioDescription, Transcript, ComplianceCertificate. |
data.deliverables[].platform | string | null | Platform name for platform-specific deliverables (e.g. YouTube, TikTok). null for production-level deliverables. |
data.deliverables[].format | string | File format: mp4, srt, vtt, txt, docx, or pdf. |
Note: The deliverable.ready event does not include SAS download URLs. Call
GET /v1/productions/{productionId}/deliverables to obtain time-limited download URLs.
If the production has more than 100 deliverables, the truncated field in the envelope is true and only the first 100 items are included. Use the deliverables API endpoint to retrieve the full list.
webhook.test
Trigger: Ping test sent via POST /v1/webhooks/{subscriptionId}/ping.
| Field | Type | Description |
|---|---|---|
data.type | string | Always "webhook.test". |
data.message | string | Human-readable test message. |
productionId and accountId are set to nil UUIDs (00000000-0000-0000-0000-000000000000) for test events.
Delivery Behaviour
Retry Schedule
JARAI guarantees at-least-once delivery via Azure Service Bus. If your endpoint does not return a 2xx response, the event is retried:
| Attempt | Timing |
|---|---|
| 1st retry | Immediate |
| 2nd retry | 1 minute after first failure |
| 3rd retry | 5 minutes after second failure |
After all three retries fail, the delivery counts as one failure and the subscription's failureCount is incremented.
Failure Counting and Auto-Suspension
| Consecutive Failures | Action |
|---|---|
| 3 | WebhookDeliveryFailed warning alert raised internally. |
| 10 | Subscription status set to Failed. No further events delivered. WebhookEndpointUnreachable alert raised. |
A successful delivery (2xx response) resets the failure counter to zero.
In-Flight Deletion
If you delete a webhook subscription while events are already queued for delivery, those in-flight events will be delivered normally. No new events are dispatched after the deletion commits. Subscriptions are logically deleted (never physically removed) for audit trail purposes.
Schema Stability Contract
JARAI commits to the following guarantees for webhook payloads:
- Additive-only changes — new fields may be added to event payloads at any time. Existing fields will not be removed or have their types changed without notice.
- 6-month deprecation notice — if a field or event type is to be removed, JARAI will provide at least 6 months of advance notice via the
X-API-Deprecation-Noticeresponse header and developer portal announcements. - Ignore unknown fields — your webhook handler should ignore any fields it does not recognise. Do not fail on unexpected fields.
Idempotency Guidance
Because JARAI guarantees at-least-once delivery, your endpoint may receive the same event more than once.
Use the composite key {productionId}:{sequenceNumber} to deduplicate events.
The sequenceNumber is scoped to a single production (not globally unique).
Store processed composite keys and skip any event where the key has already been seen.