Skip to main content

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:

HeaderDescription
X-Agc-VersionWebhook payload version
X-Agc-Tenant-IdID of the tenant that triggered the event
X-Agc-Webhook-IdID of the configured webhook
X-Agc-Attempt-IdUnique identifier for this delivery attempt
X-Agc-Event-IdUnique event identifier (stable across retries, use for idempotency)
X-Agc-Event-TypeName of the event type (e.g. pid.completed)
X-Agc-TimestampISO-8601 timestamp indicating when this delivery attempt was sent
X-Agc-SignatureHMAC 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

FieldDescription
versionWebhook payload version
tenant_idID of the tenant that triggered the event
event_idUnique event identifier (stable across retries)
event_typeEvent type name
event_timestampWhen the event occurred in the platform
dataEvent-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 Timeout
  • 429 – Too Many Requests
  • 500–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 Request
  • 401 – Unauthorized
  • 403 – Forbidden
  • 404 – Not Found
  • 410 – 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_id remains constant across all retries
  • attempt_id is 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
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_id
  • event_type
  • attempt_id
  • event_timestamp
  • X-Agc-Timestamp
  • HTTP response codes
  • Signature verification failures