Skip to main content

Webhooks

Webhooks allow you to receive real-time HTTP notifications when events occur on your cards. Instead of polling the API, webhooks push data to your server instantly.

How Webhooks Work

  1. An event occurs (e.g., card transaction)
  2. FYATU sends an HTTP POST to your webhook URL
  3. Your server processes the event and returns HTTP 200
  4. If your server fails, FYATU retries (up to 3 times)

Setting Up Webhooks

1. Create a Webhook Endpoint

Your endpoint must:
  • Accept POST requests
  • Return HTTP 200 on success
  • Be publicly accessible (HTTPS recommended)
Express.js Example
app.post('/webhooks/fyatu', (req, res) => {
  const event = req.body;

  console.log('Received webhook:', event.event);
  console.log('Data:', event.data);

  // Process the event
  handleWebhookEvent(event);

  // Always return 200 to acknowledge receipt
  res.status(200).send('OK');
});

2. Register Your Webhook URL

curl -X POST "https://api.fyatu.com/api/v2/webhooks" \
  -H "Business-ID: BUS_your_business_id" \
  -H "Authorization: Bearer sk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"webhookUrl": "https://api.yourcompany.com/webhooks/fyatu"}'
Your webhook URL must return HTTP 200 during registration verification.

3. Test Your Webhook

Use the simulate endpoint to test your integration:
curl -X POST "https://api.fyatu.com/api/v2/webhooks/simulate" \
  -H "Business-ID: BUS_your_business_id" \
  -H "Authorization: Bearer sk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"type": "transaction"}'

Webhook Payload Structure

All webhooks follow this structure:
{
  "event": "card.transaction.approved",
  "version": "2.0",
  "timestamp": "2024-12-24T10:30:00Z",
  "signature": "a1b2c3d4e5f6...",
  "data": {
    // Event-specific data
  }
}
FieldDescription
eventEvent type (e.g., card.transaction.approved)
versionWebhook version (2.0)
timestampISO 8601 timestamp
signatureHMAC signature for verification
dataEvent-specific payload

Retry Policy

If your endpoint fails to respond with HTTP 200:
AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry15 minutes
After 3 failed attempts, the webhook is marked as failed. You can view failed webhooks in your dashboard.

Security

Verify Webhook Signatures

Each webhook includes a signature in the headers. Verify it to ensure the request came from FYATU:
const crypto = require('crypto');

function verifyWebhook(payload, signature, encryptionKey) {
  const expectedSignature = crypto
    .createHmac('sha256', encryptionKey)
    .update(JSON.stringify(payload))
    .digest('hex');

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

// In your webhook handler
app.post('/webhooks/fyatu', (req, res) => {
  const signature = req.headers['x-fyatu-signature'];
  const encryptionKey = process.env.FYATU_ENCRYPTION_KEY;

  if (!verifyWebhook(req.body, signature, encryptionKey)) {
    return res.status(401).send('Invalid signature');
  }

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

Use HTTPS

Always use HTTPS for your webhook endpoint to encrypt data in transit.

Best Practices

Return HTTP 200 immediately, then process the event asynchronously. Don’t let processing delay your response.
app.post('/webhooks/fyatu', (req, res) => {
  // Respond immediately
  res.status(200).send('OK');

  // Process asynchronously
  processWebhookAsync(req.body);
});
Webhooks may be delivered multiple times. Use the event ID to deduplicate:
if (await hasProcessed(event.id)) {
  return; // Already processed
}
await markAsProcessed(event.id);
// Process event
Store webhook payloads for debugging and auditing purposes.
New event types may be added. Don’t fail on unrecognized events:
switch (event.event) {
  case 'card.transaction.approved':
  case 'card.transaction.declined':
  case 'card.transaction.reversed':
    handleTransaction(event);
    break;
  case 'card.funding':
  case 'card.unloaded':
  case 'card.terminated':
  case 'card.maintenance':
    handleCardEvent(event);
    break;
  default:
    console.log('Unknown event:', event.event);
}