Subscription Status Webhooks
Receive a webhook each time a subscription changes status.
Each time a subscription moves from one status to another, Acquired sends a billing.subscription.status_changed webhook. This lets your system keep its own view of a subscriber's access in step with Acquired Billing, for example suspending access when a subscription becomes past_due and restoring it when it returns to active.
The webhook uses the standard Webhook-Version 2 envelope. For webhook headers and how to validate the integrity of a webhook using its hash, refer to the webhook structure section of our Webhook Notifications guide.
NoteWhile webhooks provide real-time updates, they can occasionally be interrupted or missed. We recommend also using
GET /subscriptions/{subscription_id}as a fallback to reliably confirm a subscription's current status.
When is the webhook sent?
A webhook is sent for every status change after the subscription has been created, including:
- Activation — an
incompletesubscription is activated and becomesactive, ortrialingwhen atrial_endwas set. - Trial ending — a
trialingsubscription takes its first payment and becomesactive. - Failed payments and recovery — a subscription becomes
past_dueafter a failed payment, and returns toactiveonce it is recovered. See how failed payments affect the subscription lifecycle. - Pausing — a subscription is paused via the API or the Hub.
- Cancellation — a subscription is cancelled via the API or the Hub, reaches its billing cycle limit, or is cancelled automatically after repeated payment failures.
No webhook at creationCreating a subscription does not send a webhook, whatever status it is created in (
incomplete,activeortrialing). Use the status returned in the Create Subscription response as the starting point; webhooks report every change from there.Scheduling a cancellation with
?at_period_end=truedoes not change the status, so no webhook is sent until the subscription is actually cancelled at the end of the period.
Webhook body
| Field | Type | Description |
|---|---|---|
subscription_id | string | ULID of the subscription whose status changed. |
customer_id | string | UUID of the customer the subscription belongs to. |
old_status | string | The status before the change. |
new_status | string | The status after the change. |
changed_at | string | When the status changed, as an ISO 8601 date-time with a UTC offset. |
old_status and new_status are one of incomplete, trialing, active, past_due, paused or cancelled. See Subscription statuses for what each status means.
The body may growAcquired may add new optional fields to
webhook_bodywithout changing the webhook type. Ignore fields you don't recognise, and always calculate thehashover the complete payload as received.
Examples
Subscription activated — an incomplete subscription has its payment method added:
{
"webhook_type": "billing.subscription.status_changed",
"webhook_id": "3f9c2e1a-7b4d-4c8e-9a1f-2d6b8e0c5a71",
"timestamp": 1791290763,
"webhook_body": {
"subscription_id": "01KV7YAB5GMD32H2A2DHZ6FKZ7",
"customer_id": "c2b8f0d4-5e6a-4f1b-8c3d-9a7e2f1b0c64",
"old_status": "incomplete",
"new_status": "active",
"changed_at": "2026-10-06T12:46:02.5734211+00:00"
}
}Payment failed — a bill payment was declined and the subscription became past_due:
{
"webhook_type": "billing.subscription.status_changed",
"webhook_id": "8a1d4b6e-2c3f-4e5a-b7d9-0f1e2a3b4c5d",
"timestamp": 1793969163,
"webhook_body": {
"subscription_id": "01KV7YAB5GMD32H2A2DHZ6FKZ7",
"customer_id": "c2b8f0d4-5e6a-4f1b-8c3d-9a7e2f1b0c64",
"old_status": "active",
"new_status": "past_due",
"changed_at": "2026-11-06T12:46:01.1029384+00:00"
}
}Where the webhook is sent
- The subscription's
webhook_url, when one is set. It can be supplied when you create a subscription, or when activating via Components it is persisted from thewebhook_urlon the payment session. This is the same URL that receives Bill Payment Webhooks. - Otherwise, your webhook endpoint configured in the Hub for subscription status changes.
Handling the webhook
Respond quickly with a 2xx. Any 2xx response marks the webhook as delivered. Acquired waits up to 10 seconds for a response; a timeout, a connection failure or any other status code counts as a failed attempt.
Expect retries, and deduplicate on webhook_id. A failed attempt is retried, with increasing and randomised gaps between attempts, for up to 10 attempts in total. Every retry carries the same webhook_id, so if you receive a webhook_id you have already processed, acknowledge it with a 2xx and take no further action.
Order by changed_at, not by arrival. Because of retries, webhooks for the same subscription can arrive out of order. Compare changed_at with the last change you recorded for that subscription_id, and ignore any webhook older than it.
Updated 10 days ago