Webhook Events
Complete reference for all webhook event types, payloads, and delivery behavior.
Webhook Events
Bag sends webhook events to your registered endpoint when payment and subscription lifecycle events occur. All events are signed with HMAC-SHA256 for verification.
Event List
| Event | Category | Description |
|---|---|---|
payment.completed | Payment | Payment confirmed on-chain |
payment.failed | Payment | Transaction reverted or timed out |
subscription.created | Subscription | New subscription created |
subscription.updated | Subscription | Subscription plan or status changed |
subscription.renewed | Subscription | Recurring payment succeeded |
subscription.renewal_due | Subscription | Upcoming renewal requires customer action |
subscription.past_due | Subscription | Payment failed, grace period active |
subscription.canceled | Subscription | Subscription permanently canceled |
Common Envelope
Every webhook delivery follows this structure:
{
"event": "event.name",
"data": { ... },
"webhookDeliveryId": "d4e5f6a1-b2c3-7890-abcd-ef1234567890",
"timestamp": "2026-04-01T12:00:00.000Z"
}| Field | Type | Description |
|---|---|---|
event | string | Event type identifier |
data | object | Event-specific payload (see below) |
webhookDeliveryId | string | Unique delivery ID for idempotency |
timestamp | string | ISO 8601 timestamp of the event |
Sandbox events also include "livemode": false in the data object.
Payment Events
payment.completed
Fired when a payment is confirmed on-chain.
{
"event": "payment.completed",
"data": {
"sessionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"paymentLinkId": "3b9f8f22-7f1f-4c3a-8f5a-2d7a0d3e9b1c",
"txHash": "0xabc123def456...",
"merchantWalletAddress": "0xYourWalletAddress",
"amount": 29.99,
"network": "base_sepolia"
},
"webhookDeliveryId": "d4e5f6a1-b2c3-7890-abcd-ef1234567890",
"timestamp": "2026-04-01T12:05:00.000Z"
}| Field | Type | Description |
|---|---|---|
sessionId | string | Checkout session ID |
paymentLinkId | string | Payment link that initiated the checkout |
txHash | string | On-chain transaction hash |
merchantWalletAddress | string | Merchant's receiving wallet |
amount | number | Payment amount in USD |
network | string | Blockchain network (e.g., base, ethereum, polygon, solana) |
Recommended action: Fulfill the order, grant access, send a receipt.
payment.failed
Fired when a transaction reverts or the checkout session times out.
{
"event": "payment.failed",
"data": {
"sessionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"paymentLinkId": "3b9f8f22-7f1f-4c3a-8f5a-2d7a0d3e9b1c",
"reason": "Transaction reverted"
},
"webhookDeliveryId": "d4e5f6a1-b2c3-7890-abcd-ef1234567890",
"timestamp": "2026-04-01T12:05:00.000Z"
}| Field | Type | Description |
|---|---|---|
sessionId | string | Checkout session ID |
paymentLinkId | string | Payment link that initiated the checkout |
reason | string | Human-readable failure reason |
Recommended action: Notify the customer, allow retry.
Subscription Events
subscription.created
Fired when a new subscription is created (after initial payment or trial start).
{
"event": "subscription.created",
"data": {
"subscriptionId": "sub_abc123",
"paymentLinkId": "3b9f8f22-7f1f-4c3a-8f5a-2d7a0d3e9b1c",
"customerEmail": "customer@example.com",
"status": "active",
"currentPeriodEnd": "2026-05-01T12:00:00.000Z",
"livemode": true
},
"webhookDeliveryId": "d4e5f6a1-b2c3-7890-abcd-ef1234567890",
"timestamp": "2026-04-01T12:00:00.000Z"
}| Field | Type | Description |
|---|---|---|
subscriptionId | string | Subscription ID |
paymentLinkId | string | Payment link used to create the subscription |
customerEmail | string | Customer's email address |
status | string | Initial status (active or trialing) |
currentPeriodEnd | string | End of the current billing period (ISO 8601) |
livemode | boolean | true for production, false for sandbox |
Recommended action: Store the subscription ID, grant access.
subscription.updated
Fired when a subscription's status or details change (pause, resume, plan change, cancelAtPeriodEnd set).
{
"event": "subscription.updated",
"data": {
"subscriptionId": "sub_abc123",
"status": "paused",
"previousStatus": "active",
"livemode": true
},
"webhookDeliveryId": "d4e5f6a1-b2c3-7890-abcd-ef1234567890",
"timestamp": "2026-04-15T10:00:00.000Z"
}| Field | Type | Description |
|---|---|---|
subscriptionId | string | Subscription ID |
status | string | New status |
previousStatus | string | Status before the change |
cancelAtPeriodEnd | boolean | Present when cancellation is scheduled for period end |
currentPeriodEnd | string | Present when the period changes (e.g., on resume) |
livemode | boolean | true for production, false for sandbox |
Recommended action: Update stored subscription state.
subscription.renewed
Fired when a recurring payment succeeds.
{
"event": "subscription.renewed",
"data": {
"subscriptionId": "sub_abc123",
"currentPeriodStart": "2026-05-01T12:00:00.000Z",
"currentPeriodEnd": "2026-06-01T12:00:00.000Z",
"amount": 29.99,
"txHash": "0xdef789...",
"livemode": true
},
"webhookDeliveryId": "d4e5f6a1-b2c3-7890-abcd-ef1234567890",
"timestamp": "2026-05-01T12:00:30.000Z"
}| Field | Type | Description |
|---|---|---|
subscriptionId | string | Subscription ID |
currentPeriodStart | string | Start of the new billing period |
currentPeriodEnd | string | End of the new billing period |
amount | number | Renewal payment amount |
txHash | string | On-chain transaction hash (stablecoin) or charge ID (card) |
livemode | boolean | true for production, false for sandbox |
Recommended action: Extend access period.
subscription.renewal_due
Fired when an upcoming renewal requires customer action (e.g., stablecoin re-approval).
{
"event": "subscription.renewal_due",
"data": {
"subscriptionId": "sub_abc123",
"currentPeriodEnd": "2026-10-01T12:00:00.000Z",
"reason": "permit2_approval_expiring",
"livemode": true
},
"webhookDeliveryId": "d4e5f6a1-b2c3-7890-abcd-ef1234567890",
"timestamp": "2026-09-25T12:00:00.000Z"
}| Field | Type | Description |
|---|---|---|
subscriptionId | string | Subscription ID |
currentPeriodEnd | string | When the current period ends |
reason | string | Why action is needed (e.g., permit2_approval_expiring) |
livemode | boolean | true for production, false for sandbox |
Recommended action: Notify the customer to re-approve their wallet or update payment method.
subscription.past_due
Fired when a renewal payment fails and the subscription enters the dunning grace period.
{
"event": "subscription.past_due",
"data": {
"subscriptionId": "sub_abc123",
"failedAt": "2026-05-01T12:00:00.000Z",
"retryCount": 1,
"nextRetryAt": "2026-05-02T12:00:00.000Z",
"livemode": true
},
"webhookDeliveryId": "d4e5f6a1-b2c3-7890-abcd-ef1234567890",
"timestamp": "2026-05-01T12:01:00.000Z"
}| Field | Type | Description |
|---|---|---|
subscriptionId | string | Subscription ID |
failedAt | string | When the payment failed |
retryCount | number | Number of retry attempts so far |
nextRetryAt | string | When the next retry is scheduled |
livemode | boolean | true for production, false for sandbox |
Recommended action: Warn the customer, suggest updating payment method.
subscription.canceled
Fired when a subscription is permanently canceled (immediately or at period end).
{
"event": "subscription.canceled",
"data": {
"subscriptionId": "sub_abc123",
"cancelAtPeriodEnd": false,
"livemode": true
},
"webhookDeliveryId": "d4e5f6a1-b2c3-7890-abcd-ef1234567890",
"timestamp": "2026-04-20T15:30:00.000Z"
}| Field | Type | Description |
|---|---|---|
subscriptionId | string | Subscription ID |
cancelAtPeriodEnd | boolean | true if canceled at period end, false if immediate |
livemode | boolean | true for production, false for sandbox |
Recommended action: Revoke access (immediately or at period end based on cancelAtPeriodEnd).
Delivery and Retries
Bag retries failed deliveries up to 5 times with exponential backoff:
| Attempt | Delay | Method |
|---|---|---|
| 1 | Immediate | Inline |
| 2 | +5 seconds | Inline |
| 3 | +10 seconds | Inline |
| 4 | +40 seconds | Background |
| 5 | +80 seconds | Background |
After 5 failed attempts, the delivery is marked as failed. You can retry manually from the dashboard under Webhooks > Delivery Log.