Skip to main content

Webhooks

Webhooks allow you to receive real-time notifications when events occur in your FYATU integration. Instead of polling for updates, your server receives HTTP POST requests when payments are received, cards are created, eSIMs are purchased, and more.

Setting Up Webhooks

Configure your webhook URL in two ways:
  1. App-level webhook - Set in your app settings (takes priority)
  2. Business-level webhook - Set in business settings (fallback)
# Update app webhook URL
curl -X PATCH "https://api.fyatu.com/api/v3/apps/{appId}/settings" \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "webhookUrl": "https://your-server.com/webhooks/fyatu",
    "webhookSecret": "your_webhook_secret"
  }'

Webhook Structure

All webhook payloads follow this structure:
{
  "event": "collection.received",
  "version": "2.0",
  "sign": "hmac_sha256_signature",
  "data": {
    "reference": "COL678A3B4C5D6E7",
    "amount": 100.00,
    "appId": "YOUR_APP_ID",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

Collection Events

collection.initiated

Triggered when a new payment collection is created.
{
  "event": "collection.initiated",
  "version": "2.0",
  "sign": "...",
  "data": {
    "reference": "COL678A3B4C5D6E7",
    "externalReference": "ORDER-12345",
    "amount": 100.00,
    "currency": "USD",
    "description": "Order #12345",
    "checkoutUrl": "https://checkout.fyatu.com?batch=COL678A3B4C5D6E7",
    "expiresAt": "2026-01-12T19:30:00+00:00",
    "status": "PENDING",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

collection.received

Triggered when a payment is successfully received.
{
  "event": "collection.received",
  "version": "2.0",
  "sign": "...",
  "data": {
    "reference": "COL678A3B4C5D6E7",
    "externalReference": "ORDER-12345",
    "providerReference": "PAY-20260112-ABC123",
    "amount": 100.00,
    "charge": 1.90,
    "netAmount": 98.10,
    "currency": "USD",
    "paymentMethod": "FYATU_BALANCE",
    "customerEmail": "[email protected]",
    "customerPhone": "+12025559999",
    "customerName": "John Doe",
    "status": "COMPLETED",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

collection.failed

Triggered when a collection fails to process.
{
  "event": "collection.failed",
  "version": "2.0",
  "sign": "...",
  "data": {
    "reference": "COL678A3B4C5D6E7",
    "externalReference": "ORDER-12345",
    "amount": 100.00,
    "currency": "USD",
    "reason": "Insufficient funds",
    "status": "FAILED",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

collection.expired

Triggered when a collection expires without payment.
{
  "event": "collection.expired",
  "version": "2.0",
  "sign": "...",
  "data": {
    "reference": "COL678A3B4C5D6E7",
    "externalReference": "ORDER-12345",
    "amount": 100.00,
    "currency": "USD",
    "status": "EXPIRED",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

Payout Events

payout.initiated

Triggered when a payout is created.
{
  "event": "payout.initiated",
  "version": "2.0",
  "sign": "...",
  "data": {
    "reference": "PAY678A3B4C5D6E7",
    "externalReference": "PAYOUT-12345",
    "amount": 50.00,
    "charge": 0.50,
    "currency": "USD",
    "accountNumber": "CLT123456789",
    "accountName": "Jane Doe",
    "country": "CD",
    "status": "PENDING",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

payout.completed

Triggered when a payout is successfully completed.
{
  "event": "payout.completed",
  "version": "2.0",
  "sign": "...",
  "data": {
    "reference": "PAY678A3B4C5D6E7",
    "externalReference": "PAYOUT-12345",
    "amount": 50.00,
    "charge": 0.50,
    "netAmount": 49.50,
    "currency": "USD",
    "accountNumber": "CLT123456789",
    "accountName": "Jane Doe",
    "status": "COMPLETED",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

payout.failed

Triggered when a payout fails.
{
  "event": "payout.failed",
  "version": "2.0",
  "sign": "...",
  "data": {
    "reference": "PAY678A3B4C5D6E7",
    "externalReference": "PAYOUT-12345",
    "amount": 50.00,
    "currency": "USD",
    "reason": "Recipient account not found",
    "status": "FAILED",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

Refund Events

refund.initiated

Triggered when a refund is created.
{
  "event": "refund.initiated",
  "version": "2.0",
  "sign": "...",
  "data": {
    "refundId": "REF678A3B4C5D6E7",
    "collectionId": "COL678A3B4C5D6E7",
    "amount": 100.00,
    "currency": "USD",
    "reason": "CUSTOMER_REQUEST",
    "reasonDescription": "Customer changed their mind",
    "mode": "FULL",
    "recipient": {
      "clientId": "CLT123456789",
      "name": "John Doe"
    },
    "status": "PENDING",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

refund.completed

Triggered when a refund is successfully processed.
{
  "event": "refund.completed",
  "version": "2.0",
  "sign": "...",
  "data": {
    "refundId": "REF678A3B4C5D6E7",
    "collectionId": "COL678A3B4C5D6E7",
    "amount": 100.00,
    "currency": "USD",
    "reason": "CUSTOMER_REQUEST",
    "reasonDescription": "Customer changed their mind",
    "recipient": {
      "clientId": "CLT123456789",
      "name": "John Doe"
    },
    "status": "COMPLETED",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

refund.failed

Triggered when a refund fails.
{
  "event": "refund.failed",
  "version": "2.0",
  "sign": "...",
  "data": {
    "refundId": "REF678A3B4C5D6E7",
    "collectionId": "COL678A3B4C5D6E7",
    "amount": 100.00,
    "currency": "USD",
    "reason": "Insufficient business balance",
    "status": "FAILED",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

eSIM Events

esim.purchased

Triggered when an eSIM is purchased.
{
  "event": "esim.purchased",
  "version": "2.0",
  "sign": "...",
  "data": {
    "esimId": "ESM678A3B4C5D6E7",
    "iccid": "8901234567890123456",
    "batch": "BES678A3B4C5D6E7",
    "packageName": "France 5GB - 30 Days",
    "dataAmount": "5 GB",
    "validity": "30 days",
    "countries": ["France"],
    "amount": 15.00,
    "charge": 0.00,
    "currency": "USD",
    "status": "SUCCESS",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

esim.topped_up

Triggered when an eSIM is topped up.
{
  "event": "esim.topped_up",
  "version": "2.0",
  "sign": "...",
  "data": {
    "esimId": "ESM678A3B4C5D6E7",
    "iccid": "8901234567890123456",
    "packageName": "France Top-Up 2GB",
    "dataAmount": "2 GB",
    "validity": "30 days",
    "amount": 8.00,
    "currency": "USD",
    "status": "SUCCESS",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

esim.activated

Triggered when an eSIM is activated (first use in coverage area).
{
  "event": "esim.activated",
  "version": "2.0",
  "sign": "...",
  "data": {
    "esimId": "ESM678A3B4C5D6E7",
    "iccid": "8901234567890123456",
    "activatedAt": "2026-01-12T18:30:00+00:00",
    "expiresAt": "2026-02-11T18:30:00+00:00",
    "status": "ACTIVE",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

esim.data_warning

Triggered when data usage reaches 80%.
{
  "event": "esim.data_warning",
  "version": "2.0",
  "sign": "...",
  "data": {
    "esimId": "ESM678A3B4C5D6E7",
    "iccid": "8901234567890123456",
    "dataUsedMb": 4096,
    "dataTotalMb": 5120,
    "percentUsed": 80,
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

esim.data_exhausted

Triggered when data is fully consumed.
{
  "event": "esim.data_exhausted",
  "version": "2.0",
  "sign": "...",
  "data": {
    "esimId": "ESM678A3B4C5D6E7",
    "iccid": "8901234567890123456",
    "dataUsedMb": 5120,
    "dataTotalMb": 5120,
    "status": "DATA_EXHAUSTED",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

esim.expired

Triggered when an eSIM validity period ends.
{
  "event": "esim.expired",
  "version": "2.0",
  "sign": "...",
  "data": {
    "esimId": "ESM678A3B4C5D6E7",
    "iccid": "8901234567890123456",
    "expiredAt": "2026-02-11T18:30:00+00:00",
    "status": "EXPIRED",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-02-11T18:30:00+00:00"
  }
}

Card Events (Issuing)

card.created

Triggered when a new virtual card is created.
{
  "event": "card.created",
  "version": "2.0",
  "sign": "...",
  "data": {
    "cardId": "CRD678A3B4C5D6E7",
    "cardholderId": "ch_abc123xyz",
    "type": "VIRTUAL",
    "brand": "MASTERCARD",
    "currency": "USD",
    "last4": "4532",
    "status": "ACTIVE",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

card.funded

Triggered when funds are added to a card.
{
  "event": "card.funded",
  "version": "2.0",
  "sign": "...",
  "data": {
    "cardId": "CRD678A3B4C5D6E7",
    "cardholderId": "ch_abc123xyz",
    "reference": "my-order-12345",
    "amount": 100.00,
    "newBalance": 150.00,
    "currency": "USD",
    "status": "SUCCESS",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}
The reference field contains your custom reference if provided during the fund request, or defaults to the cardId.

card.unloaded

Triggered when funds are withdrawn from a card.
{
  "event": "card.unloaded",
  "version": "2.0",
  "sign": "...",
  "data": {
    "cardId": "CRD678A3B4C5D6E7",
    "cardholderId": "ch_abc123xyz",
    "reference": "withdraw-67890",
    "amount": 50.00,
    "newBalance": 100.00,
    "currency": "USD",
    "status": "SUCCESS",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}
The reference field contains your custom reference if provided during the unload request, or defaults to the cardId.

card.frozen

Triggered when a card is frozen.
{
  "event": "card.frozen",
  "version": "2.0",
  "sign": "...",
  "data": {
    "cardId": "CRD678A3B4C5D6E7",
    "reason": "User requested",
    "status": "FROZEN",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

card.unfrozen

Triggered when a frozen card is unfrozen.
{
  "event": "card.unfrozen",
  "version": "2.0",
  "sign": "...",
  "data": {
    "cardId": "CRD678A3B4C5D6E7",
    "status": "ACTIVE",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

card.terminated

Triggered when a card is permanently terminated.
{
  "event": "card.terminated",
  "version": "2.0",
  "sign": "...",
  "data": {
    "cardId": "CRD678A3B4C5D6E7",
    "cardholderId": "ch_abc123xyz",
    "cardName": "John Doe",
    "last4": "4532",
    "reference": "cancel-card-abc123",
    "status": "TERMINATED",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}
The reference field contains your custom reference if provided during the terminate request, or defaults to the cardId.

card.replaced

Triggered when a card is replaced with a new card (same cardId, new card details).
{
  "event": "card.replaced",
  "version": "2.0",
  "sign": "...",
  "data": {
    "cardId": "CRD678A3B4C5D6E7",
    "cardholderId": "ch_abc123xyz",
    "cardName": "John Doe",
    "last4": "7891",
    "brand": "MASTERCARD",
    "status": "ACTIVE",
    "previousLast4": "4532",
    "reason": "Card compromised",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}
Card replacement creates a new physical/virtual card while keeping the same cardId for your records. The previous card is automatically terminated.

card.transaction.approved

Triggered when a card purchase is approved.
{
  "event": "card.transaction.approved",
  "version": "2.0",
  "sign": "...",
  "data": {
    "cardId": "CRD678A3B4C5D6E7",
    "reference": "TXN550e8400e29b41d4",
    "amount": 49.99,
    "currency": "USD",
    "merchant": {
      "name": "AMAZON *MARKETPLACE",
      "category": "SHOPPING",
      "country": "US"
    },
    "cardBalanceBefore": 150.00,
    "cardBalanceAfter": 100.01,
    "status": "APPROVED",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

card.transaction.declined

Triggered when a card purchase is declined.
{
  "event": "card.transaction.declined",
  "version": "2.0",
  "sign": "...",
  "data": {
    "cardId": "CRD678A3B4C5D6E7",
    "reference": "TXN550e8400e29b41d4",
    "amount": 299.99,
    "currency": "USD",
    "merchant": {
      "name": "BESTBUY",
      "category": "ELECTRONICS"
    },
    "reason": "Insufficient funds",
    "status": "DECLINED",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

card.transaction.reversed

Triggered when a card transaction is refunded by merchant.
{
  "event": "card.transaction.reversed",
  "version": "2.0",
  "sign": "...",
  "data": {
    "cardId": "CRD678A3B4C5D6E7",
    "reference": "REV550e8400e29b41d4",
    "originalReference": "TXN550e8400e29b41d4",
    "amount": 49.99,
    "currency": "USD",
    "status": "REVERSED",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-12T18:30:00+00:00"
  }
}

Cardholder Events (Issuing)

cardholder.created

Triggered when a new cardholder is created.
{
  "event": "cardholder.created",
  "version": "2.0",
  "sign": "...",
  "data": {
    "cardholderId": "ch_1a2b3c4d5e6f7890abcdef1234567890",
    "firstName": "John",
    "lastName": "Smith",
    "email": "[email protected]",
    "phone": "+12025551234",
    "status": "ACTIVE",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-15T21:31:39+00:00"
  }
}

cardholder.kyc_submitted

Triggered when KYC documents are submitted for a cardholder.
{
  "event": "cardholder.kyc_submitted",
  "version": "2.0",
  "sign": "...",
  "data": {
    "cardholderId": "ch_1a2b3c4d5e6f7890abcdef1234567890",
    "firstName": "John",
    "lastName": "Smith",
    "documentType": "PASSPORT",
    "kycStatus": "PENDING",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-15T22:00:00+00:00"
  }
}

cardholder.kyc_approved

Triggered when cardholder KYC verification is approved.
{
  "event": "cardholder.kyc_approved",
  "version": "2.0",
  "sign": "...",
  "data": {
    "cardholderId": "ch_1a2b3c4d5e6f7890abcdef1234567890",
    "firstName": "John",
    "lastName": "Smith",
    "kycLevel": "VERIFIED",
    "kycStatus": "VERIFIED",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-16T10:00:00+00:00"
  }
}

cardholder.kyc_rejected

Triggered when cardholder KYC verification is rejected.
{
  "event": "cardholder.kyc_rejected",
  "version": "2.0",
  "sign": "...",
  "data": {
    "cardholderId": "ch_1a2b3c4d5e6f7890abcdef1234567890",
    "firstName": "John",
    "lastName": "Smith",
    "reason": "Document image is unclear",
    "status": "REJECTED",
    "appId": "D0H6R7Z6R1C2N5O5",
    "timestamp": "2026-01-16T10:00:00+00:00"
  }
}

Verifying Signatures

All webhooks include an HMAC-SHA256 signature. Verify using your webhook secret:
const crypto = require('crypto');

function verifySignature(data, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(data))
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

app.post('/webhooks/fyatu', (req, res) => {
  const { sign, data } = req.body;

  if (!verifySignature(data, sign, process.env.WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Respond immediately
  res.status(200).send('OK');

  // Process asynchronously
  processWebhook(req.body);
});

Event Summary

Collection Events

EventDescription
collection.initiatedPayment request created
collection.receivedPayment successfully received
collection.failedPayment failed
collection.expiredPayment request expired

Payout Events

EventDescription
payout.initiatedPayout request created
payout.completedPayout successfully sent
payout.failedPayout failed

Refund Events

EventDescription
refund.initiatedRefund request created
refund.completedRefund successfully processed
refund.failedRefund failed

eSIM Events

EventDescription
esim.purchasedeSIM purchased
esim.topped_upeSIM topped up
esim.activatedeSIM activated
esim.data_warningData usage at 80%
esim.data_exhaustedData fully consumed
esim.expiredeSIM validity ended

Card Events (Issuing)

EventDescription
card.createdNew card created
card.fundedCard funded
card.unloadedCard unloaded
card.frozenCard frozen
card.unfrozenCard unfrozen
card.terminatedCard terminated
card.replacedCard replaced with new card
card.transaction.approvedTransaction approved
card.transaction.declinedTransaction declined
card.transaction.reversedTransaction reversed

Cardholder Events (Issuing)

EventDescription
cardholder.createdNew cardholder created
cardholder.kyc_submittedKYC documents submitted
cardholder.kyc_approvedKYC verification approved
cardholder.kyc_rejectedKYC verification rejected

Best Practices

Respond Quickly

Return 200 OK within 5 seconds to avoid retries

Verify Signatures

Always verify HMAC signatures before processing

Handle Idempotency

Use reference fields to handle duplicate webhooks

Process Async

Queue webhooks for reliable background processing