Webhook Signature Verification
Every webhook delivery from FYATU includes anX-Fyatu-Signature header. Verify this signature before processing any event — it proves the request originated from FYATU and the body has not been tampered with.
Delivery Headers
Every webhook delivery includes these headers:| Header | Example | Description |
|---|---|---|
X-Fyatu-Signature | t=1716372000,v1=abc123... | Unix timestamp + HMAC-SHA256 signature |
X-Fyatu-Timestamp | 1716372000 | Unix timestamp (same as t= in signature) |
X-Fyatu-Event | CARD_ISSUED | Event type in UPPERCASE_SNAKE format |
X-Fyatu-Event-ID | evt_01HXY123456ABCDEF | Unique event ID — use for deduplication |
X-Fyatu-Environment | LIVE | LIVE or SANDBOX |
User-Agent | Fyatu-Webhook/3.20 | Sender identification |
Event names use
UPPERCASE_SNAKE format: CARD_ISSUED, TRANSACTION_AUTHORIZED, CARDHOLDER_KYC_APPROVED. Earlier docs may reference dot-notation names such as card.issued — those are deprecated. Always use the header value as the canonical event name.Webhook Payload Envelope
All events share the same outer envelope:| Field | Description |
|---|---|
event | Event type — matches X-Fyatu-Event header |
eventId | Unique event ID — matches X-Fyatu-Event-ID header |
businessId | Your business identifier |
environment | LIVE or SANDBOX |
timestamp | ISO 8601 event timestamp |
data | Event-specific payload — varies by event type |
The Signing Algorithm
FYATU signs every webhook using a two-step process. Step 1 — Derive the signing key Your raw webhook secret (whsec_...) is never used directly as the HMAC key. Instead, FYATU computes:
{timestamp}.{fullRequestBody}:
timestamp is the Unix timestamp from the t= field in X-Fyatu-Signature, and body is the raw request body bytes before any JSON parsing.
Step 3 — Compare
Extract v1 from the header and compare with your computed signature using a constant-time comparison function to prevent timing attacks.
Verification Examples
Replay Attack Prevention
Thet= timestamp in the signature header lets you reject stale requests. Reject any delivery where the timestamp is more than 5 minutes old:
Idempotency — Deduplicating Retries
FYATU retries failed deliveries up to 5 times. The same event may arrive more than once. UseX-Fyatu-Event-ID (also available as eventId in the body) to deduplicate:
Retry Schedule
FYATU retries failed deliveries (any response other than2xx) on this schedule:
| Attempt | Delay after previous failure |
|---|---|
| 1st retry | ~5 minutes |
| 2nd retry | ~30 minutes |
| 3rd retry | ~2 hours |
| 4th retry | ~8 hours |
| 5th retry | ~24 hours |
FAILED permanently. You can view and manually replay failed deliveries from the portal under Developer → Webhooks.
Best Practices
Use raw body
Always capture the raw request body bytes before JSON parsing. Re-serializing parsed JSON can change whitespace or key order, breaking the signature.
Respond quickly
Return
200 within 30 seconds. Queue events for async processing if your handler does database writes or external API calls.Reject stale timestamps
Enforce a 5-minute window on
t= to prevent replay attacks where captured requests are redelivered later.Deduplicate with Event-ID
Store processed
X-Fyatu-Event-ID values. A retry arriving after your handler already ran should return 200 immediately without re-processing.Rotate secrets safely
If your webhook secret is compromised, delete the webhook endpoint and create a new one. The old secret stops working immediately.
Test with SANDBOX first
Use the portal’s Send Test Event feature to fire a sample payload to your endpoint before going live.

