Skip to main content
By the end of this page you have an Express endpoint that verifies AgentaOS’s signature and marks an order paid when a checkout completes. It fulfills each order once, even if the same event arrives twice.

Before you start

  • Node.js 20.6 or later, and @agentaos/pay and Express installed (npm install @agentaos/pay express). The samples run on Express 4 and 5.
  • A URL AgentaOS can reach over HTTPS. AgentaOS does not deliver to localhost or a private address, so for local development run a tunnel service that gives http://localhost:3000 a public HTTPS address.
  • A payment link or checkout to test against. See Accept your first payment if you don’t have one yet.
1

Register a webhook URL and reveal the signing secret

In app.agentaos.ai, open Settings → Developers → Webhooks, enter the HTTPS URL you want events sent to, for example https://myshop.com/webhooks, and click Save. Click Reveal signing secret, copy the whsec_... value, and add it to your server’s environment as AGENTAOS_WEBHOOK_SECRET. Never commit it, never log it, never send it to the client.
2

Set up the raw-body route

Signature verification signs the raw request body. If a JSON body parser runs first, the reserialized body won’t byte-for-byte match what was signed, and every event will fail verification. Mount express.raw() on the webhook route only, and keep express.json() for everything else.
3

Verify the signature

webhooks.verify() parses the t=...,v1=... header, rejects a timestamp older than 5 minutes or in the future, recomputes the HMAC-SHA256 digest in constant time, and returns a typed WebhookEvent. If any check fails, it throws WebhookVerificationError. Keep your server’s clock synced (NTP): a clock that runs behind rejects every event.
4

React to checkout.session.completed, idempotently

Key your fulfillment logic on event.data.sessionId, and check whether you’ve already processed it before doing anything. Retries mean the same event can arrive more than once, so your handler must be safe to run twice.
metadata holds what you set on checkouts.create, so metadata.customerId is your own user id. verify() camelCases every key, including the keys inside metadata: use camelCase keys in metadata, because a key like user_id comes back as userId. The event’s customerId field is different: it is AgentaOS’s own id for the buyer, not yours. A subscription renewal fires this event without your metadata. See Event order for subscriptions and Sell a subscription.event.data.amount is a string, such as "49.99". See the money model.
5

Respond 200 quickly

Verify, queue or record the event, then respond. Do slow work, emails, external API calls, outside the request so AgentaOS doesn’t time out waiting for you.

Full example

webhook-server.ts
Not using Node? The signing algorithm is HMAC-SHA256 over {timestamp}.{raw_body}, and any language can check it. See manual verification in Python, Go, and PHP.

Test it end to end

1

Start your server

Save the example as webhook-server.ts, put AGENTAOS_API_KEY and AGENTAOS_WEBHOOK_SECRET in a .env file, and run npx tsx --env-file=.env webhook-server.ts. Make sure your tunnel or production URL points at it.
2

Create a checkout with a webhookUrl

Use the link or checkout from Accept your first payment, or create a new one with webhookUrl set to your endpoint.
3

Pay with the test card

4242 4242 4242 4242, any future expiry, any CVC, typed directly into the hosted checkout page.
4

Watch your server log the order as paid

You should see Order <sessionId> paid: 49.99 EUR in your logs within seconds of the payment clearing.

Verify it worked

  • Your endpoint returned 200 for the delivery. The event list in Settings → Developers → Webhooks shows each delivery’s status and response code.
  • Your logs show exactly one fulfillment for that sessionId, even if AgentaOS retries the delivery.
  • An invalid or missing X-AgentaOS-Signature header gets rejected with 400, not silently processed. Try POSTing a fake payload without a valid signature to confirm webhooks.verify() throws as expected.

Next steps

Event reference

Every event type, its full payload, and a JSON example.

Webhooks (concept)

Manual verification in Python, Go, and PHP, plus retry and delivery details.

Accept your first payment

Create the checkout that triggers this handler.

Payouts

What happens to your balance after the payment lands.