> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apostra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook endpoints

> Register endpoints and verify signed Apostra webhook deliveries.

V3 webhooks let an integration react when Apostra records an event. They are
not a reporting export: use the delivery read when you need the current facts.
A webhook is a signed prompt to make that read.

## Register a webhook endpoint

Open the **Webhook endpoints** Page from a V3 MCP host to add and manage a
public HTTPS endpoint. The Page is for external buyer agents and customer
systems. Hosted Adclaw uses an internal subscriber on the same event catalogue,
not a public endpoint.

1. Enter the public HTTPS URL you control.
2. Copy the signing secret when the Page displays it. It is shown once and is
   never returned by endpoint lists or later reads.
3. Select **Verify**. Apostra sends a control challenge to the URL. A failed
   challenge keeps the endpoint pending so you can correct the receiver and
   retry it.
4. Rotate a verified endpoint's secret when needed. The previous key remains
   valid for 24 hours, so receivers can switch without dropping deliveries.
5. Disable an endpoint to stop future delivery. Accounts can retain at most 25
   endpoint records, including disabled records. Delete a disabled endpoint to
   recover capacity; deletion also retires its signing secret. You can then
   register that URL again as a new endpoint when you are ready to start over.

Registering and verifying an endpoint does not connect it to any events. The
first subscription family, `campaign.delivery.changed`, will reference a
verified endpoint ID when subscriptions are available.

Do not use the older v2 `X-Webhook-Signature` contract for a V3 delivery. The
two formats are incompatible.

## Verify a delivery

Every V3 delivery is an HTTPS `POST` with these headers.

| Header | Value |
| - | - |
| `Apostra-Signature` | `v1,kid=SECRET_ID,hmac-sha256=HEX_DIGEST` |
| `Apostra-Timestamp` | Unix timestamp in whole seconds |
| `Apostra-Delivery-Id` | An opaque, retry-stable delivery identifier |

For version `v1`, calculate the signature over the exact bytes below. Do not
parse or re-serialize the JSON before calculating it.

```text theme={null}
UTF-8("v1." + Apostra-Timestamp + "." + Apostra-Delivery-Id + ".") + raw request body bytes
```

Use the endpoint secret as the UTF-8 HMAC key and HMAC-SHA256 as the algorithm.
The lowercase hexadecimal result is `HEX_DIGEST` in `Apostra-Signature`.

Treat a delivery as invalid when a required header occurs more than once, the
signature is not exactly the shape above, the key id is unknown, the HMAC does
not match in constant time, or the timestamp differs from your clock by more
than five minutes. Check the signature before parsing the body.

Keep the raw occurrences of each header until you verify the request. A server
adapter that flattens duplicate headers to one string cannot enforce this
requirement. The reference helper therefore accepts an array of raw values for
each required header, even when there is only one value.

## Handle retries and replay

`Apostra-Delivery-Id` identifies one logical delivery. Its value and body stay
the same on every retry. The timestamp and signature can change because each
attempt is signed when Apostra sends it.

After signature verification, store the pair of your endpoint ID and delivery
ID in the same durable transaction that applies the event. Keep it for at least
30 days. If you receive the same pair again, return a `2xx` response without
processing the event twice. A duplicate is normally a retry after Apostra did
not receive your earlier success response, not an error.

Do not use the delivery ID to order events. When order matters, follow the
event's documented cursor or read the current resource.

## Rotate a secret

The Webhook endpoints Page will show a newly created secret once. Store it
before you leave the page. When you rotate it, Apostra begins signing new
deliveries with the new key ID and keeps the previous key valid for exactly 24
hours.
Your verifier must select the secret by `kid` and accept both keys for exactly
24 hours. Remove the old key after its expiry.

Never log a secret or return it from a webhook handler.

## Retry and success policy

A `2xx` response is success. Apostra does not follow redirects. It retries
network failures, timeouts, `408`, `429`, and `5xx` responses up to eight times
in 24 hours: the initial attempt, then approximately 1 minute, 5 minutes,
30 minutes, 2 hours, 6 hours, 12 hours, and 24 hours later. Each delay has
jitter. For `429`, Apostra honours a valid `Retry-After` value when that still
fits inside the 24-hour window.

Other `4xx` responses are final. A `410 Gone` disables the endpoint immediately.
After the final failed attempt, Apostra marks the delivery as failed and exposes
it in endpoint health rather than silently dropping it.

## Test vectors and helpers

The `@scope3/agentic-contracts/webhooks` reference TypeScript verifier and the
`@scope3/agentic-contracts/webhooks/vectors` language-neutral fixture ship
with the contracts package. The vectors cover a valid current key, a valid previous key during rotation,
body tampering, unknown keys, each duplicate required header, a stale timestamp,
missing or malformed required headers, an expired rotated key, and a retry with
the same delivery ID. SDKs should run these exact vectors before shipping a
verifier.
See the [V3 quickstart](/v3/quickstart) for the API-key connection flow that
precedes endpoint registration.

## First event

The first standing subscription event will be `campaign.delivery.changed`. It
is an invalidation, not a complete delivery report. On receipt, read the
campaign's delivery through the V3 delivery operation before deciding what to
do. Endpoint registration is available in the Webhook endpoints Page.
Subscription setup will be documented here when it is available.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.