Skip to content

Retry & Delivery Policy ​

Delivery flow ​

  1. When an event occurs, TokenCashFlow creates a webhook_delivery_job record and queues it
  2. The webhook worker picks up the job and sends an HTTP POST to your configured URL
  3. Timeout: 10 seconds per attempt
  4. Success: HTTP 2xx response → job marked success; no further attempts
  5. Failure: Non-2xx response, connection error, or timeout → job marked failed; retry scheduled

Retry schedule ​

Retries use an increasing backoff schedule:

AttemptWait before this attempt
1 (initial)— (immediate)
230 seconds
31 minute
45 minutes
510 minutes
630 minutes
71 hour
82 hours
94 hours
108 hours
(beyond 10)24 hours each — only reachable if your job's max_attempts is raised above the default

After the attempt count reaches the job's max_attempts (default 10), the job transitions to permanently_failed. No further delivery attempts are made automatically — but you can re-queue the job at any time (see below).

Total retry window: at the default max_attempts of 10, the 10 attempts span about 15.5 hours of scheduled waits (30 s + 1 m + 5 m + 10 m + 30 m + 1 h + 2 h + 4 h + 8 h = 55,790 seconds) from the first attempt to the last — budget your receiver's recovery window accordingly.

After permanent failure ​

Once a job reaches permanently_failed:

  • The delivery log is retained and viewable in Dashboard → Webhooks → Delivery History
  • You can retry it manually — click Retry in the portal or call POST /v1/webhooks/deliveries/{job_id}/retry. The attempt counter resets and the delivery gets up to max_attempts fresh attempts.
  • You can still retrieve payment status via GET /v1/payments/{payment_id} to reconcile your system

If your endpoint was down for an extended period, query the API to reconcile any events you may have missed.

If no active webhook configuration exists for your account (or the config was deactivated), pending jobs are marked permanently_failed immediately rather than retried.

What counts as success ​

Any HTTP response in the 2xx range (200–299) is treated as success. The body content is ignored.

Your endpoint should return a 2xx status quickly — do not perform long-running operations synchronously in the webhook handler. Acknowledge the webhook immediately, then process asynchronously.

Idempotency ​

Webhooks are delivered at-least-once. Duplicate deliveries are rare but possible (e.g. your endpoint returned 2xx but the network timed out before TokenCashFlow received the response).

Design your handler to be idempotent:

python
def handle_payment_completed(payment_id: str, net_amount_usd: float):
    # Check if already processed
    if Order.objects.filter(payment_id=payment_id, status="fulfilled").exists():
        return  # Already handled — safe to ignore

    # Process for the first time
    order = Order.objects.get(payment_id=payment_id)
    order.status = "fulfilled"
    order.net_amount = net_amount_usd
    order.save()
    send_fulfillment_email(order)

Use data.payment_id + event as a natural deduplication key (or the X-TCF-Delivery header, which uniquely identifies a single delivery job).

Monitoring delivery health ​

  • Dashboard: Client Portal → Webhooks → Delivery History shows per-event status and attempt counts
  • API: GET /v1/webhooks/deliveries?status=permanently_failed to programmatically check for failures
  • Alert threshold: If too many deliveries permanently fail, contact support to investigate your endpoint health

TokenCashFlow Documentation