Webhooks
AGLedger pushes every business-meaningful event in the record lifecycle to the systems you already run, signed so the receiver can check who produced the delivery — not just that the bytes arrived intact. Two signing schemes cover two different trust problems: a shared secret when only your own system consumes the event, and an asymmetric signature when a Settlement Signal has to be trusted by a counterparty's payment system. AGLedger does not poll, queue on your side, or ask you to adopt a broker — it is an HTTPS POST your endpoint already knows how to receive.
HMAC-SHA256 · Ed25519 RFC 9421 · CloudEvents 1.0 · At-least-once · Circuit breaker · DLQ replay
Last updated: 2026-05-25 · API v0.25.4
Signing
Two ways to prove a delivery is real
Every delivery is signed. The scheme is fixed per subscription and reported on the webhook's read surface as signingAlg, so a consumer checks it once and writes the matching verifier. The two schemes solve different problems — pick by who needs to verify the delivery.
hmac — default
HMAC-SHA256 over a per-subscription shared secret, in the same header shape Stripe, GitHub, and Shopify use: X-AGLedger-Signature: t=<unix>,v1=<hex>. It confirms to one specific receiver that the POST came from the Server it subscribed against, untampered. It verifies in two lines and needs no third party. This is right for ordinary notarization fan-out, where only your own system consumes the event.
ed25519 — for Settlement Signals
RFC 9421 HTTP Message Signatures, signed with the Server's vault key — the same key that signs the chain. The receiver verifies against the Server's published public keys and holds no secret of its own. A subscription that lists a settlement event defaults to ed25519 when the Server has a signing key. This is the scheme that lets a settlement instruction cross a company boundary.
Non-repudiation
Signed the same way the chain is signed
A shared secret is symmetric: the receiver holds the same key that signs, so it can confirm a delivery for itself but cannot prove to anyone else who produced it. For a Settlement Signal routed into a payment platform, ERP, or ticketing system, that is the difference that matters.
A Settlement Signal is signed by the same vault key that signs the chain. The payment system on the other side verifies a SETTLE against a public key it fetched once — no shared secret to exchange, and the signature is provable to a third party. That is what lets a settlement instruction cross a company boundary without a trusted intermediary in the middle.
The receiver resolves the key by the keyid in the signature header against GET /v1/verification-keys (also published at /.well-known/agledger-vault-keys.json), recomputes the signature base, and checks the Ed25519 signature. The SETTLE and the chain record it settles share one trust root.
Derived components (@method, @target-uri, @authority) are deliberately excluded so a load balancer or reverse proxy that rewrites host and path cannot break verification of an authentic message. Step-by-step verification code for both schemes is in the webhook guide.
Events
Business moments, not internal state transitions
AGLedger emits events at every business-meaningful moment in the record lifecycle. Register an endpoint through POST /v1/webhooks with an event-type filter or the wildcard ["*"]. Federation events mirror the lifecycle stream with a federation. prefix and a thin cross-org payload, so a single subscription can see both sides without two divergent schemas on one event type. Payloads ship in AGLedger's native envelope or CloudEvents 1.0.
| Category | Example events |
|---|---|
| Record lifecycle | record.created, record.recorded, record.completion_submitted, record.fulfilled, record.failed, record.expired, record.cancelled |
| Agent-to-agent | record.proposed, record.proposal_accepted, record.proposal_rejected, record.delegated, record.revision_requested |
| Cascading verification | cascading.verification.complete |
| Settlement and disputes | signal.emitted, signal.received, dispute.opened, dispute.resolved, dispute.withdrawn |
| Federation | federation.record.state_changed, federation.settlement.signal, record.federation_fulfilled (and other federation-projected lifecycle events) |
| Entity references | record.reference_added, agent.reference_added |
Every payload carries a type and a status in display names (CREATED / FULFILLED / FAILED / RECORDED …), not internal state-machine names. The full event-type list is in the API reference.
Delivery
Built to survive a receiver that is having a bad day
At-least-once, deduplicated by you. Every event carries a stable X-AGLedger-Idempotency-Key — minted once and replayed verbatim on every retry of the same event. Treat a repeat as a no-op. A separate per-attempt X-AGLedger-Delivery id is for log correlation, not dedup.
Order by status, not by arrival. Delivery is serialized per subscription, but a retried event can overtake a later one that succeeded first. Receivers integrating a state machine branch on the status field in the payload rather than on event arrival order.
Retry with exponential backoff. A failed delivery retries roughly six times over about five minutes, then moves to the subscription's dead-letter queue. The window is intentionally short — operators wanting longer windows replay from the DLQ explicitly rather than tying up a worker for hours.
Circuit breaker. After ten consecutive failures against the same subscription the breaker opens and subsequent events go straight to the DLQ without an attempt. It closes again on the first successful delivery, or an operator resets it once the receiver is fixed.
Dead-letter replay. Permanently failed deliveries land in the DLQ for seven days, carrying the original event body so you can audit what failed. Replay one entry or drain the whole queue once the receiver is back.
Secret rotation with a grace window. Rotate an HMAC secret without downtime — the previous secret stays valid for a configured window so consumers update on their own schedule.
Management
Subscriptions are an API, not a config file you redeploy
Register, inspect, rotate, pause, resume, and tear down endpoints programmatically. Send a test event before you wire anything up, read the delivery log to see exactly what each attempt returned, and work the dead-letter queue when a receiver comes back. For config-as-code installs, the same subscriptions can be declared in provisioning YAML with operator-supplied secrets.
Org-wide health and DLQ views (GET /v1/admin/webhooks/health) give SRE dashboards one row per subscription with breaker state and last-success timestamp.
Related capabilities
What the Ed25519-signed delivery carries on terminal verdicts — SETTLE, HOLD, RELEASE.
How a peer Server's event projects into your local stream as signal.received.
Trace-ID join keys and OCSF SIEM export — the other ways AGLedger fits under your stack.
SSRF protection on delivery URLs, secret storage, and the verification-key surface.
Register an endpoint and verify both signature schemes, with real code.
The full webhook route surface and per-event payload schemas.