Webhooks
Toffee uses webhooks to notify your backend when important events occur, such as offer engagements and completions. All webhooks are signed for security.
Webhook Structure
All Toffee webhooks follow a consistent structure with four key fields:
{
"id": "wh_01j9x5k2m8n4p6q7r8s9t0v1w2",
"event": "event.type",
"timestamp": "2023-06-01T12:05:00Z",
"data": {
// Event-specific data
}
}
Fields:
id: Unique webhook identifier for idempotency handlingevent: Event type (e.g.,offer.shown,offer.redeemed)timestamp: ISO 8601 timestamp when the event occurreddata: Event-specific payload containing relevant entity data
Webhook Events
Toffee sends webhooks for various events throughout the offer lifecycle:
- Offer Events: See the Offers page for offer-related events
Sample Webhook Payloads
offer.shown
{
"id": "wh_01j9x5k2m8n4p6q7r8s9t0v1w2",
"event": "offer.shown",
"timestamp": "2023-06-01T14:22:00Z",
"data": {
"offer_id": "offer_abc123"
}
}
offer.engaged
{
"id": "wh_01j9x5k2m8n4p6q7r8s9t0v1w2",
"event": "offer.engaged",
"timestamp": "2023-06-01T14:23:15Z",
"data": {
"offer_id": "offer_abc123"
}
}
Webhook Signature Verification
All webhooks include a signature in the X-Toffee-Signature header that you must verify to ensure the webhook is authentic.
Header Format:
X-Toffee-Signature: 6aeedfabc12345...
Verification Steps
- Compute the expected signature: Use HMAC SHA256 with your webhook secret and the raw request body:
expected_signature = HMAC_SHA256(your_webhook_secret, raw_body) - Compare signatures: Use a constant-time comparison to compare the expected signature with the header value
Example Verification (TypeScript)
import { createHmac, timingSafeEqual } from 'crypto';
function verifyToffeeWebhook(
rawPayload: string,
signatureHeader: string,
webhookSecret: string
): boolean {
// Compute expected signature
const expectedSignature = createHmac('sha256', webhookSecret)
.update(rawPayload, 'utf8')
.digest('hex');
// Compare signatures (constant-time)
const expectedBuffer = Buffer.from(expectedSignature, 'hex');
const signatureBuffer = Buffer.from(signatureHeader, 'hex');
return expectedBuffer.length === signatureBuffer.length &&
timingSafeEqual(expectedBuffer, signatureBuffer);
}
Webhook Configuration
You can configure and manage your webhook settings:
- Webhook URL: The endpoint where Toffee will send webhooks
- Webhook Secret: Used to sign webhook payloads (can be rotated)
- Event Selection (soon): Choose which events to receive (offers, or other future event types)
Best Practices
- Always verify signatures: Never process webhooks without signature verification
- Handle duplicate events: While rare, webhooks may be sent multiple times, implement idempotency using webhook IDs
- Respond quickly: Return a 200 status code within 10 seconds to acknowledge receipt
- Retry logic: Toffee will retry failed webhooks, but implement your own retry logic for critical operations
- Log webhook events: Keep audit logs of all webhook events for debugging and compliance
- Use HTTPS: Always use HTTPS endpoints for webhook URLs
Troubleshooting
Common Issues
- Signature verification fails: Check that you're using the correct webhook secret and following the verification steps exactly
- Timeout errors: Ensure your webhook endpoint responds within 10 seconds
- Missing webhooks: Check your webhook URL configuration and ensure your endpoint is accessible
Testing Webhooks
Use tools like ngrok for local development to expose your local webhook endpoint to Toffee:
ngrok http 3000