> ## Documentation Index
> Fetch the complete documentation index at: https://docs.yativo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook Events

> Real-time notifications for card issuer program events.

## Setup

Yativo sends webhook notifications to your registered HTTPS endpoint via HTTP POST whenever a program event occurs. Each request is signed with HMAC-SHA256 so you can verify it genuinely came from Yativo.

Register your endpoint at the unified webhook endpoint. Card issuer events and general crypto events share the same service — subscribe to any combination.

```bash theme={null}
curl -X POST 'https://crypto-api.yativo.com/api/v1/webhook/create-webhook' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "url": "https://api.yourapp.com/webhooks/yativo",
    "events": ["customer.funded", "customer.funding.failed", "transaction.authorized", "transaction.settled"],
    "description": "Production card issuer webhook"
  }'
```

```json theme={null}
{
  "status": true,
  "message": "Webhook created successfully",
  "data": {
    "webhook_id": "68241abc2f3d4e5f6a7b8c9d",
    "url": "https://api.yourapp.com/webhooks/yativo",
    "events": ["customer.funded", "customer.funding.failed", "transaction.authorized", "transaction.settled"],
    "secret": "whsec_a8f3c2e1d4b7f9e2c5a3b6d8f1e4c7a2b5d8e1f4c7a2b5d8",
    "created_at": "2026-05-12T14:00:00.000Z"
  }
}
```

<Warning>
  Store the `secret` securely — use a secrets manager or environment variable. The secret is also retrievable later via `GET /v1/yativo-card/webhooks/:webhookId`. If compromised, rotate it via `POST /v1/yativo-card/webhooks/:webhookId/rotate-secret`.
</Warning>

Pass `"events": ["*"]` to receive all event types. Other management endpoints:

| Method   | Path                                                    | Action                                              |
| -------- | ------------------------------------------------------- | --------------------------------------------------- |
| `GET`    | `/v1/yativo-card/webhooks`                              | List all subscriptions                              |
| `GET`    | `/v1/yativo-card/webhooks/:webhookId`                   | Get one subscription (returns secret)               |
| `PUT`    | `/v1/yativo-card/webhooks/:webhookId`                   | Update URL, events, or enabled state                |
| `DELETE` | `/v1/yativo-card/webhooks/:webhookId`                   | Delete subscription                                 |
| `POST`   | `/v1/yativo-card/webhooks/:webhookId/rotate-secret`     | Rotate signing secret                               |
| `GET`    | `/v1/yativo-card/webhooks/:webhookId/deliveries`        | Delivery history for one webhook                    |
| `GET`    | `/v1/yativo-card/webhooks/deliveries/all`               | All deliveries across all webhooks                  |
| `GET`    | `/v1/yativo-card/webhooks/deliveries/:deliveryId`       | Single delivery detail (full payload + attempt log) |
| `POST`   | `/v1/yativo-card/webhooks/deliveries/:deliveryId/retry` | Retry a failed delivery                             |

## Event categories

| Category              | Events                                                                                  |
| --------------------- | --------------------------------------------------------------------------------------- |
| **Master Wallet**     | Deposits, token swaps, customer funding                                                 |
| **Customer Funding**  | Funds received or failed into a customer's card wallet, balance updates                 |
| **Card Lifecycle**    | Card created, activated, frozen, unfrozen, voided, lost, stolen, cancelled, deactivated |
| **Card Transactions** | Authorization, settlement, decline, reversal, refund                                    |

## Event reference

| Event                           | Trigger                                                                                |
| ------------------------------- | -------------------------------------------------------------------------------------- |
| `master_wallet.deposit`         | Funds received into your master wallet                                                 |
| `master_wallet.swap`            | Token swap submitted (e.g. USD → EUR)                                                  |
| `master_wallet.customer_funded` | Master wallet debited to fund a customer                                               |
| `customer.funded`               | Customer's card wallet received funds                                                  |
| `customer.funding.failed`       | Funding transfer failed (wallet misconfigured, insufficient balance, or network error) |
| `customer.balance.updated`      | Customer's card balance changed (after spend, top-up, or authorization)                |
| `card.created`                  | New card issued to a customer                                                          |
| `card.activated`                | Card activated                                                                         |
| `card.frozen`                   | Card frozen                                                                            |
| `card.unfrozen`                 | Card unfrozen                                                                          |
| `card.voided`                   | Card voided (permanently disabled)                                                     |
| `card.lost`                     | Card reported lost                                                                     |
| `card.stolen`                   | Card reported stolen                                                                   |
| `card.cancelled`                | Card cancelled                                                                         |
| `card.deactivated`              | Card deactivated                                                                       |
| `transaction.authorized`        | Card transaction authorized                                                            |
| `transaction.settled`           | Transaction settled                                                                    |
| `transaction.declined`          | Transaction declined                                                                   |
| `transaction.reversed`          | Transaction reversed                                                                   |
| `transaction.refund.created`    | Refund created                                                                         |

## Payload structure

All webhook events share a common envelope. The `data` object contains event-specific fields. The event type is also sent in the `X-Yativo-Event` request header.

```json theme={null}
{
  "id": "evt_1747058400000_abc123",
  "type": "customer.funded",
  "created_at": "2026-05-12T14:30:00.000Z",
  "data": { ... }
}
```

## Payload examples

### `master_wallet.deposit`

You will receive this event **twice** for a single deposit: once immediately when funds are detected (`processing`), and again when settlement is confirmed (`settled`). The payload shape differs between the two states.

**`status: "processing"`** — fired as soon as the incoming deposit is detected.

```json theme={null}
{
  "id": "evt_1747058400000_abc123",
  "type": "master_wallet.deposit",
  "created_at": "2026-05-12T14:00:00.000Z",
  "data": {
    "settlement_id": "dep_1745123456_2ea87af00de1",
    "source_amount": 500.00,
    "tx_hash": "5KtP3MzXYZABCDE1234567890abcdef12345678901234567890abcdef1234567",
    "status": "processing"
  }
}
```

**`status: "settled"`** — fired once the funds are settled into your master wallet.

```json theme={null}
{
  "id": "evt_1747058520000_def456",
  "type": "master_wallet.deposit",
  "created_at": "2026-05-12T14:02:00.000Z",
  "data": {
    "settlement_id": "dep_1745123456_2ea87af00de1",
    "source_amount": 500.00,
    "settled_amount": 499.75,
    "status": "settled"
  }
}
```

### `master_wallet.swap`

Fired when a token swap is submitted from your master wallet.

```json theme={null}
{
  "id": "evt_1747058700000_def456",
  "type": "master_wallet.swap",
  "created_at": "2026-05-12T14:05:00.000Z",
  "data": {
    "swap_id": "0x4a1b2c3d...",
    "from_token": "USD",
    "to_token": "EUR",
    "amount": 1000.00,
    "expected_output": 921.50,
    "status": "submitted"
  }
}
```

### `customer.funded`

Fired when a customer's card wallet has been credited. The `transfer_id` can be looked up via [Get Transfer](/api-reference/issuer/transfer-status).

```json theme={null}
{
  "id": "evt_1747059000000_ghi789",
  "type": "customer.funded",
  "created_at": "2026-05-12T14:10:00.000Z",
  "data": {
    "transfer_id": "tx_1745128800_9fc12ab3e4d5",
    "customer_id": "69f0bdf29a84752db9cc8ff9",
    "amount": 50.00,
    "currency": "USD",
    "status": "completed"
  }
}
```

### `customer.funding.failed`

Fired when a funding transfer to a customer's card wallet fails.

```json theme={null}
{
  "id": "evt_1747059000000_fail01",
  "type": "customer.funding.failed",
  "created_at": "2026-05-12T14:10:00.000Z",
  "data": {
    "customer_id": "69f0bdf29a84752db9cc8ff9",
    "yativo_card_id": "yativo_card_customer_8f9a...",
    "amount": 50.00,
    "currency": "USD"
  }
}
```

### `master_wallet.customer_funded`

Fired alongside `customer.funded` whenever your master wallet is debited. Includes `remaining_balance` when the transfer settles immediately; omitted when the balance updates asynchronously.

```json theme={null}
{
  "id": "evt_1747059001000_mwf01",
  "type": "master_wallet.customer_funded",
  "created_at": "2026-05-12T14:10:00.000Z",
  "data": {
    "transfer_id": "tx_1745128800_9fc12ab3e4d5",
    "customer_id": "69f0bdf29a84752db9cc8ff9",
    "amount": 50.00,
    "currency": "USD",
    "status": "completed",
    "remaining_balance": 114.98
  }
}
```

### `card.frozen`

Fired when a card is frozen, either by your program or by the customer.

```json theme={null}
{
  "id": "evt_1747059600000_mno345",
  "type": "card.frozen",
  "created_at": "2026-05-12T14:20:00.000Z",
  "data": {
    "yativo_card_id": "yativo_card_customer_8f9a...",
    "card_id": "card_token_abc123",
    "status": "Frozen",
    "timestamp": "2026-05-12T14:20:00.000Z"
  }
}
```

<Note>
  The `card_id` field in webhook events is the card token identifier. This may differ from the `card_id` returned by the [Get Customer](/api-reference/issuer/customer) endpoint. Use `yativo_card_id` as the stable cross-reference between webhook events and API responses.
</Note>

### `customer.balance.updated`

Fired whenever the card provider reports a balance change — after a purchase authorization, settlement, top-up, or reversal. Provides the full balance breakdown so you can update your UI in real time without polling.

```json theme={null}
{
  "id": "evt_1747060200000_stu901",
  "type": "customer.balance.updated",
  "created_at": "2026-05-12T14:30:00.000Z",
  "data": {
    "yativo_card_id": "yativo_card_customer_8f9a...",
    "ledger_balance": 45.23,
    "available_balance": 38.50,
    "pending_balance": 6.73,
    "currency": "EUR",
    "timestamp": "2026-05-12T14:30:00.000Z"
  }
}
```

### `transaction.authorized`

Fired when a card transaction is authorized at the point of sale. A `customer.balance.updated` event typically follows immediately.

```json theme={null}
{
  "id": "evt_1747059300000_jkl012",
  "type": "transaction.authorized",
  "created_at": "2026-05-12T14:15:00.000Z",
  "data": {
    "yativo_card_id": "yativo_card_customer_8f9a...",
    "card_id": "card_token_abc123",
    "transaction_id": "tx_9d8e7f...",
    "type": "purchase",
    "amount": 12.50,
    "currency": "EUR",
    "billing_amount": 12.50,
    "billing_currency": "EUR",
    "fx_rate": 1,
    "merchant": "Starbucks",
    "merchant_category": "5812",
    "merchant_city": "Paris",
    "merchant_country": "FR",
    "status": "AUTHORIZED",
    "timestamp": "2026-05-12T14:15:00.000Z"
  }
}
```

<Note>
  `billing_amount`/`billing_currency` reflect the card's own settlement currency and are present on every transaction event, not just foreign-currency ones — on a same-currency purchase like this one they simply equal `amount`/`currency`. See the [full transaction event reference](/guides/card-issuer-webhooks#transaction-event-field-reference) for the foreign-currency case and the `fx_rate` calculation.
</Note>

### `card.lost`

Fired when a card is reported lost.

```json theme={null}
{
  "id": "evt_1747059900000_pqr678",
  "type": "card.lost",
  "created_at": "2026-05-12T14:25:00.000Z",
  "data": {
    "yativo_card_id": "yativo_card_customer_8f9a...",
    "card_id": "card_token_abc123",
    "status": "Lost",
    "timestamp": "2026-05-12T14:25:00.000Z"
  }
}
```

## Signature verification

Every webhook request includes an `X-Yativo-Signature` header and an `X-Yativo-Timestamp` header. The event type is also in the `X-Yativo-Event` header and the unique delivery ID in `X-Yativo-Delivery-Id`. Verify signature and timestamp before processing the event.

The signature is computed over the raw request body (the exact JSON bytes received), prefixed with the timestamp.

```javascript theme={null}
const crypto = require('crypto');

// Use express.raw({ type: 'application/json' }) so req.body is a Buffer, not a parsed object.
// req.headers['x-yativo-signature'] — the HMAC signature
// req.headers['x-yativo-timestamp'] — Unix timestamp (string)
function verifyWebhook(rawBody, signature, timestamp, secret) {
  if (!signature || !timestamp) return false;

  // Reject replays older than 5 minutes
  if (Math.abs(Date.now() / 1000 - parseInt(timestamp, 10)) > 300) return false;

  const signed = `${timestamp}.${rawBody.toString('utf8')}`;
  const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(signed).digest('hex');
  try {
    return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
  } catch {
    return false;
  }
}
```

<Warning>
  Always verify the signature before trusting the payload. Use `express.raw({ type: 'application/json' })` (not `express.json()`) so you have the raw bytes for verification.
</Warning>

## Delivery history

Both delivery history endpoints accept the same query parameters:

| Parameter   | Type   | Default | Description                                            |
| ----------- | ------ | ------- | ------------------------------------------------------ |
| `limit`     | number | `50`    | Number of records to return. Maximum `100`.            |
| `offset`    | number | `0`     | Number of records to skip — use for pagination.        |
| `status`    | string | —       | Filter by delivery status: `success` or `failed`.      |
| `eventType` | string | —       | Filter by event type, e.g. `card.transaction.created`. |

Results are sorted newest first.

```bash Examples theme={null}
# Last 30 deliveries
GET /v1/yativo-card/webhooks/deliveries/all?limit=30

# Only failed deliveries
GET /v1/yativo-card/webhooks/deliveries/all?status=failed

# Failed transaction events, most recent 20
GET /v1/yativo-card/webhooks/deliveries/all?status=failed&eventType=card.transaction.created&limit=20

# Failed deliveries for one specific webhook
GET /v1/yativo-card/webhooks/{webhookId}/deliveries?status=failed
```

Each record in the response includes:

| Field                | Description                                                |
| -------------------- | ---------------------------------------------------------- |
| `delivery_id`        | ID used to fetch details or trigger a manual retry         |
| `event_type`         | The event that triggered this delivery                     |
| `status`             | `success` or `failed`                                      |
| `attempt_count`      | How many delivery attempts have been made                  |
| `last_response_code` | HTTP status code from your endpoint on the last attempt    |
| `delivered_at`       | Timestamp of successful delivery (`null` if still failing) |
| `created_at`         | When the delivery was first attempted                      |

To see the full payload and per-attempt log for a delivery, call `GET /v1/yativo-card/webhooks/deliveries/:deliveryId`.

## Retry policy

Yativo retries failed webhook deliveries using exponential backoff. Respond with any HTTP `2xx` status code to acknowledge receipt and prevent retries.

| Attempt     | Delay after previous attempt |
| ----------- | ---------------------------- |
| 1 (initial) | —                            |
| 2           | 1 minute                     |
| 3           | 5 minutes                    |
| 4           | 30 minutes                   |
| 5           | 2 hours                      |
| 6           | 12 hours                     |
| 7           | 24 hours                     |

After 7 failed attempts the delivery is marked **permanently failed** — no further automatic retries. You can manually replay individual deliveries via `POST /v1/yativo-card/webhooks/deliveries/:deliveryId/retry`.

If your endpoint fails 100 consecutive deliveries, Yativo will automatically disable the subscription to protect your program's event queue. Re-enable it via `PUT /v1/yativo-card/webhooks/:webhookId` with `{ "enabled": true }` after fixing the issue.

<Note>
  To avoid processing duplicate events, use the top-level `id` field (e.g. `evt_1747058400000_abc123`) as an idempotency key. The `X-Yativo-Delivery-Id` header carries the delivery record ID (a separate identifier used to retry individual deliveries via the API).
</Note>
