API Reference
Webhooks
A POST notification to your server when a generation finishes — body format, HMAC-SHA256 signature check, response deadline and the redelivery schedule.
Pass a webhook_url when you submit a generation, and Clipia sends a POST to that address when the job finishes. You don't need to poll the status. Every delivery is signed with HMAC-SHA256 using your key's secret, and a failed delivery is retried for about a day.
URL requirements
https://only. Addresses on internal or private networks are rejected at submit time with400 webhook_url_invalid.- Redirects aren't followed: a
3xxresponse counts as a failed delivery.
Body
Success:
{
"request_id": "764cabcf-b745-4b3e-ae38-1200304cf45b",
"status": "OK",
"payload": {
"model": "nano-banana-2",
"output": { "images": [{ "url": "https://media.clipia.ai/....webp" }] },
"cost": 12
}
}Generation failure:
{
"request_id": "764cabcf-b745-4b3e-ae38-1200304cf45b",
"status": "ERROR",
"error": {
"code": "generation_failed",
"message": "Generation failed. Please retry or contact support."
}
}output depends on the model type: images: [{ url }], video: { url } or audio: { url, format? } together with audios: [{ url, format? }] (every track variant, the first one equals audio). error.code comes from the generation error dictionary: generation_failed, content_moderated, model_unavailable and others; treat an unknown code as generation_failed.
The webhook body is shorter than the REST response
Webhooks carry no original_url or thumbnail_url, and images come with a single link. Fetch the full result with GET /v1/requests/:id.
Prop
Type
Headers
Content-Type: application/json
X-Clipia-Webhook-Id: 1f2e3d4c-5b6a-7890-abcd-ef0123456789
X-Clipia-Timestamp: 1760011200
X-Clipia-Signature: t=1760011200,v1=5257a869e7...| Header | Value |
|---|---|
X-Clipia-Webhook-Id | delivery ID, the same across all retries |
X-Clipia-Timestamp | Unix time of this attempt, in seconds |
X-Clipia-Signature | t=<timestamp>,v1=<hex HMAC-SHA256> |
Signing secret
Each API key has its own secret, used to sign webhooks for jobs submitted with that key. The secret is shown once, when you create the key in the Developer Console. The console has no delivery history or manual resend. If you lose the secret, create a new key and switch your integration to it — see Managing keys.
Verifying the signature
The signature is HMAC_SHA256(secret, "{timestamp}.{raw_body}"). Verify it against the raw request body before parsing JSON, compare in constant time, and reject deliveries older than 5 minutes. The easiest way is the ready-made SDK function.
import { verifyWebhookSignature } from 'clipia-ai/webhook';
// rawBody is the original body bytes, not JSON.stringify(req.body)
const ok = verifyWebhookSignature({
secret: process.env.CLIPIA_WEBHOOK_SECRET!,
headers: req.headers,
body: rawBody,
toleranceSeconds: 300,
});
if (!ok) return res.status(401).end();Both SDK functions return true or false and never throw on malformed headers.
Delivery and retries
- Respond with
2xxwithin 10 seconds. Do heavy processing after you respond. - A non-
2xxresponse, a3xx, or a timeout counts as a failure. Delivery is attempted up to 15 times in total. - The pause between attempts doubles, starting at 30 seconds, capped at 4 hours: 30 s, 1, 2, 4, 8, 16, 32 min, 1 h 04 min, 2 h 08 min, then 4 hours each — about 24 hours 15 minutes in total from the first attempt. Deliveries are dispatched every 30 seconds, so the actual delay can be slightly longer.
- After the last failed attempt the delivery is marked as failed and is not retried again; it cannot be requested anew. If your handler was down for longer, fetch the result with
GET /v1/requests/:id— it is stored independently of the webhook.
Handle deliveries idempotently
The same delivery can arrive more than once, for example if your response got lost. Deduplicate by X-Clipia-Webhook-Id or request_id before triggering side effects.
In the sandbox, webhooks are delivered and signed the same way — a convenient way to test your handler. A test key gets at most one delivery per minute: the rest within that minute are not created, and a failed delivery stops after three attempts.
Idempotency
The Idempotency-Key header makes retrying a generation submit safe — the same request returns the same request_id with no second job and no second charge.
Rate limits
Clipia API rate limits — 120 requests per minute per key and 600 per account, RateLimit-* headers, the 429 response, concurrency limits and LLM limits.