F
FromChain
API Reference

Webhooks

Receive real-time notifications when invoice statuses change. All webhooks are signed with HMAC-SHA256 for security.

Available Events

invoice.created

New invoice created

invoice.detected

Payment detected (0 confirmations)

invoice.partial

Partial payment received

invoice.overpaid

Overpayment received

invoice.confirmed

Payment confirmed (5 blocks)

invoice.processing_payout

Payout in progress

invoice.paid_out

Funds successfully distributed

invoice.expired

Invoice expired without payment

invoice.failed

Payout failed

invoice.late_payment

Payment after expiration

Create Webhook Endpoint

POST/v1/webhooks/endpoints

Register a new webhook endpoint for your tenant

create-webhook.js
const response = await fetch('https://api.fromchain.io/v1/webhooks/endpoints', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer gw_live_...',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    url: 'https://yourdomain.com/api/webhooks',
    enabled: true,
    eventsSubscribed: [
      'invoice.confirmed',
      'invoice.paid_out',
      'invoice.failed'
    ]
  })
});

const endpoint = await response.json();
// {
//   "id": "wh_endpoint_abc123",
//   "url": "https://yourdomain.com/api/webhooks",
//   "secret": "whsec_xyz789...",  // Store this securely!
//   "enabled": true,
//   "eventsSubscribed": ["invoice.confirmed", "invoice.paid_out", "invoice.failed"],
//   "createdAt": "2025-12-18T10:00:00Z"
// }

Verifying Webhook Signatures

Every webhook includes HMAC-SHA256 signature headers for verification:

POST /api/webhooks HTTP/1.1
Host: yourdomain.com
Content-Type: application/json
X-Webhook-Id: evt_abc123
X-Webhook-Timestamp: 1702905600000
X-Webhook-Signature: v1=a1b2c3d4e5f6...
X-Tenant-Id: tenant_xyz789

{
  "id": "evt_abc123",
  "type": "invoice.confirmed",
  "createdAt": "2025-12-18T11:00:00.000Z",
  "data": {
    "invoiceId": "inv_123",
    "status": "CONFIRMED",
    ...
  }
}

Verify the signature to ensure the webhook came from FromChain:

verify-webhook.js
import crypto from 'crypto';

function verifyWebhook(
  payload: string,        // Raw request body
  timestamp: string,      // X-Webhook-Timestamp header
  signature: string,      // X-Webhook-Signature header
  secret: string          // Your endpoint secret
): boolean {
  // Construct the signed payload
  const signedPayload = `${timestamp}.${payload}`;
  
  // Compute HMAC
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');
  
  // Compare signatures (constant-time comparison)
  const receivedSignature = signature.replace('v1=', '');
  
  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature),
    Buffer.from(receivedSignature)
  );
}

// Express.js example
app.post('/api/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-webhook-signature'] as string;
  const timestamp = req.headers['x-webhook-timestamp'] as string;
  const payload = req.body.toString();
  
  // Verify timestamp to prevent replay attacks
  const now = Date.now();
  const webhookTime = parseInt(timestamp);
  if (Math.abs(now - webhookTime) > 5 * 60 * 1000) { // 5 minutes
    return res.status(400).send('Timestamp too old');
  }
  
  // Verify signature
  if (!verifyWebhook(payload, timestamp, signature, process.env.WEBHOOK_SECRET!)) {
    return res.status(401).send('Invalid signature');
  }
  
  // Process the webhook
  const event = JSON.parse(payload);
  console.log('Valid webhook received:', event.type);
  
  res.status(200).send('OK');
});

Retry Behavior

If your endpoint fails (non-2xx response), FromChain will retry with exponential backoff:

AttemptDelayTotal Time
1Immediate0s
230 seconds30s
32 minutes2m 30s
45 minutes7m 30s
515 minutes22m 30s
61 hour1h 22m
73 hours4h 22m
86 hours10h 22m

⚠️ Important: After 8 failed attempts, the delivery moves to a dead-letter queue. You can manually replay failed deliveries via the API.

Webhook Management

GET/v1/webhooks/endpoints

List all webhook endpoints

PATCH/v1/webhooks/endpoints/:id

Update endpoint URL or subscribed events

POST/v1/webhooks/endpoints/:id/rotate-secret

Rotate the webhook secret

POST/v1/webhooks/endpoints/:id/test

Send a test webhook event

POST/v1/webhooks/replay/:eventId

Manually replay a specific webhook event

GET/v1/webhooks/deliveries

List webhook delivery attempts

Best Practices

✅ Always verify signatures

Never process webhooks without verifying the HMAC signature to prevent spoofing.

✅ Respond quickly

Return a 200 response immediately. Process the webhook asynchronously using a queue.

✅ Handle idempotency

Use the X-Webhook-Id header to deduplicate webhook deliveries.

✅ Use HTTPS endpoints

Webhook URLs must use HTTPS in production for security.