Webhooks

Webhooks notify your application in real-time when events occur in Chain — payments completing, card authorizations, business verification updates, and more.

Setup

You can manage webhook endpoints via the API or from the Chain Dashboard.

1

Create a webhook endpoint with your HTTPS URL and the events you want to subscribe to.

2

Store the secret returned in the response — it's only shown once and is used to verify webhook signatures.

3

Implement signature verification in your endpoint handler (see below).

Manage Webhook Endpoints

Create, list, and delete webhook endpoints programmatically.

POST/v1/webhooks
ParameterTypeDescription
urlrequiredstringEndpoint URL (must be HTTPS).
eventsrequiredstring[]Array of event types to subscribe to.
descriptionstringOptional description (max 200 characters).
Create webhook endpoint
curl -X POST https://api.chain.com/v1/webhooks \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks/chain",
    "events": ["funding.completed", "payout.completed", "card.authorization.request"],
    "description": "Production webhook handler"
  }'
Response
{
  "id": "wh_abc123",
  "url": "https://example.com/webhooks/chain",
  "events": ["funding.completed", "payout.completed", "card.authorization.request"],
  "description": "Production webhook handler",
  "status": "active",
  "secret": "whsec_abc123...",
  "created_at": "2026-01-15T10:00:00Z"
}

Store the secret

The secret is only returned when the endpoint is created. Store it securely — you'll need it to verify webhook signatures.
GET/v1/webhooks

List all webhook endpoints. Secrets are masked in the response.

DELETE/v1/webhooks?endpointId=wh_abc123

Delete a webhook endpoint. Pass the endpoint ID as a query parameter.

Signature Verification

Every webhook includes a X-Chain-Signature header containing an HMAC-SHA256 signature. Always verify this signature to confirm the event came from Chain.

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)
  );
}

// In your route handler:
app.post('/webhooks/chain', (req, res) => {
  const signature = req.headers['x-chain-signature'];
  const isValid = verifyWebhook(
    JSON.stringify(req.body),
    signature,
    process.env.CHAIN_WEBHOOK_SECRET
  );

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  // Process event...
  res.status(200).send('OK');
});

Event Types

Payments

EventDescription
funding.initiatedA funding request has been created.
funding.completedFunds have been received and converted.
funding.failedA funding request has failed.
payout.initiatedA payout has been created.
payout.completedPayout has been delivered to the recipient.
payout.failedA payout has failed.

Cards

EventDescription
card.createdA new card has been issued.
card.authorization.requestReal-time authorization request (respond within 2s).
card.transaction.completedA card transaction has settled.
card.frozenA card has been frozen.
card.cancelledA card has been cancelled.

Business & Compliance

EventDescription
business.verification.approvedBusiness KYB verification approved.
business.verification.rejectedBusiness KYB verification rejected.
compliance.screening.flaggedA compliance screening flagged an issue.
report.readyA generated report is ready for download.

Payload Format

All webhook payloads follow a consistent structure.

Webhook payload
{
  "id": "evt_abc123",
  "type": "funding.completed",
  "created_at": "2026-01-15T10:30:00Z",
  "data": {
    "id": "fund_123abc",
    "status": "completed",
    "amount": 10000,
    "currency": "USD",
    "target_amount": 9985.50,
    "target_currency": "USDC"
  }
}

Retry Policy

Idempotency

Your webhook handler should be idempotent. Chain may deliver the same event more than once. Use the id field to deduplicate.
AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry24 hours

After 5 failed attempts, the webhook is marked as failed and the endpoint may be disabled. You can retry failed webhooks from the Dashboard.

globe
icon

Pax Dollar

USDP

icon

Ripple USD

RLUSD

icon

Dollar

USD

icon

Tether

USDT

icon

Pounds

GBP

icon

Euro

Eur