Developers
Webhooks
Receive signed notifications when generations finish, Credits change or payments succeed, and verify every delivery.
Webhooks send an HTTPS POST to your server when something happens in your account, so you don't have to poll. Endpoints are managed under Account › Webhooks or with the REST API.
Events
| Event | Sent when | data fields |
|---|---|---|
generation.queued | A generation starts | job_id, creation_id, status, model_id |
generation.completed | A generation succeeds | job_id, creation_id, status, model_id |
generation.failed | A generation fails | job_id, creation_id, status, model_id, failure_code |
credits.updated | Your Credit balance changes | balance, credit_sequence |
billing.payment_succeeded | A payment succeeds | invoice_no, amount (minor units), currency, purchase_kind |
subscription.updated | Your subscription changes | subscription_no, status, plan_id, current_period_end |
support_request.updated | A support request is updated | request_id, kind, status |
Payloads carry identifiers and statuses only, never prompts, file URLs or contact details. Fetch the full resource with the API when you need it, for example GET /creations/{creationId} after generation.completed.
Every delivery has the same envelope:
{
"event_id": "…",
"type": "generation.completed",
"created_at": "2026-11-13T10:24:05.000Z",
"schema_version": 1,
"data": {
"job_id": "…",
"creation_id": "…",
"status": "succeeded",
"model_id": "seedream-5-5"
}
}Add an endpoint
Register the URL
In Account › Webhooks, select Add endpoint, enter the Endpoint URL and choose the Events to send. The URL must be a public https address.
Save the signing secret
The endpoint's signing secret (whsec_…) is shown once, under Copy your signing secret. Store it with your server's secrets.
Verify the endpoint
A new endpoint is Not verified and receives nothing until you verify it. Select Verify now: we send a signed webhook.verification event whose data holds a challenge. Your endpoint must answer with status 2xx and the same value as JSON:
{ "challenge": "<the value you received>" }The challenge expires after a few minutes. On success the endpoint shows Verified and starts receiving events.
Verify signatures
Deliveries follow the Standard Webhooks specification. Each request has three headers:
webhook-id: the event'sevent_id;webhook-timestamp: when this attempt was signed, in Unix seconds;webhook-signature: the signature of the raw body with your endpoint's secret.
Verify every delivery against the raw request body before trusting it. Any Standard Webhooks library does this, and also rejects old timestamps:
import { Webhook } from 'standardwebhooks';
const webhook = new Webhook(process.env.WEBHOOK_SECRET); // whsec_…
export async function handleWebhook(request: Request) {
const body = await request.text();
const event = webhook.verify(body, {
'webhook-id': request.headers.get('webhook-id') ?? '',
'webhook-timestamp': request.headers.get('webhook-timestamp') ?? '',
'webhook-signature': request.headers.get('webhook-signature') ?? '',
}); // throws if the signature is invalid
if (event.type === 'webhook.verification') {
return Response.json({ challenge: event.data.challenge });
}
// Process the event, then answer quickly.
return new Response(null, { status: 204 });
}Delivery behavior
- Return status
2xxpromptly: any 4xx/5xx status, redirect, connection timeout, or network error is recorded as a failed delivery. Defer long-running work to asynchronous background queues after sending the HTTP response. - Retry schedule: failed deliveries retry exponentially across predefined intervals (~5s, 5m, 30m, 2h, 5h, 10h). If the final attempt fails, delivery status transitions to Failed.
- At-least-once delivery: events may be delivered more than once with identical
event_id. Maintain an idempotency store of processedevent_idrecords to discard duplicates. - Ordering: event arrival order is not strictly guaranteed. Rely on
created_attimestamps, and forcredits.updated, the monotonically increasingcredit_sequenceto ignore stale updates.
Monitor and fix deliveries
Each endpoint shows its Recent deliveries: Queued, Sending, Delivered, Retrying or Failed, with the attempt number, the last HTTP status and the next attempt time.
Failed deliveries are not sent again. After an outage, reconcile with the API, for example GET /generations or GET /credits/history.
From the endpoint you can also:
- Edit events to change what it receives;
- Rotate secret to replace the signing secret: deliveries are signed with the new secret from then on, so update your server first;
- Delete the endpoint, which stops deliveries and removes its history.
