Skip to main content
Webhooks fire on every state transition, delivering real-time updates to your server. Configure your webhook URL in the dashboard.

Event format

Every webhook delivery has the same envelope:
{
  "eventId": "evt_abc123",
  "type": "intent.confirmed",
  "createdAt": "2026-04-06T12:00:00Z",
  "livemode": true,
  "data": {
    "intent": { /* full PaymentIntent object */ }
  }
}

Event types

EventTrigger
intent.createdIntent created
intent.pendingTransaction broadcast to chain
intent.confirmedSufficient confirmations
intent.finalizedChain-level finality
intent.failedOn-chain failure (revert, program error)
intent.expiredTTL elapsed without fulfillment
intent.cancelledExplicitly cancelled
intent.retry_requiredTransaction dropped, fulfiller must re-sign
intent.detectedFulfiller acknowledged audio receipt

Verifying signatures

Every delivery includes an X-PF-Signature header signed with your webhook secret using HMAC-SHA256 (same pattern as Stripe):
import crypto from 'crypto';

function verifyWebhook(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}
Your webhook secret is available in the dashboard.

Delivery guarantees

  • Retry policy: Exponential backoff, ~10 attempts over up to 72 hours
  • Ordering: Best-effort. Events may arrive out of order — always confirm current state via GET /v1/intents/:id before acting
  • Deduplication: Each event has a stable eventId. The same event may be delivered more than once — deduplicate on eventId

Managing deliveries

List recent deliveries and retry failed ones via the API:
# List recent deliveries
curl https://api.cherp.dev/v1/webhooks \
  -H "Authorization: Bearer pk_live_..."

# Retry a failed delivery
curl -X POST https://api.cherp.dev/v1/webhooks/del_abc123/retry \
  -H "Authorization: Bearer pk_live_..."