POST requests to your configured endpoint when a generation task is created, starts running, completes, fails, or is canceled.
Webhook delivery is at least once. Your handler must verify the signature, reject replayed requests, and process events idempotently.
Configure Endpoints
Configure webhook endpoints from the developer dashboard: https://skills.video/dashboard/developer Use the dashboard to manage endpoint URLs, event filters, enablement, test deliveries, and secret rotation. Store the signing secret in server-side secret storage when it is shown. Do not place the webhook secret in frontend code, mobile apps, logs, or public repositories. Each endpoint is scoped to a workspace and event filter. It only receives events for the selected workspace and selected task lifecycle events.When Webhooks Are Sent
Webhooks are emitted for generation tasks created through:Event Types
Supported event types:| Event | Meaning |
|---|---|
task.created | A generation task was accepted and queued. |
task.started | The worker started processing the task. |
task.completed | The task finished successfully. |
task.failed | The task failed. |
task.canceled | The task was canceled. |
task.completed, task.failed, and task.canceled. Once your local task record reaches a terminal state, do not let an older non-terminal event move it backward.
Webhook event names describe lifecycle transitions. prediction.state and prediction.status describe the current task state in the payload.
| Webhook event | prediction.state | Terminal |
|---|---|---|
task.created | queued | No |
task.started | running | No |
task.completed | succeeded | Yes |
task.failed | failed | Yes |
task.canceled | canceled | Yes |
prediction.state to update your local task record.
Delivery Format
Your endpoint receives an HTTPPOST request with a JSON body.
| Header | Description |
|---|---|
webhook-id | Standard Webhooks event id. Use this as your primary idempotency key. |
webhook-timestamp | Standard Webhooks Unix timestamp in seconds. Used for replay protection and signature verification. |
webhook-signature | Standard Webhooks signature in the format v1,<base64>. Recommended for new integrations. |
X-Webhook-Signature | Legacy v1 HMAC signature in the format v1=<hex>. Kept for backward compatibility. |
X-Webhook-Timestamp | Legacy v1 timestamp. Same value as webhook-timestamp. |
X-Webhook-Event-Id | Legacy v1 event id. Same value as webhook-id. |
X-Webhook-Event-Type | Event type, such as task.completed. |
Content-Type | Always application/json. |
task.failed, prediction.error contains a sanitized error object with code and message. Raw provider errors and internal error details are not included. For task.completed, generated media is usually available in prediction.artifacts and, depending on the model, task output fields returned by the generation result endpoint.
Failed task example:
Payload Field Reference
Common fields:| Field | Type | Description |
|---|---|---|
event | string | Webhook event type, such as task.completed. |
prediction.id | string | Task document id. Use this to correlate with generation results. |
prediction.state | string | Current task state: queued, running, succeeded, failed, or canceled. |
prediction.status | string | Same state value as prediction.state. |
prediction.input | object | Normalized model input that was used for the task. |
prediction.template | object | null | Template metadata when the task was created from a template. |
prediction.error | object | null | Sanitized error details for failed tasks, usually with code and message. |
prediction.logs | any | null | Provider or worker logs when available. |
prediction.created_at | string | null | Task creation timestamp. |
prediction.started_at | string | null | Worker start timestamp. |
prediction.completed_at | string | null | Terminal timestamp for completed, failed, or canceled tasks. |
prediction.workspace | string | null | Workspace document id. |
prediction.owner | object | null | Basic owner profile for the task. |
prediction.artifacts | object[] | Generated assets associated with the task. |
prediction.credits.available | number | Remaining available credits after the task update. |
prediction.credits.usage | number | Credits used by the task. |
documentId, state, type, provider, model, prompt, actual_prompt, resolution, aspect_ratio, url, and asset.
Signature Algorithm
Each webhook endpoint has a secret. Store it on your server and use it to verify every incoming webhook before parsing or processing the JSON payload. New integrations should use the Standard Webhooks v2 headers. Legacy v1X-Webhook-* headers are still sent for backward compatibility with existing receivers.
Standard Webhooks v2
skills.video sends Standard Webhooks-compatible headers:standardwebhooks verifier checks the timestamp tolerance and compares signatures safely. Your route still needs the raw body; do not mount a JSON parser before the webhook route.
Java / Spring Boot Verification
This example verifies Standard Webhooks v2 with JDK crypto APIs. Keep the body as raw bytes and parse JSON only after verification succeeds.Legacy v1 Compatibility
The signature is computed from the timestamp and the exact raw request body:- Use the value of
X-Webhook-Timestampexactly as received. - Use the raw request body exactly as received. Do not verify against parsed and re-serialized JSON.
- Use HMAC-SHA256 with the endpoint secret as the HMAC key.
- Compare signatures with a constant-time comparison function.
- Reject requests whose timestamp is outside a short tolerance window, such as 5 minutes.
- Verify the signature before trusting
X-Webhook-Event-Id,X-Webhook-Event-Type, or any body fields.
Legacy v1 Node.js Verification
This example verifies the signature and timestamp. Mount the raw body parser before any JSON parser for the webhook route.Python Verification
Signature Test Vector
Use this fixed example to verify your implementation. The exactraw_body string matters.
timestamp + "." + raw_body, and the expected legacy signature for the same example is:
Signature Troubleshooting
Most signature failures come from verifying different bytes than the bytes that were signed.| Symptom | Likely cause | Fix |
|---|---|---|
| Signature works locally but fails in production | Proxy, framework, or middleware changed the request body. | Capture the raw body before JSON parsing and verify that exact string. |
Signature fails after JSON.parse and JSON.stringify | Re-serialized JSON changed whitespace or field order. | Verify against raw body only. Parse JSON after signature verification. |
| Every request fails after secret rotation | Receiver still uses the old secret or only one server was updated. | Deploy the new secret everywhere. During rollout, support both old and new secrets. |
| Requests fail intermittently with timestamp errors | Server clock drift or too narrow tolerance. | Sync server time with NTP and use a tolerance such as 5 minutes. |
| Signature header looks malformed | Missing v1, prefix or invalid base64 digest for Standard Webhooks v2. | Prefer the standardwebhooks verifier and pass the raw body plus webhook-* headers. |
| Duplicate events trigger duplicate work | Event id is not stored before business logic runs. | Insert webhook-id with a unique constraint before side effects. |
Replay Protection
Signature verification proves the request was signed with the endpoint secret, but a valid signed request can still be replayed for a short period. Combine timestamp checks with event id deduplication. Recommended checks:- Reject requests with missing or invalid signature headers.
- Reject requests whose
webhook-timestampis outside your tolerance window, such as 5 minutes. - Insert
webhook-idinto a table with a unique constraint before doing any business work. - If the insert conflicts, treat the event as already received and return
2xx.
webhook-id as the replay and idempotency key. Legacy receivers can use X-Webhook-Timestamp and X-Webhook-Event-Id, which carry the same values.
Keep event ids for at least the full retry window. A 24-hour retention period is a practical minimum for most integrations.
Idempotency
Webhook delivery uses an at-least-once model. Your endpoint must assume these cases can happen:- The same event is delivered more than once.
- A delivery succeeds in your system, but the HTTP response is lost and skills.video retries.
- Events for the same task arrive out of order because older deliveries are retried later.
- A terminal event is processed before an older
task.createdortask.startedretry.
| Layer | Key | Purpose |
|---|---|---|
| Event receipt | webhook-id | Prevent processing the same webhook event twice. |
| Business side effects | prediction.id + action | Prevent duplicate actions such as asset import, user notification, order fulfillment, or downstream job creation. |
Recommended Tables
Processing Flow
- Read the raw request body.
- Verify
webhook-signatureandwebhook-timestamp. - Parse the JSON body.
- Insert
webhook-idintoskills_video_webhook_events. - If the insert conflicts, return
204 No Content. - Upsert your local task record by
prediction.id. - Apply terminal-state protection so older non-terminal events do not overwrite a completed, failed, or canceled task.
- Before executing any side effect, insert
(prediction.id, action)intoskills_video_task_side_effects. - Only run the side effect if that insert succeeds.
- Return a
2xxresponse quickly.
Responses And Retries
Return any2xx status code to acknowledge delivery. 204 No Content is recommended.
Non-2xx responses, network failures, and timeouts are retried.
| Behavior | Value |
|---|---|
| Delivery timeout | 30 seconds |
| Maximum delivery attempts | 5 total attempts |
| Retry delays after failed attempts | Approximately 1 minute, 5 minutes, 15 minutes, then 1 hour |
| Case | Response | Notes |
|---|---|---|
| Valid event processed | 204 | Acknowledge quickly. |
| Duplicate event id | 204 | Duplicate delivery is not an error. |
| Invalid signature | 401 | Do not parse or process the payload. |
| Invalid JSON | 400 | Log a short request summary without secrets. |
| Temporary internal failure | 500 | Let skills.video retry. Keep processing idempotent. |
| Business record not ready yet | 202 or 204 | Store the event and reconcile asynchronously. |
2xx after the event has been durably recorded.
Test Events
Webhook endpoint test deliveries use the same signature headers. Test events have:204 after signature verification.
Delivery Logs
Use the developer dashboard to inspect the 20 most recent delivery attempts for a webhook endpoint: https://skills.video/dashboard/developer Delivery log entries contain fields like this:| Field | Description |
|---|---|
id | Webhook delivery document id. |
eventId | Unique event id sent in webhook-id and X-Webhook-Event-Id. |
eventType | Event type sent in X-Webhook-Event-Type. |
taskId | Related task id when available. |
status | Delivery state: pending, processing, success, or failed. |
attempts | Number of delivery attempts already made. |
httpStatus | Last HTTP status returned by your endpoint. |
signatureVersion | Signing scheme used for that delivery attempt, when recorded. Older logs may be null. |
signedPayloadFormat | Canonical signed payload format used for that delivery attempt, when recorded. Older logs may be null. |
error | Last network or HTTP error summary. |
nextRetryAt | Next scheduled retry time, if the delivery is pending retry. |
createdAt | Delivery record creation time. |
success means your endpoint returned a 2xx response. failed means the delivery exhausted retries or could not be queued. pending means it is waiting for its next attempt. processing means a worker has claimed the delivery.
Create Request Idempotency
Webhook idempotency is supported throughwebhook-id on delivered events. Legacy receivers can use the equivalent X-Webhook-Event-Id. Do not rely on Idempotency-Key for generation create requests unless your integration has confirmed support for that header.
If your client retries POST /api/v1/generation/..., store the first returned task id and reconcile by that id before retrying from your own backend. This prevents your application from creating duplicate jobs when the first request succeeded but the client connection failed.
Implementation Checklist
- Configure one endpoint per destination URL and workspace in the developer dashboard.
- Store the endpoint secret in server-side secret storage.
- Verify Standard Webhooks v2 signatures with
webhook_id + "." + timestamp + "." + raw_body. - Reject timestamps outside your replay window.
- Insert
webhook-idbefore running business logic. - Guard terminal task states from older non-terminal retries.
- Make side effects idempotent with
prediction.id + action. - Return
2xxafter durable receipt and process slow work asynchronously. - Use delivery logs to inspect failed attempts and retry timing.
Security Checklist
- Accept only
POSTrequests. - Accept only
Content-Type: application/json. - Verify signatures before parsing JSON or executing business logic.
- Enforce a timestamp tolerance window.
- Deduplicate by
webhook-id. - Make task-level side effects idempotent.
- Put a maximum raw body size on the webhook route.
- Store webhook secrets only in server-side secret storage.
- Do not log webhook secrets, full signatures, API keys, or sensitive user payloads.
- Rotate the webhook secret immediately if exposure is suspected.