id, a type, a business, and a data object whose shape depends on the type. See Webhooks for how to register a URL and verify the signature before you trust any of this.
Amounts follow the money model: amount is a string in currency units, and amount_minor is an integer in the smallest currency unit.
The field names below are the literal snake_case keys AgentaOS
POSTs to your webhookUrl: parse these in any language. The SDK’s webhooks.verify() (Node.js) returns the same fields camelCased: link_id becomes linkId, session_id becomes sessionId, trial_end becomes trialEnd, and so on.When each event fires
Each event type below lists every trigger in the platform. Which action fires which event gives the same facts in the other direction, per action.Event envelope
string
The event ID. A paid checkout’s id is
evt_ plus the sale’s transaction id, a send’s id is evt_ plus data.transaction_id, and every other event gets a random id such as evt_4e1b9a0c2d3f5a67. An event keeps its id across delivery attempts and a retry from the dashboard. The SDK’s WebhookEvent type does not declare id yet, so deduplicate on the keys in Deduplication.string
One of
checkout.session.completed, checkout.session.bank_transfer_pending, send.completed, send.failed, subscription.created, subscription.renewed, subscription.payment_failed, subscription.updated, subscription.canceled, dispute.created, dispute.closed, account.updated, or webhook.test. Switch on this to decide how to parse data.string
The id of the business the event happened in. On your own events it is your own id. If you are a platform, your webhook also receives the events of every business you manage, and there
business is your client’s id. See Platform webhooks.object
Event-specific payload. See each event below.
Which action fires which event
Events describe money and account state, not every API call. An action that changes nothing about a payment, a subscription or a business’s verification sends no event. A “check” below is AgentaOS reading the current state from the card processor. While the buyer’s checkout page is open, AgentaOS checks within seconds. A background check runs every 5 minutes for subscriptions and every 10 minutes for disputes.Your API calls
Actions outside your API calls
For the order of the subscription events, see Event order for subscriptions.
Which URL receives an event
Acheckout.session.completed event goes to one URL: the checkout’s own webhookUrl if it has one, else its payment link’s, else your account’s webhook URL. The first one found is the only one that receives it. Every other event goes to your account’s webhook URL. If no URL applies, AgentaOS sends nothing and shows no error.
Delivery and retries
AgentaOS sends each event as aPOST with Content-Type: application/json and an X-AgentaOS-Signature header. A delivery follows these rules:
- Each attempt times out after 10 seconds. A response outside
2xx, a timeout, or a network error counts as a failed attempt. - AgentaOS makes up to 3 attempts. It waits 1 second before the second attempt and 2 seconds before the third.
- The 3 attempts carry the same signature, with the same
t. - AgentaOS does not follow redirects. A
3xxresponse is a failed attempt. - AgentaOS refuses a URL that resolves to a private or internal address, such as
localhost,10.0.0.0/8or192.168.0.0/16, and marks the event failed without an attempt. For local development, expose your server through a public HTTPS tunnel.
failed in Settings → Developers → Webhooks and sends it no more by itself. An owner or admin can send a failed event again with the retry button on its row (labeled “Retry webhook”). The retry sends the same body, signed again with a new t.
Events are delivered independently of each other. Two events about the same subscription can arrive in either order.
Deduplication
The same event can arrive more than once: after a retry, or when your server stored the event but answered too slowly. Record a key per event and skip one you have already handled.Event order for subscriptions
AgentaOS checks each subscription with the card processor on a schedule and sends the events for what changed. The events below are what your endpoint receives. Unlock access oncheckout.session.completed: it carries the metadata of the checkout, with your own ids.
The
checkout.session.completed for a renewal, or for the first charge after a trial, has its own session_id. Its metadata is AgentaOS’s own, not the metadata you set on the checkout, so your metadata.customerId is not on it. Match it to your user through metadata.subscriptionId, the subscription’s id, which is also on the first checkout event. The metadata of a renewal, and of the first charge after a trial, also has subscriptionCycle: true.
checkout.session.completed
Fires when:
- A buyer pays a checkout by card, bank transfer or stablecoin.
- A free trial starts:
amountis"0"andtrialistrue. - A trial with a trial price starts:
amountis the amount charged. - A later subscription charge succeeds: a renewal, the first charge after a trial, or an upgrade’s prorated charge.
- A subscriber credit pays a renewal in full:
amountis"0".
The SDK’s
CheckoutCompletedData type differs from the wire body in two ways. It types txHash, payer and linkId as string, but they are null in the cases above, so check for null. It does not declare rail, vendorReference, customerId, buyerEmail, buyerName, overpaidByCents, trial or trialEnd yet, although verify() returns them: read them with a cast, as shown in Payment SDK: Webhooks.Example
send.completed
Fires when: an outbound stablecoin send that you started is confirmed.
Example
send.failed
Fires when: an outbound stablecoin send that you started fails: the broadcast fails, for example on a network error or an insufficient balance, or the transaction reverts on-chain.
Example
Subscription events
Fires when:subscription.created: the subscription becomesactiveortrialingfor the first time: the buyer pays the first period, or saves a card for a trial.subscription.renewed: a charge after the first one succeeds: a renewal, the first charge after a trial, or an upgrade’s prorated charge. A renewal that a subscriber credit pays also fires it. The first period does not fire it.subscription.payment_failed: the status becomespast_dueorunpaid: a renewal charge fails, including the first charge after a trial. It fires once when the status first moves there; a move frompast_duetounpaid, and every later failed retry, sends nothing more.subscription.updated: a cancel is scheduled or resumed (cancel_at_period_end), an upgrade is applied (newplan_name,link_idandamount_minor), a downgrade is scheduled (pending_planset), a scheduled downgrade is reverted (pending_plannull again), or a scheduled downgrade takes over at the period end.subscription.canceled: the status becomescanceled: at the period end after a scheduled cancel, or when the card processor ends the subscription for another reason. No more charges follow.
data shape. For the order they arrive in, see Event order for subscriptions.
Example
Dispute events
Fires when:dispute.created: AgentaOS finds a new dispute on one of your sales.dispute.closed: a dispute closes in your favor (statusiswon), or a bank inquiry closes without a chargeback (statusiswarning_closed). Either way the sale is released. A lost dispute sends no event: read it from the sale’smor_status, which becomescharged_back.
warning_ statuses). Both events share the same data shape.
transaction_id is the sale’s transaction id. For a one-time sale and for every subscription charge, it equals that charge’s checkout.session.completed id without the evt_ prefix, so keep those event ids and a dispute finds its sale, and for a renewal its metadata.subscriptionId. subscription.renewed cannot be matched to a dispute: its id is random and it carries no transaction id.
Example
account.updated
Fires when: a business’s verification changes, in live mode only.
- Its identity check is verified or declined.
- Our reviewers approve, reject, hold or resume the business, or ask for a change.
- Our reviewers verify or withdraw its identity by hand.
Example
webhook.test
Fires when: you click Send test event in Settings → Developers → Webhooks.
Use it to check that your endpoint receives and verifies events. It goes to your account’s webhook URL, signed like every other event.
Example
Next steps
Verify signatures
How to register a URL and verify the HMAC-SHA256 signature, in TypeScript, Python, Go, and PHP.
Payment SDK: Webhooks
The
webhooks resource in @agentaos/pay.Platform webhooks
One endpoint for all the businesses you manage.