Skip to main content

Get the key in Console

Sign in to Console → Settings → Webhook security and select Show key, then Copy key. Store it only in your server secret configuration, for example APISTABLE_WEBHOOK_SECRET. It is an account-level signing key, separate from API keys; replacing an API key does not change it. The key is hidden by default and is not included in ordinary settings responses. Open account settings

Signature format

Read X-APIstable-Timestamp (Unix seconds), X-APIstable-Signature and X-APIstable-Event-Id. Compute HMAC-SHA256 using the exact copied key as a UTF-8 string, over timestamp + ”.” followed by the unmodified request body bytes. Encode the digest as lowercase hexadecimal. The header contains v1=<64 hex characters>. Do not hex-decode the key or sign a parsed/re-serialized JSON body. This protocol differs from Kie: the full body is signed, not only the task ID.
  • Reject missing/malformed signatures and timestamps more than 300 seconds in the past or future. Keep the receiver clock synchronized.
  • Use a constant-time comparison. During key rotation, the signature header contains comma-separated v1 values; accept the request if any value matches your configured key.

Node.js verification

With Express, use express.raw({type:“application/json”}) on the callback route before any express.json() middleware. Pass req.body as a Buffer and req.get(…) header values. Call verifyWebhook before parsing JSON.

Python verification

For Flask, pass request.get_data() bytes and request.headers values. Verify before calling request.get_json(). Both examples return false on an invalid signature; reject it with HTTP 401.

Deduplication and processing

After verification, parse the JSON and require X-APIstable-Event-Id to equal the signed body’s data.task_id. The current event ID is the task ID and remains the same on retries; the timestamp and signature can change per delivery. Persist the event ID with a unique constraint and enqueue work in the same transaction. Return 2xx after durable acceptance; return 2xx for already accepted duplicates. A five-minute timestamp window alone does not prevent replay within that window. Read the selected model’s callback format to distinguish task success and failure. Song success sends the model result directly; other terminal callbacks use the common task envelope. Query the task by ID if a callback is missed. Delivery retries do not create generation tasks or additional charges.

Reset and update the key

Reset key in Console requires confirmation and takes effect immediately. Copy the new key to every receiver. For 24 hours, callbacks carry signatures made with both old and new keys; after the displayed UTC expiry, only the new key is used. Another reset is blocked during the transition. Keep verifiers capable of checking all v1 values. The old key remains usable during this transition; if it was exposed, contact support for incident handling. Do not rotate the platform master secret as an account-key reset.