Skip to content

Webhook Protocol v1.0

AON sends the supported conversion event to the HTTPS endpoint configured for a Developer Application. Verify every delivery before parsing JSON or applying a business side effect.

Canonical source and runtime evidence are different

The protected Protocol v1.0 postback specification and schema fixtures define the canonical contract. Publication does not prove that a particular runtime, OpenAPI document, SDK, or Portal follower has deployed and validated it. Treat each follower as unvalidated until its owner publishes evidence; production support requires separate runtime and release evidence.

Required headers

Every Protocol v1.0 delivery uses POST, Content-Type: application/json, and exactly one value for each security/control header:

HeaderRequired value or responsibility
AON-Protocol-VersionExact value 1.0
X-AON-KeyOpaque callback key id selecting one current or grace secret
X-AON-TimestampCanonical unsigned Unix epoch seconds for this attempt
X-AON-SignatureLowercase-hex HMAC-SHA256 over the exact wire inputs

Reject a missing, empty, or duplicate header before HMAC verification or JSON parsing. Never choose the first or last value from duplicate security headers.

Verify the raw body

For every attempt, AON signs this byte string:

POST\n{request-target}\n{exact-raw-body}\n{timestamp}

request-target is the exact origin-form path and raw query. Preserve repeated slashes, percent encoding, query order, and spelling. exact-raw-body is the byte sequence received on the wire. Verify the HMAC over those bytes before parsing or re-serializing JSON, compare the lowercase hexadecimal signature in constant time, then validate the closed conversion payload.

X-AON-Timestamp is the attempt signing time. The payload timestamp is the conversion event time, normalized to UTC; the two fields are intentionally different.

Idempotency

Use (agent_id, event_id) as the durable idempotency key and retain the SHA-256 digest of the exact raw body for at least 24 hours.

  • First valid key and body: apply the business effect once and store the digest in the same transaction.
  • Same key and same digest: return 2xx without another business effect.
  • Same key and different digest: return HTTP 409 and do not process it.

An in-memory cache or SDK signature verifier does not provide this durable transaction.

Five delivery attempts

Any 2xx response completes delivery. AON does not follow redirects and ignores the response body. A timeout, 3xx, or other non-2xx response follows one fixed five-attempt ladder:

AttemptDelay after the previous attempt
1Immediate
21 minute
35 minutes
430 minutes
52 hours

There is no sixth attempt. Across retries, event_id and the exact raw body stay frozen. Each attempt gets a fresh timestamp and matching signature.

Frozen and current facts

Configuration changes affect different parts of the delivery lifecycle:

  • Event-time opt-in is frozen. A conversion records whether it was eligible when the event was accepted. Disabling the current subscription does not cancel an already eligible conversion.
  • Target URL is frozen at outbox creation. Changing the endpoint affects future outbox entries, not the target already stored on a queued entry.
  • Wire version and raw body are frozen at outbox creation. A v1.0 row stays v1.0 across every attempt and is never downgraded as a fallback.
  • Signing secret and key version are current per attempt. The worker reads the application's current callback secret and key version when it attempts delivery. A rotation can therefore change X-AON-Key and the signature for an already queued entry while its target and body remain unchanged.

Keep the current and previous key generations available during your receiver's rotation window. Unknown or expired key ids must fail closed; do not try every secret as a fallback.

Configuration is not delivery health

A saved endpoint, enabled subscription, configured secret, or successful Portal response is a configuration fact. It is not proof that a worker is running or that a delivery reached your receiver. Runtime support is closed by deployment observation and a signed callback canary, not by this static page.