POST requests with Content-Type: application/json.Important: Return any 2xxstatus promptly. Failed deliveries are retried — the same event may arrive more than once. Handle events idempotently.
5xx, or 429) are retried until the retry window| Environment | First 4 retries | Subsequent retries | Retry until |
|---|---|---|---|
| Live | 60 seconds apart | 1 hour apart | 24 hours after first attempt |
| Test | 60 seconds apart | 60 seconds apart | 5 minutes after first attempt |
4xx status codes (except 429) are not retried.Important: Always verify x-ciao-signaturebefore processing a webhook. Reject requests with invalid signatures.
x-ciao-signature: <hex>HMAC-SHA256(raw_request_body, your_api_token)raw_request_body is the exact JSON bytes received in the POST body and your_api_tokensk_test_… or sk_live_…).Important: Use constant-time comparison when verifying signatures. Sign the raw request body exactly as received — do not decode and re-encode JSON before hashing.
{
"event": "subscription.activated",
"data": { },
"created_at": "2026-07-01T10:05:00.000000Z"
}| Field | Description |
|---|---|
event | Event type (see below) |
data | Event-specific payload |
created_at | When the webhook was dispatched |
| Event | When |
|---|---|
redemption-intent.success | Standalone redemption completed |
redemption-intent.chargeback | A redeemed voucher was charged back |
subscription.activated | First subscription cycle redeemed |
subscription.renewed | Renewal cycle redeemed |
subscription.completed | Final cycle of a finite subscription |
subscription.cancelled | Subscription canceled |
Important: Cycle redemptions do not send a separate redemption-intent.success. The cycle's redemption intent is nested indata.redemption_intent. Correlate subscriptions usingdata.reference.
redemption-intent.success{
"event": "redemption-intent.success",
"data": {
"reference": "RI-01J8Z9ABC",
"amount": 2500,
"status": "success",
"authorization_url": "https://app.ciao.cx/redemptions/checkout/XyZ789AbC123456",
"access_code": "XyZ789AbC123456",
"metadata": { "order_id": "ORD-123" },
"callback_url": null,
"customer": { "email": "customer@example.com" },
"redeemed_at": "2026-07-01T10:05:00.000000Z",
"created_at": "2026-07-01T10:00:00.000000Z",
"updated_at": "2026-07-01T10:05:00.000000Z"
},
"created_at": "2026-07-01T10:05:01.000000Z"
}redemption-intent.chargeback{
"event": "redemption-intent.chargeback",
"data": {
"reference": "RI-01J8Z9ABC",
"amount": 2500,
"metadata": { "order_id": "ORD-123" },
"customer": { "email": "customer@example.com" },
"chargeback": {
"reference": "CB-01J8Z9XYZ",
"reason": "Customer dispute",
"created_at": "2026-07-15T08:00:00.000000Z"
},
"redeemed_at": "2026-07-01T10:05:00.000000Z",
"created_at": "2026-07-01T10:00:00.000000Z",
"updated_at": "2026-07-15T08:00:00.000000Z"
},
"created_at": "2026-07-15T08:00:01.000000Z"
}subscription.activated{
"event": "subscription.activated",
"data": {
"reference": "SUB-01J8Z9ABC",
"amount": 1000,
"status": "active",
"interval": "month",
"interval_count": 1,
"total_cycles": 12,
"current_cycle": 1,
"authorization_url": "https://app.ciao.cx/subscriptions/checkout/AbC123XyZ456789",
"access_code": "AbC123XyZ456789",
"metadata": { "plan": "gold" },
"callback_url": null,
"customer": { "email": "customer@example.com" },
"current_period_start": "2026-07-01T10:05:00.000000Z",
"current_period_end": "2026-08-01T10:05:00.000000Z",
"canceled_at": null,
"environment": "live",
"created_at": "2026-07-01T10:00:00.000000Z",
"updated_at": "2026-07-01T10:05:00.000000Z",
"redemption_intent": {
"reference": "RI-01J8Z9DEF",
"cycle": 1,
"amount": 1000,
"status": "success",
"redeemed_at": "2026-07-01T10:05:00.000000Z",
"created_at": "2026-07-01T10:05:00.000000Z",
"updated_at": "2026-07-01T10:05:00.000000Z"
}
},
"created_at": "2026-07-01T10:05:01.000000Z"
}subscription.renewedsubscription.activated, with current_cycle incremented and status still active.redemption_intent.cycle matches current_cycle.subscription.completedstatus becomes completed.current_cycle equals total_cycles. No further cycle webhooks follow.subscription.cancelled{
"event": "subscription.cancelled",
"data": {
"reference": "SUB-01J8Z9ABC",
"amount": 1000,
"status": "canceled",
"interval": "month",
"interval_count": 1,
"total_cycles": 12,
"current_cycle": 3,
"authorization_url": "https://app.ciao.cx/subscriptions/checkout/AbC123XyZ456789",
"access_code": "AbC123XyZ456789",
"metadata": { "plan": "gold" },
"callback_url": null,
"customer": { "email": "customer@example.com" },
"current_period_start": "2026-07-01T10:05:00.000000Z",
"current_period_end": "2026-08-01T10:05:00.000000Z",
"cancelled_at": "2026-07-10T14:00:00.000000Z",
"environment": "live",
"created_at": "2026-07-01T10:00:00.000000Z",
"updated_at": "2026-07-10T14:00:00.000000Z",
"redemption_intent": null
},
"created_at": "2026-07-10T14:00:01.000000Z"
}Important: For live subscriptions billed through a reseller, this fires after the reseller confirms cancellation — not when you first call Cancel Subscription