1. Partners
CIAO API
  • Getting Started
    • Introduction
    • Authentication
    • Responses and Error
  • Partners
    • Webhooks
    • Redemption Intent (Partners)
      • Create Redemption Intent
      • Retrieve Redemption Intent
    • Subscriptions
      • Create Subscription
      • Retrieve a subscription
      • Cancel Subscription
  • Resellers
    • Webhooks
    • Voucher Generation (Resellers)
      • Create Voucher Payment
      • Retrieve Voucher Payment
  • General
    • Get all Counties
      GET
  1. Partners

Webhooks

Configure your webhook URL in the partner dashboard. Test and live environments use separate URLs
and only receive events from matching API keys.

Delivery#

Webhooks are sent as POST requests with Content-Type: application/json.
Important: Return any 2xx status promptly. Failed deliveries are retried — the same event may arrive more than once. Handle events idempotently.

Retries#

Failed deliveries (timeout, connection error, 5xx, or 429) are retried until the retry window
expires:
EnvironmentFirst 4 retriesSubsequent retriesRetry until
Live60 seconds apart1 hour apart24 hours after first attempt
Test60 seconds apart60 seconds apart5 minutes after first attempt
Responses with other 4xx status codes (except 429) are not retried.

Verify signatures#

Important: Always verify x-ciao-signature before processing a webhook. Reject requests with invalid signatures.
Every webhook includes:
x-ciao-signature: <hex>
Compute the expected value as:
HMAC-SHA256(raw_request_body, your_api_token)
Where raw_request_body is the exact JSON bytes received in the POST body and your_api_token
is the API secret for the matching environment (sk_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.

Envelope#

All events share this structure:
{
  "event": "subscription.activated",
  "data": { },
  "created_at": "2026-07-01T10:05:00.000000Z"
}
FieldDescription
eventEvent type (see below)
dataEvent-specific payload
created_atWhen the webhook was dispatched

Events#

EventWhen
redemption-intent.successStandalone redemption completed
redemption-intent.chargebackA redeemed voucher was charged back
subscription.activatedFirst subscription cycle redeemed
subscription.renewedRenewal cycle redeemed
subscription.completedFinal cycle of a finite subscription
subscription.cancelledSubscription canceled

Subscription cycle events#

Important: Cycle redemptions do not send a separate redemption-intent.success. The cycle's redemption intent is nested in data.redemption_intent. Correlate subscriptions using data.reference.

redemption-intent.success#

Sent when a standalone redemption intent completes checkout.
{
  "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#

Sent when a voucher used for a redemption is charged back.
{
  "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#

First successful billing cycle.
{
  "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.renewed#

Sent for each renewal after the first cycle.
Same shape as subscription.activated, with current_cycle incremented and status still active.
The nested redemption_intent.cycle matches current_cycle.

subscription.completed#

Sent when the final cycle of a finite subscription is redeemed. status becomes completed.
Same payload shape. current_cycle equals total_cycles. No further cycle webhooks follow.

subscription.cancelled#

Sent when the subscription reaches a canceled terminal state.
{
  "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
Modified at 2026-08-26 15:38:16
Previous
Partners
Next
Redemption Intent (Partners)
Built with