Handle Deliveries
This document describes how webhook events are delivered, acknowledged, retried, and how consumers should process and order them.
Overview
Webhook events are delivered using an at-least-once delivery model.
This means:
Each event is guaranteed to be delivered one or more times. Duplicate deliveries are possible and expected.
Webhook consumers must implement idempotent processing.
Delivery Model
- Delivery guarantee: At least once
- Duplicates: Possible
- Ordering: Not guaranteed at delivery time
Delivery Method
- Protocol: HTTPS only
- HTTP Method:
POST - Content-Type:
application/json - Encoding: UTF-8
Delivery Headers
Each delivery includes the following headers:
| Header | Description |
|---|---|
X-Agc-Version | Webhook payload version |
X-Agc-Tenant-Id | ID of the tenant that triggered the event |
X-Agc-Webhook-Id | ID of the configured webhook |
X-Agc-Attempt-Id | Unique identifier for this delivery attempt |
X-Agc-Event-Id | Unique event identifier (stable across retries, use for idempotency) |
X-Agc-Event-Type | Name of the event type (e.g. pid.completed) |
X-Agc-Timestamp | ISO-8601 timestamp indicating when this delivery attempt was sent |
X-Agc-Signature | HMAC signature used to verify authenticity (only present if a secret is configured |
Delivery Payload
The webhook request body uses the following structure:
{
"version": "1.0",
"tenant_id": "tenant_abc",
"event_id": "evt_123456789",
"event_type": "pid.completed",
"event_timestamp": "2026-01-22T06:40:00.000Z",
"data": {}
}
Payload Field Semantics
| Field | Description |
|---|---|
version | Webhook payload version |
tenant_id | ID of the tenant that triggered the event |
event_id | Unique event identifier (stable across retries) |
event_type | Event type name |
event_timestamp | When the event occurred in the platform |
data | Event-specific payload |
Success & Failure Criteria
Successful Delivery
Any HTTP response with a status code in the range:
200–399
is considered a successful delivery, and no further retries will be made.
Retryable Failures
The following status codes will trigger retries:
408– Request Timeout429– Too Many Requests500–599– Server errors
Retries are also triggered for:
- Network errors
- TLS failures
- DNS failures
- Connection timeouts
Non-Retryable Failures
The following status codes will NOT be retried:
400– Bad Request401– Unauthorized403– Forbidden404– Not Found410– Gone
When these responses are received, the delivery is considered final.
Retry Behavior
Retry Strategy
- Retries use exponential backoff
- Retries continue for up to 24 hours
- Retry intervals are not guaranteed and may vary
Retry Characteristics
event_idremains constant across all retriesattempt_idis unique for every delivery attempt- The same payload is resent for each retry
Idempotency (Required)
Due to at-least-once delivery semantics, webhook consumers must implement idempotent processing.
Idempotency Key
Use:
event_id
Recommended Processing Pattern
if event_id already processed:
return success
else:
process event
store event_id
Failing to implement idempotency may result in duplicate side effects.
Timeout Expectations
Webhook endpoints should:
- Respond within a few seconds
- Avoid long-running synchronous processing
- Offload heavy work to background jobs or queues
Slow responses increase retry likelihood.
Security Considerations
- Use HTTPS
- Validate HMAC signatures when present
- Validate timestamps (
X-Agc-Timestamp) to prevent replay attacks - Reject malformed payloads
See Validating Deliveries for details.
Logging & Observability
Strongly recommended logging fields:
event_idevent_typeattempt_idevent_timestampX-Agc-Timestamp- HTTP response codes
- Signature verification failures