Preview documentation. The interface is being rolled out — examples are indicative, and we will confirm final values at launch.
Webhooks: signature, retry, replay
Webhooks push commerce events to your endpoint as order status changes, a call
attempt completes, or a result becomes ready. Management base URL:
https://api.callbotagent.ai/api/v1/commerce. Sign configuration requests with
HMAC (X-CBA-Key-Id, X-CBA-Timestamp, X-CBA-Signature); mutations add
Idempotency-Key.
Subscription and secret
GET /webhook-subscription
PUT /webhook-subscription
Read and upsert subscription: url (HTTPS required), event_types list,
enabled. Scope webhooks:write for PUT.
POST /webhook-subscription/secret/rotate
Rotate the signing secret. The new secret is shown only once (reveal-once) in the response — save it immediately; it cannot be read later.
POST /webhook-subscription/test
Sends a test event to the configured url to verify receipt and signature.
url must be a public HTTPS address. Private, loopback, and link-local
addresses are rejected (SSRF protection).
Events and envelope
event_type |
When |
|---|---|
commerce.order.status_changed.v1 |
processing_state of the order changes |
commerce.call.attempt_completed.v1 |
call attempt completed (call_disposition) |
commerce.order.result_ready.v1 |
final_order_snapshot is ready |
commerce.writeback.failed.v1 |
write-back to the store failed |
Every event uses the same envelope (data carries the type-specific payload):
| Field | Description |
|---|---|
event_id |
Unique event identifier (deduplication key). |
event_type / event_version |
Type and schema version, e.g. ...v1. |
occurred_at |
RFC 3339 UTC timestamp. |
integration_ref |
Recipient integration identifier. |
external_order_id |
Your order identifier for this event. |
correlation_id / causation_id |
Correlation and causation in the event chain. |
data |
Payload for event_type. |
Signature and verification
Each delivery includes headers X-CBA-Webhook-Timestamp,
X-CBA-Webhook-Event-Id, X-CBA-Webhook-Signature. The signature is
HMAC-SHA256 over timestamp + "." + event_id + "." + raw_body (raw body, no
re-serialization), using the secret from rotation. Compare in a timing-safe way
and reject events older than a few minutes.
# Recompute signature locally and compare with X-CBA-Webhook-Signature header BASE="$TS.$EVENT_ID.$RAW_BODY" printf '%s' "$BASE" | openssl dgst -sha256 -hmac "$CBA_WEBHOOK_SECRET"
import crypto from "node:crypto";function verify(headers, rawBody, secret) { const base =
${headers["x-cba-webhook-timestamp"]}.+${headers["x-cba-webhook-event-id"]}.${rawBody}; const expected = crypto.createHmac("sha256", secret).update(base).digest("hex"); const got = headers["x-cba-webhook-signature"]; return expected.length === got.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(got)); } // secret from rotation (reveal-once), e.g. process.env.CBA_WEBHOOK_SECRET
function verify(array $headers, string $rawBody, string $secret): bool {
$base = $headers['x-cba-webhook-timestamp'].'.'.
$headers['x-cba-webhook-event-id'].'.'.$rawBody;
$expected = hash_hmac('sha256', $base, $secret);
return hash_equals($expected, $headers['x-cba-webhook-signature']);
}
// $secret from rotation (reveal-once), e.g. getenv('CBA_WEBHOOK_SECRET')Retry, dead-letter, deliveries
Delivery succeeds when the receiver responds with 2xx within the time limit.
Otherwise we retry with increasing backoff: up to 8 attempts within 24 hours.
After exhausting attempts, the event goes to dead-letter and can be replayed
manually.
GET /webhook-deliveries
Delivery history (opaque cursor, limit up to 200): status, response code,
attempt count.
POST /webhook-deliveries/{deliveryId}/replay
Resend a specific delivery. event_id stays the same, so build your receiver
idempotently — deduplicate by event_id.
Undelivered event diagnostics: ../help/troubleshooting/webhooki-niedostarczone.md.