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.
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"
}'
{
"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"
}
}
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.
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.
{
"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.
{
"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.
{
"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.
{
"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.
{
"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.
{
"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.
{
"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.
{
"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"
}
}
The card_id field in webhook events is the card token identifier. This may differ from the card_id returned by the Get Customer endpoint. Use yativo_card_id as the stable cross-reference between webhook events and API responses.
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.
{
"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.
{
"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"
}
}
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 for the foreign-currency case and the fx_rate calculation.
card.lost
Fired when a card is reported lost.
{
"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.
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;
}
}
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.
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.
# 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.
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).