Event Delivery
Event Delivery
When a domain event matches one of your subscriptions, Avvyr sends an HTTP POST to your destination URL. This page covers the payload format, authentication, headers, retry behavior, and how to verify signatures.
Request format
POST https://your-system.com/webhooks/avvyr
Content-Type: application/json
X-Webhook-Event: orderCreated
X-Webhook-Delivery: 550e8400-e29b-41d4-a716-446655440000
{
"_meta": {
"eventType": "orderCreated",
"entityType": "order",
"tenantId": "676140c4...",
"messageId": "abc123...",
"timestamp": "2025-06-15T14:30:00.000Z"
},
"data": {
// Full entity payload
}
}
Headers
Every webhook request includes these headers:
| Header | Description |
|---|---|
Content-Type | Always application/json |
X-Webhook-Event | The event type (e.g. orderCreated) |
X-Webhook-Delivery | Unique ID for this delivery attempt — use for deduplication |
Plus any additionalHeaders you configured on the subscription.
Authentication
You can configure how Avvyr authenticates with your endpoint. Set authType when creating the subscription.
None (default)
No authentication header is sent. Use this only for endpoints behind a firewall or with their own access controls.
Basic
Authorization: Basic base64(username:password)
Set authUsername and authPassword on the subscription.
Bearer
Authorization: Bearer {token}
Set authToken on the subscription. Useful when your endpoint expects a static API key or JWT.
HMAC
X-Webhook-Signature: sha256={hex_digest}
Set hmacSecret on the subscription. Avvyr computes an HMAC-SHA256 of the request body using your secret and sends the result in the X-Webhook-Signature header.
Verifying the signature on your side:
const crypto = require("crypto");
function verifySignature(body, signature, secret) {
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(body, "utf8").digest("hex");
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
// In your webhook handler:
const isValid = verifySignature(rawBody, req.headers["x-webhook-signature"], "your-hmac-secret");
Expected response
Avvyr only checks the HTTP status code to determine if the event was received. Return any 2xx status (e.g. 200 OK or 201 Created) — no response body is required.
HTTP 200 OK
That's it. We don't inspect the response body, headers, or anything else. A 2xx status means "received" and the event is marked as delivered.
200, then process the data in a background job. If your endpoint takes too long to respond, the delivery is treated as failed and will be retried — which can lead to duplicate processing.Retry behavior
If your endpoint returns an error, Avvyr can retry the delivery. Enable retries by setting retryEnabled: true and retryCount on the subscription.
What gets retried
| Response | Retried? |
|---|---|
| 2xx | Success — no retry |
| 4xx (except 408, 429) | Client error — not retried |
| 408 Request Timeout | Retried |
| 429 Too Many Requests | Retried |
| 5xx | Retried |
| Network error / timeout | Retried |
Backoff
Retries use exponential backoff: min(1000ms × 2^attempt, 30 seconds).
| Attempt | Delay |
|---|---|
| 1st retry | ~1 second |
| 2nd retry | ~2 seconds |
| 3rd retry | ~4 seconds |
| 4th retry | ~8 seconds |
| 5th+ retry | 30 seconds (cap) |
Timeout
Each delivery attempt has a 30-second timeout. If your endpoint doesn't respond within 30 seconds, the attempt is treated as a failure and will be retried (if retries are enabled). This is why it's critical to return 200 immediately and handle processing asynchronously — a slow handler that does real work before responding will hit the timeout and trigger unnecessary retries.
Error notifications
Set errorEmail on your subscription to receive an email notification when a delivery fails after all retries are exhausted. This gives your team visibility without having to monitor webhook logs manually.
Best practices
- Return
200immediately — acknowledge receipt, then process asynchronously in a background job or queue. Never do heavy work inside the event handler. - Fetch fresh data — treat the event as a notification, then call the Management API to get the latest entity state before processing. See Always fetch fresh data.
- Use HMAC — verify that events came from Avvyr, not a spoofed source
- Deduplicate — use
X-Webhook-Deliveryto handle potential duplicate deliveries - Enable retries — transient failures happen;
retryEnabled: truewithretryCount: 3is a good default - Test first — use the test endpoint to verify your setup before enabling the subscription