diff --git a/api-reference/webhooks.mdx b/api-reference/webhooks.mdx
index 3d6adb7..87e648b 100644
--- a/api-reference/webhooks.mdx
+++ b/api-reference/webhooks.mdx
@@ -40,7 +40,7 @@ If none of the three is set, nothing is sent. That's not an error, it's silent b
"payer": null,
"payer_type": "human",
"network": "stripe",
- "testnet": false,
+ "livemode": true,
"overpaid_by_cents": null,
"metadata": { "orderId": "order-123" }
}
@@ -57,7 +57,7 @@ If none of the three is set, nothing is sent. That's not an error, it's silent b
On-chain payer address. `null` for card/bank rails.
`human` or `agent`.
CAIP-2 network ID for on-chain rails, or `stripe` for card/bank.
-`true` on a test-mode checkout.
+`true` in live mode, `false` in test mode.
Set when a bank-transfer buyer sent more than the amount due (beyond a 1-cent tolerance). `null` otherwise.
Whatever you set on the checkout (or its link), unchanged.
@@ -76,7 +76,7 @@ If none of the three is set, nothing is sent. That's not an error, it's silent b
"token": "USDC",
"chain_id": 8453,
"network": "eip155:8453",
- "testnet": false,
+ "livemode": true,
"description": null
}
}
@@ -99,14 +99,14 @@ Identical shape to `send.completed`, with `tx_hash: null`.
"token": "USDC",
"chain_id": 8453,
"network": "eip155:8453",
- "testnet": false,
+ "livemode": true,
"description": null
}
}
```
-The raw payload above is the literal snake_case JSON body AgentaOS `POST`s to your `webhookUrl`, this is what you'll parse in any language other than the SDK. `agentaos.webhooks.verify()` (Node.js) parses it and returns a camelCased, typed object instead: `linkId`, `sessionId`, `txHash`, `payerType`, etc. `rail`, `vendor_reference`, `testnet`, and `overpaid_by_cents` are additive fields not yet reflected in the SDK's TypeScript types, they arrive on the wire regardless of language.
+The raw payload above is the literal snake_case JSON body AgentaOS `POST`s to your `webhookUrl`, this is what you'll parse in any language other than the SDK. `agentaos.webhooks.verify()` (Node.js) parses it and returns a camelCased, typed object instead: `linkId`, `sessionId`, `txHash`, `payerType`, `livemode`, etc. `rail`, `vendor_reference`, and `overpaid_by_cents` arrive on the wire but are not yet reflected in the SDK's TypeScript types, they're present regardless of language.
## Delivery
diff --git a/sdk/pay-webhooks.mdx b/sdk/pay-webhooks.mdx
index 1ee4fed..191f0c5 100644
--- a/sdk/pay-webhooks.mdx
+++ b/sdk/pay-webhooks.mdx
@@ -82,7 +82,7 @@ A discriminated union on `type`. `switch`/narrow on it to get typed `data`.
Unique event ID, `evt_`. Present on every event, both on the wire and on the object `verify()` returns.
-
+
Inside every event's `data`, **`amount` is a string**, e.g. `"49.99"`, not the `number` you get back from `checkouts.create()` or `paymentLinks.create()`. Parse it before doing math. See the [money model](/sdk/pay-overview#the-money-model).
@@ -100,6 +100,7 @@ Fires when a checkout session's payment confirms.
Payer wallet address.
CAIP-2 network ID.
+`true` in live mode, `false` in test mode.
Whatever `metadata` you passed at `checkouts.create()`.
### `send.completed`
@@ -114,6 +115,7 @@ Fires when an outbound send (a payout or transfer you initiated) is broadcast su
+`true` in live mode, `false` in test mode.
### `send.failed`
@@ -128,8 +130,24 @@ Same shape as `send.completed`, but `txHash` is always `null` and no broadcast s
+`true` in live mode, `false` in test mode.
+### `subscription.*`
+
+All five `subscription.*` events carry the same `SubscriptionData`, delivered to your organization webhook endpoint.
+
+The subscription ID.
+Stripe status, verbatim: `incomplete`, `active`, `trialing`, `past_due`, `unpaid`, or `canceled`.
+The plan name, from the backing payment link.
+
+Per-cycle price in integer minor units. `1000` means `€10.00`.
+ISO 8601 timestamp, or `null` before the first cycle is set.
+`true` once a cancellation is scheduled for period end.
+
+
+`true` in live mode, `false` in test mode.
+
`successUrl` is best-effort, the customer might close their browser before the redirect lands. Fulfill orders from the webhook (server-to-server, reliable), not the redirect.
diff --git a/webhooks/events.mdx b/webhooks/events.mdx
index 4956c28..6027ca3 100644
--- a/webhooks/events.mdx
+++ b/webhooks/events.mdx
@@ -3,7 +3,7 @@ title: "Events"
description: "Every webhook event AgentaOS sends: full payload fields, types, and a JSON example for each."
---
-AgentaOS sends three event types today. Every event shares the same envelope, an `id`, a `type`, and a `data` object whose shape depends on the type. See [Webhooks](/payments/webhooks) for how to register a URL and verify the signature before trusting any of this.
+AgentaOS sends webhook events for checkouts, outbound sends, and subscriptions. Every event shares the same envelope, an `id`, a `type`, and a `data` object whose shape depends on the type. See [Webhooks](/payments/webhooks) for how to register a URL and verify the signature before trusting any of this.
Every `amount` field below is a **string** (e.g. `"49.99"`), not a number. This differs from `amount` on SDK create calls (checkouts, payment links), which is a plain number in currency units. Parse it before doing arithmetic.
@@ -18,7 +18,7 @@ The field names below are the literal snake_case keys AgentaOS `POST`s to your `
- One of `checkout.session.completed`, `send.completed`, `send.failed`. Switch on this to decide how to parse `data`.
+ One of `checkout.session.completed`, `send.completed`, `send.failed`, or a `subscription.*` event. Switch on this to decide how to parse `data`.
@@ -62,8 +62,8 @@ Fired when a checkout is paid, by card, wallet, or stablecoin. This is the event
CAIP-2 network ID for on-chain rails, e.g. `"eip155:8453"` for Base, or `stripe` for card/bank.
-
- `true` on a test-mode checkout, `false` in live.
+
+ `true` in live mode, `false` in test mode.
Set when a bank-transfer buyer sent more than the amount due (beyond a 1-cent tolerance). `null` otherwise.
@@ -92,7 +92,7 @@ Fired when a checkout is paid, by card, wallet, or stablecoin. This is the event
"payer": null,
"payer_type": "human",
"network": "stripe",
- "testnet": false,
+ "livemode": true,
"overpaid_by_cents": null,
"metadata": { "orderId": "order-123" }
}
@@ -128,8 +128,8 @@ Fired when an outbound stablecoin send you initiated confirms on-chain.
CAIP-2 network ID, e.g. `"eip155:8453"`.
-
- `true` on a test-mode send, `false` in live.
+
+ `true` in live mode, `false` in test mode.
Optional description you attached to the send. `null` if none was set.
@@ -149,7 +149,7 @@ Fired when an outbound stablecoin send you initiated confirms on-chain.
"token": "USDC",
"chain_id": 8453,
"network": "eip155:8453",
- "testnet": false,
+ "livemode": true,
"description": "Payout to contractor"
}
}
@@ -184,8 +184,8 @@ Fired when an outbound send fails to broadcast, network error, insufficient bala
CAIP-2 network ID, e.g. `"eip155:8453"`.
-
- `true` on a test-mode send, `false` in live.
+
+ `true` in live mode, `false` in test mode.
Optional description you attached to the send. `null` if none was set.
@@ -205,7 +205,7 @@ Fired when an outbound send fails to broadcast, network error, insufficient bala
"token": "USDC",
"chain_id": 8453,
"network": "eip155:8453",
- "testnet": false,
+ "livemode": true,
"description": null
}
}
@@ -215,6 +215,48 @@ Fired when an outbound send fails to broadcast, network error, insufficient bala
`send.failed` means the transaction never landed on-chain. It's distinct from a payment dispute or a card decline, those don't go through this event, they affect your balance directly. See [Payouts](/payouts/overview).
+## Subscription events
+
+Subscription lifecycle events, delivered to your **organization** webhook endpoint. All five share the same `data` shape.
+
+- **`subscription.created`** first payment succeeded; now active (or trialing).
+- **`subscription.renewed`** a cycle was paid and renewed for another period.
+- **`subscription.payment_failed`** a renewal payment failed; now past due.
+- **`subscription.updated`** the subscription changed (commonly `cancel_at_period_end`).
+- **`subscription.canceled`** terminal; no further charges.
+
+
+ The subscription ID.
+ Stripe status: `active`, `trialing`, `past_due`, `unpaid`, `canceled`, `incomplete`.
+ Plan name, from the backing payment link.
+ e.g. `"EUR"`.
+ Per-cycle price in integer minor units. `1000` = `€10.00`.
+ ISO 8601, or `null` before the first cycle.
+ `true` if cancellation is scheduled for period end.
+
+
+ `true` in live mode, `false` in test mode.
+
+
+```json Example
+{
+ "id": "evt_3f2a1b0c-9d8e-7f6a-5b4c-3d2e1f0a9b8c",
+ "type": "subscription.renewed",
+ "data": {
+ "id": "sub_6pC2lNB6joCRQIZ1aMrTpi",
+ "status": "active",
+ "plan_name": "Pro Monthly",
+ "currency": "EUR",
+ "amount_minor": 1000,
+ "current_period_end": "2026-09-19T12:00:00.000Z",
+ "cancel_at_period_end": false,
+ "customer_email": "customer@example.com",
+ "customer_name": "Alex Rivera",
+ "livemode": true
+ }
+}
+```
+
## Next steps