UCP Webhooks: How Stores Tell Agents an Order Changed — and How to Verify It
Checkout is not the end of an order. It's the beginning.
After an agent buys something, the interesting part starts: the order gets accepted, charged, partially fulfilled, shipped, maybe refunded. Every one of those changes happens on the merchant's side — long after the agent has stopped watching. So how does the agent ever find out? It can poll, hammering the order endpoint every few minutes hoping something changed. Or the store can just tell it the moment it happens. That second option is a UCP webhook, and as of UCP 2026-04-08 it has a spec-defined, cryptographically verified shape.
This is the natural sequel to the agent profile. That post was about the platform proving its identity to a store before it places an order. Webhooks are the same handshake running in reverse: now the store proves its identity to the platform when it reports what happened to that order. Same UCP-Agent header, same signing_keys, same RFC 9421 signatures — pointed the other direction. If you understood the agent profile, you already understand 80% of webhooks.
What a UCP webhook actually is
The spec calls the operation an Order Event Webhook. The direction is business → platform: when something about an order changes, the merchant POSTs to the platform to say so.
The single most important design decision is this: the payload is a current-state snapshot, not a diff. The merchant MUST send the full order entity on every update, never an incremental delta. There is no "field X changed from A to B" event — every webhook is the complete, current order, and the platform reconciles against what it last knew.
That snapshot carries the fields you'd expect on an order:
id— the order identifiercheckout_id— links the order back to the checkout that created it, for reconciliationline_items— the array of order line items, each with its own derivedstatusfulfillment— fulfillment expectations and eventsadjustments— post-order changes (refunds, corrections), each with astatustotals— the current cost breakdown
Sending the whole entity every time is what makes the system self-healing: if a platform misses a delivery, the next one carries the complete truth anyway. No event-ordering nightmares, no replaying a stream of deltas to rebuild state.
One webhook, many event types
If you've wired up Stripe, you're used to a webhook per event — charge.succeeded, invoice.paid, dozens of named types. UCP is deliberately different: there is one operation, the Order Event Webhook, and the variety lives inside the snapshot as typed event logs. You subscribe once and receive everything that happens to an order. So when people ask "what are the different webhooks in UCP?", the honest answer is: one webhook, several kinds of event — and the events are what you actually branch on.
Two logs carry them.
Fulfillment events — an append-only log tracking actual shipments. Each entry has a type that is an open string, so a merchant can model its real fulfillment process instead of squeezing into a fixed enum. The spec's example values are the ones you'd reach for first:
processing · shipped · in_transit · delivered · failed_attempt · canceled · undeliverable · returned_to_sender
Append-only is the point: a shipment that goes processing → shipped → in_transit → delivered keeps every step inside the snapshot, so the platform sees the whole journey rather than just the latest dot.
Adjustment events — post-order money movements, also an open-string type, typically:
refund · return · credit · price_adjustment · dispute · cancellation
Each adjustment also carries a status enum — pending, completed, failed — so a refund that's been initiated but not yet settled is distinguishable from one that has cleared.
The rollup a platform usually renders is the per-line-item status, derived from quantities: processing, partial, fulfilled, removed. Notably there is no top-level order status field in UCP — status lives at the line-item level, because a real order is often several things at once (one line shipped, one refunded, one still processing). The snapshot model captures that honestly instead of flattening it to a single misleading label.
One more thing worth saying plainly: checkout and identity linking are not webhooks. Checkout is a synchronous request/response, and identity linking is an OAuth redirect flow. The Order Event Webhook is the only place UCP pushes asynchronously from the merchant back to the platform — which is exactly why getting its verification right matters so much.
How the platform advertises where to send them
Discovery reuses the profile machinery from the last post. The platform declares its webhook endpoint as config.webhook_url on the order capability in its agent profile:
{
"ucp": {
"version": "2026-04-08",
"capabilities": {
"dev.ucp.shopping.order": [
{
"version": "2026-04-08",
"config": { "webhook_url": "https://platform.example.com/webhooks/ucp/orders" }
}
]
}
}
}
So the merchant learns where to deliver order events the same way it learned everything else about the platform: by reading the profile at /.well-known/ucp-agent. The profile isn't just a credential — it's the address book too.
How a webhook is signed and verified
This is where the agent-profile groundwork pays off. A spec-compliant order webhook is a signed HTTP request, and verification is symmetric to verifying an agent.
An illustrative delivery looks like this:
POST /webhooks/ucp/orders HTTP/1.1
Host: platform.example.com
UCP-Agent: profile="https://store.example.com/.well-known/ucp"
Webhook-Id: 1f4a9c2e-7b3d-4c1a-9f0e-2a6b8d4e1c00
Webhook-Timestamp: 1749120000
Content-Digest: sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:
Signature-Input: sig1=("@method" "@authority" "@path" "content-digest" "content-type");keyid="store-2026"
Signature: sig1=:MEUCIQ…:
Content-Type: application/json
{ "id": "order_8x2…", "checkout_id": "co_…", "line_items": [ … ], "totals": { … } }
Walking the headers:
UCP-Agentcarries the business profile URL (as an RFC 8941 dictionary). This is the pointer that tells the platform whose keys to fetch — the mirror of how a store resolves an agent.Content-Digestis the SHA-256 digest of the raw request body (RFC 9530). The signature covers the digest, not the body directly, so any tampering with the JSON invalidates it.Signature-Input/Signatureare the RFC 9421 HTTP Message Signature (ECDSA P-256 / ES256).Signature-Inputalso declares exactly which components are covered — in the spec's example:@method,@authority,@path,content-digest, andcontent-type— andkeyidnames which key signed it.Webhook-Idis a unique event identifier (use it to dedupe retries), andWebhook-Timestampis the unix time the event occurred (use it to reject stale deliveries). Both are required headers carried alongside the signature rather than covered by it.
To verify, the platform: reads the business profile URL from UCP-Agent, fetches that profile, matches the keyid to a kid in the business's signing_keys, confirms the Content-Digest against the body it received, and checks the signature. Keys come from signing_keys in the business's UCP profile — exactly the resolution path from the agent-profile post, just resolving a store instead of an agent.
The rules that make it trustworthy
The spec leans on a few hard MUSTs that are easy to skip and painful to skip:
- The business MUST send the full order entity, not deltas (covered above).
- The business MUST retry failed deliveries — so the platform should expect duplicates and dedupe on
Webhook-Id. - The platform MUST respond quickly with a
2xxto acknowledge receipt, then process the event asynchronously. Doing real work inline before you ACK is how you cause the sender to time out and retry, multiplying load.
And the rule that turns "authenticated" into "authorized": the signer must actually own the referenced order. A valid signature from some business isn't enough — it has to be from this order's business. Otherwise any party with a conformant profile could forge status updates for orders that aren't theirs.
How Playground implements it
In Playground (shipped in v0.10.12), the platform accepts business→platform order webhooks at /webhooks/ucp/orders and verifies them per the spec:
- Each delivery's RFC 9421 signature is checked against the sending business's published key — resolved from the
UCP-Agentprofile and matched bykid. - The body digest is confirmed.
- The signer is authorized against the order's owning business — a webhook signed by a business that doesn't own the referenced order is rejected.
- Unsigned or invalid deliveries are refused; verified deliveries are acknowledged and recorded for inspection.
And closing the loop with the last post: the webhook URL is advertised as config.webhook_url on the order capability in Playground's agent profile. The profile we made schema-conformant is the same profile that now tells stores where to send order events.
Why this matters
For merchants, verified webhooks are how you keep an agent's view of an order honest without exposing a polling endpoint to the world. Sign the full entity, retry on failure, and any conformant platform can trust your updates — using the keys you already publish for everything else.
For platforms, this is the other half of being a real party in agentic commerce. The agent profile let you prove who you are on the way in; webhook verification lets you trust what comes back. Together they're a closed loop: named, signed traffic in both directions, with no anonymous hop anywhere in the order lifecycle.
That closed loop — not the checkout call itself — is what makes agent-mediated commerce something a merchant can actually run a business on.
Try it
See the order capability + webhook_url in our profile: ucpplayground.com/.well-known/ucp-agent Read the changelog: ucpplayground.com/changelog Read the spec: ucp.dev/specification/order · RFC 9421 Check your store: ucpchecker.com/check
Related reading: This is the downstream of the UCP agent profile — same signing machinery, reversed. For the buyer side of identity, see UCP identity linking; for why we built an agent that places real orders, see Why We Built UCP Playground.
About UCP Checker
UCP Checker is the independent validation and monitoring layer for the Universal Commerce Protocol. We crawl, validate and grade every public UCP profile we can find, run the merchant directory, the UCP Score and live adoption stats, and — through UCP Playground — test how real AI agents behave against real stores. Verified order webhooks are how those agents stay in sync with the orders they place.
- See our agent profile: ucpplayground.com/.well-known/ucp-agent
- Check your store: ucpchecker.com/check
- Grade it (UCP Score): ucpchecker.com/score
- Browse the directory: ucpchecker.com/directory
- Get notified on changes: ucpchecker.com/alerts
Check your domain's UCP status
See if your storefront is ready for agentic commerce in seconds.
Get the agentic commerce digest every Monday
Real adoption data, ecosystem trends, new spec versions, and the stores that broke or recovered this week. Read by founders and engineers building the next generation of commerce.

