Subscriptions
Subscriptions
Subscriptions let you charge customers on a recurring schedule. You define a price with a billing interval, create a subscription linking a customer and a payment method to that price, and the billing engine handles invoice generation and payment collection automatically.
Before creating a subscription, you need:
- A customer with a saved payment method
- A recurring price (see below)
Subscription lifecycle
- Create a recurring price that defines the amount, currency, and billing interval
- Create a subscription with at least one item referencing a recurring price
- An invoice is generated and the saved payment method is charged automatically
- On success, the subscription becomes
ACTIVEand renews at the end of each billing period - On payment failure, the subscription enters
PAST_DUEand eventuallyUNPAIDif not resolved
Step 1: Create a recurring price
A price defines the amount, currency, and billing schedule. Set the recurring field to make it a recurring price that can be attached to subscriptions.
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/prices \
-H "Authorization: Api-Key $API_KEY" \
-H "Idempotency-Key: YOUR_UNIQUE_KEY" \
-H "Content-Type: application/json" \
-d '{
"price": {
"display_name": "Pro Plan (Monthly)",
"currency": "GBP",
"unit_amount": "2900",
"recurring": {
"interval": "MONTH",
"interval_count": 1
}
}
}'Response
{
"name": "accounts/acct_YOUR_ACCOUNT/prices/pr_PRICE_ID",
"display_name": "Pro Plan (Monthly)",
"currency": "GBP",
"unit_amount": "2900",
"active": true,
"recurring": {
"interval": "MONTH",
"interval_count": 1
},
"create_time": "2026-02-19T08:30:12Z",
"update_time": "2026-02-19T08:30:12Z"
}The unit_amount is in the smallest currency unit — 2900 = 29.00 GBP.
Prices are designed to be stable. Instead of changing the amount or interval on an existing price, create a new price and deactivate the old one by setting active to false via UpdatePrice.
Deactivating a price prevents it from being used for new subscriptions, but existing subscriptions that already reference it will continue to bill against it.
Recurring intervals
| Interval | Example |
|---|---|
DAY | Every N days |
WEEK | Every N weeks |
MONTH | Every N months |
YEAR | Every N years |
Set interval_count to bill at multiples — for example, interval: MONTH with interval_count: 3 means "every 3 months".
MONTH and YEAR intervals use calendar arithmetic anchored to the subscription's start date. For example, a monthly subscription created on January 31 will next bill on February 28 (or 29 in a leap year), then March 31, and so on.
Step 2: Create a subscription
Create a subscription with at least one item referencing a recurring price. All items must share the same billing interval and currency.
The Idempotency-Key header lets you safely retry the request if it fails — see Idempotency.
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/subscriptions \
-H "Authorization: Api-Key $API_KEY" \
-H "Idempotency-Key: YOUR_UNIQUE_KEY" \
-H "Content-Type: application/json" \
-d '{
"subscription": {
"customer": "accounts/acct_YOUR_ACCOUNT/customers/cus_CUSTOMER_ID",
"default_payment_method": "accounts/acct_YOUR_ACCOUNT/payment-methods/pm_PAYMENT_METHOD_ID"
},
"items": [
{
"display_name": "Pro Plan",
"price": "accounts/acct_YOUR_ACCOUNT/prices/pr_PRICE_ID",
"quantity": 1
}
]
}'Response
{
"name": "accounts/acct_YOUR_ACCOUNT/subscriptions/sub_SUBSCRIPTION_ID",
"customer": "accounts/acct_YOUR_ACCOUNT/customers/cus_CUSTOMER_ID",
"state": "ACTIVE",
"billing_cycle_anchor": "2026-02-19T08:31:10Z",
"items": [
"accounts/acct_YOUR_ACCOUNT/subscriptions/sub_SUBSCRIPTION_ID/subscription-items/si_ITEM_ID"
],
"default_payment_method": "accounts/acct_YOUR_ACCOUNT/payment-methods/pm_PAYMENT_METHOD_ID",
"latest_invoice": "accounts/acct_YOUR_ACCOUNT/invoices/in_INVOICE_ID",
"next_action_time": "2026-03-19T08:31:10Z",
"create_time": "2026-02-19T08:31:10Z",
"update_time": "2026-02-19T08:31:10Z"
}On creation, the billing engine immediately generates an invoice and attempts to charge the saved payment method. If payment succeeds, the subscription transitions to ACTIVE. If payment fails, the subscription stays in INCOMPLETE and the invoice remains OPEN. You can complete activation by paying the invoice directly via the pay invoice endpoint — for example, with a different payment method. If the invoice is not paid within 23 hours, the subscription expires to INCOMPLETE_EXPIRED.
The billing_cycle_anchor is the reference timestamp for computing future billing periods. The next_action_time shows when the next scheduled action will occur.
Multiple items
A subscription can have multiple items. Each item references a recurring price and has its own quantity. All items must share the same billing interval and currency.
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/subscriptions \
-H "Authorization: Api-Key $API_KEY" \
-H "Idempotency-Key: YOUR_UNIQUE_KEY" \
-H "Content-Type: application/json" \
-d '{
"subscription": {
"customer": "accounts/acct_YOUR_ACCOUNT/customers/cus_CUSTOMER_ID",
"default_payment_method": "accounts/acct_YOUR_ACCOUNT/payment-methods/pm_PAYMENT_METHOD_ID"
},
"items": [
{
"display_name": "Pro Plan",
"price": "accounts/acct_YOUR_ACCOUNT/prices/pr_PLAN_PRICE_ID",
"quantity": 1
},
{
"display_name": "Extra seats",
"price": "accounts/acct_YOUR_ACCOUNT/prices/pr_SEAT_PRICE_ID",
"quantity": 5
}
]
}'Each item generates its own line on the subscription's invoices. The total charged is the sum of unit_amount * quantity across all items.
Free trials
To start a subscription with a free trial, set trial_end to a future timestamp. The subscription starts in TRIALING — the payment method is not charged at creation.
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/subscriptions \
-H "Authorization: Api-Key $API_KEY" \
-H "Idempotency-Key: YOUR_UNIQUE_KEY" \
-H "Content-Type: application/json" \
-d '{
"subscription": {
"customer": "accounts/acct_YOUR_ACCOUNT/customers/cus_CUSTOMER_ID",
"default_payment_method": "accounts/acct_YOUR_ACCOUNT/payment-methods/pm_PAYMENT_METHOD_ID",
"trial_end": "2026-03-05T00:00:00Z"
},
"items": [
{
"display_name": "Pro Plan",
"price": "accounts/acct_YOUR_ACCOUNT/prices/pr_PRICE_ID"
}
]
}'{
"name": "accounts/acct_YOUR_ACCOUNT/subscriptions/sub_SUBSCRIPTION_ID",
"state": "TRIALING",
"trial_end": "2026-03-05T00:00:00Z",
"next_action_time": "2026-03-05T00:00:00Z"
}At trial_end, the billing engine automatically generates an invoice and charges the payment method. On success the subscription transitions to ACTIVE. On failure it moves to PAST_DUE.
How billing works
The billing engine runs automatically. At each billing event (activation or renewal), it:
- Creates an invoice with line items for each subscription item
- Finalizes the invoice
- Charges the subscription's
default_payment_method
Invoices generated by subscriptions follow the same lifecycle as regular invoices — you can view them via the invoice endpoints. The subscription's latest_invoice field always points to the most recently generated invoice.
Payment failures and grace periods
When a renewal payment fails, the subscription moves to PAST_DUE. If the outstanding invoice is paid within the grace period — either by the customer through the hosted invoice URL or by your server via the pay invoice endpoint — the subscription automatically transitions back to ACTIVE.
If the grace period expires without payment, the subscription transitions to UNPAID. Once UNPAID, paying the outstanding invoice directly will not reactivate the subscription — you must use the ReactivateSubscription endpoint instead (see Reactivating an unpaid subscription).
You can configure grace_period_days to control how long the subscription stays in PAST_DUE before transitioning to UNPAID.
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/subscriptions \
-H "Authorization: Api-Key $API_KEY" \
-H "Idempotency-Key: YOUR_UNIQUE_KEY" \
-H "Content-Type: application/json" \
-d '{
"subscription": {
"customer": "accounts/acct_YOUR_ACCOUNT/customers/cus_CUSTOMER_ID",
"default_payment_method": "accounts/acct_YOUR_ACCOUNT/payment-methods/pm_PAYMENT_METHOD_ID",
"grace_period_days": 7
},
"items": [
{
"display_name": "Pro Plan",
"price": "accounts/acct_YOUR_ACCOUNT/prices/pr_PRICE_ID"
}
]
}'The grace period must be shorter than the billing interval — for example, less than 28 days for a monthly subscription.
Collection method
collection_method controls how invoices generated by a subscription are settled.
| Value | Behavior |
|---|---|
CHARGE_AUTOMATICALLY (default) | The billing engine auto-charges the subscription's default_payment_method whenever an invoice is generated. |
REQUEST_PAYMENT | The invoice is finalized and issued to the customer, but not auto-charged. The customer settles it out-of-band — typically via the invoice's hosted invoice URL. |
If collection_method is unset, it defaults to CHARGE_AUTOMATICALLY.
CHARGE_AUTOMATICALLY
CHARGE_AUTOMATICALLYdefault_payment_methodis required at creation.- At each billing event the engine generates and finalizes an invoice, then immediately charges the saved payment method.
- A failed charge moves the subscription to
PAST_DUE;grace_period_dayscontrols how long it remains inPAST_DUEbefore transitioning toUNPAID.
REQUEST_PAYMENT
REQUEST_PAYMENTUse this mode when you want SynaptoPay to manage the billing schedule and invoice generation, but you (or the customer) want to handle payment manually.
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/subscriptions \
-H "Authorization: Api-Key $API_KEY" \
-H "Idempotency-Key: YOUR_UNIQUE_KEY" \
-H "Content-Type: application/json" \
-d '{
"subscription": {
"customer": "accounts/acct_YOUR_ACCOUNT/customers/cus_CUSTOMER_ID",
"collection_method": "REQUEST_PAYMENT",
"grace_period_days": 7
},
"items": [
{
"display_name": "Pro Plan",
"price": "accounts/acct_YOUR_ACCOUNT/prices/pr_PRICE_ID"
}
]
}'Key differences from CHARGE_AUTOMATICALLY:
default_payment_methodis optional. If supplied, it is stored on the subscription for later use (e.g. when flipping back toCHARGE_AUTOMATICALLY, where it becomes required) but is never auto-charged.grace_period_daysmust be at least 1. It defines the customer's window to pay each invoice.- The subscription is
ACTIVEas soon as the invoice is finalized — service is claimed at issuance, not at payment. Each generated invoice carries adue_timeofcreate_time + grace_period_days. - No
PAST_DUEdetour. If an invoice is still unpaid when itsdue_timepasses, the subscription transitionsACTIVE → UNPAIDdirectly. To recover, pay the outstanding invoice and call https://docs.synaptopay.com/reference/publicapi_reactivatesubscription. - Renewal still happens automatically. At the end of each billing period the engine generates the next invoice and issues it; the customer has another
grace_period_dayswindow to pay. (If the previous invoice is unpaid at the deadline, the subscription will already beUNPAIDand renewal will not fire — reactivate first.) - Voiding an open invoice does not extend the deadline. The deadline is anchored to the invoice's
due_timeregardless ofVOID/UNCOLLECTIBLEstatus. To stop deadline tracking on a customer in difficulty, cancel the subscription.
grace_period_daysis snapshotted at invoice finalization as the per-invoicedue_time. Updatinggrace_period_dayson the subscription affects only future invoices — the deadline on any in-flight invoice is frozen.
Invoices carry their own collection_method
collection_methodWhen the billing engine creates a subscription-backed invoice, it stamps the invoice's collection_method from the subscription's value at the time of finalization. This snapshot is what drives whether the engine attempts to auto-charge it and how its due_time is computed — flipping collection_method on the subscription later does not retroactively change the handling of an already-finalized invoice.
For invoices created directly via https://docs.synaptopay.com/reference/publicapi_createinvoice (not subscription-backed), collection_method is informational only — a passthrough field for your own bookkeeping.
Switching collection method
collection_method is mutable on an existing subscription via https://docs.synaptopay.com/reference/publicapi_updatesubscription. Changing collection_method affects how future invoices are generated. Invoices already issued retain their original collection_method and complete per their existing mode.
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/subscriptions/sub_SUBSCRIPTION_ID \
-H "Authorization: Api-Key $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"subscription": {
"name": "accounts/acct_YOUR_ACCOUNT/subscriptions/sub_SUBSCRIPTION_ID",
"collection_method": "REQUEST_PAYMENT"
},
"update_mask": "collection_method"
}'Constraints when switching:
CHARGE_AUTOMATICALLY → REQUEST_PAYMENT: allowed at any time except while inPAST_DUE(settle the outstanding invoice first). You may includedefault_payment_methodin the sameupdate_maskto clear it.REQUEST_PAYMENT → CHARGE_AUTOMATICALLY: the subscription must have adefault_payment_method. Set it in the sameupdate_maskif it isn't already populated.- Either direction: rejected if the latest invoice is in a non-final state.
OPENinvoices must be settled (pay or void) first.DRAFTis rare and only appears if the billing engine is mid-retry; wait a few seconds and try again.
Reactivation
Reactivation is only available for UNPAID subscriptions. https://docs.synaptopay.com/reference/publicapi_reactivatesubscription honors the subscription's current collection_method:
CHARGE_AUTOMATICALLY: a fresh invoice is finalized and immediately auto-charged.default_payment_methodis required (either already on the subscription or supplied in the request).REQUEST_PAYMENT: a fresh invoice is finalized and issued; the customer settles it. Supplying a payment method is optional and, if provided, is stored on the subscription for future use.
Applying tax rates
Set default_tax_rates on the subscription to apply tax rates to all invoice lines. If a specific subscription item has its own tax_rates, those take precedence over the subscription-level defaults.
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/subscriptions \
-H "Authorization: Api-Key $API_KEY" \
-H "Idempotency-Key: YOUR_UNIQUE_KEY" \
-H "Content-Type: application/json" \
-d '{
"subscription": {
"customer": "accounts/acct_YOUR_ACCOUNT/customers/cus_CUSTOMER_ID",
"default_payment_method": "accounts/acct_YOUR_ACCOUNT/payment-methods/pm_PAYMENT_METHOD_ID",
"default_tax_rates": [
"accounts/acct_YOUR_ACCOUNT/tax-rates/txr_DEFAULT_TAX_RATE_ID"
]
},
"items": [
{
"display_name": "Pro Plan",
"price": "accounts/acct_YOUR_ACCOUNT/prices/pr_PLAN_PRICE_ID"
},
{
"display_name": "Extra seats",
"price": "accounts/acct_YOUR_ACCOUNT/prices/pr_SEAT_PRICE_ID",
"quantity": 5,
"tax_rates": [
"accounts/acct_YOUR_ACCOUNT/tax-rates/txr_ITEM_TAX_RATE_ID"
]
}
]
}'In this example, the "Pro Plan" item inherits txr_DEFAULT_TAX_RATE_ID from the subscription, while the "Extra seats" item uses its own txr_ITEM_TAX_RATE_ID instead.
Managing subscription items
You can add, update, and remove items on an active subscription. Changes take effect on the next billing cycle.
Newly added items will not have a current_period until the next invoice is generated and paid. An item without a current_period has been added mid-cycle and has not yet been billed.
Adding an item
The new item's price must have the same billing interval and currency as existing items.
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/subscriptions/sub_SUBSCRIPTION_ID/subscription-items \
-H "Authorization: Api-Key $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"item": {
"display_name": "Add-on storage",
"price": "accounts/acct_YOUR_ACCOUNT/prices/pr_ADDON_PRICE_ID",
"quantity": 2
}
}'Updating an item
You can update an item's display_name, price, quantity, tax_rates, and metadata. Use the update_mask field to specify which fields to change.
When updating the price on a single item, the new price must have the same billing interval and currency as other items on the subscription. If the subscription has only one item, this constraint does not apply — you can change to any recurring price.
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/subscriptions/sub_SUBSCRIPTION_ID/subscription-items/si_ITEM_ID \
-H "Authorization: Api-Key $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"subscription_item": {
"name": "accounts/acct_YOUR_ACCOUNT/subscriptions/sub_SUBSCRIPTION_ID/subscription-items/si_ITEM_ID",
"quantity": 10
},
"update_mask": "quantity"
}'Changing billing frequency
All items on a subscription must share the same billing interval and currency. This means you cannot change a single item's price to a different interval when other items still use the old one.
To change the billing frequency (for example, moving a customer from monthly to yearly), use the bulk update endpoint to update all items at once. When every item is included in the update, the interval and currency constraints are validated against the new prices rather than the existing ones.
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/subscriptions/sub_SUBSCRIPTION_ID/subscription-items:bulk-update \
-H "Authorization: Api-Key $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"updates": [
{
"name": "accounts/acct_YOUR_ACCOUNT/subscriptions/sub_SUBSCRIPTION_ID/subscription-items/si_ITEM_1",
"price": "accounts/acct_YOUR_ACCOUNT/prices/pr_NEW_PRICE_ID_1"
},
{
"name": "accounts/acct_YOUR_ACCOUNT/subscriptions/sub_SUBSCRIPTION_ID/subscription-items/si_ITEM_2",
"price": "accounts/acct_YOUR_ACCOUNT/prices/pr_NEW_PRICE_ID_2"
}
]
}'Removing an item
A subscription must always have at least one item. The last remaining item cannot be deleted.
curl -X DELETE https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/subscriptions/sub_SUBSCRIPTION_ID/subscription-items/si_ITEM_ID \
-H "Authorization: Api-Key $API_KEY"Canceling a subscription
Canceling an ACTIVE subscription schedules cancellation at the end of the current billing period. The subscription moves to CANCELLATION_REQUESTED immediately, and the customer retains access until the period ends, at which point it transitions to CANCELED.
For PAST_DUE, UNPAID, or TRIALING subscriptions, cancellation takes effect immediately.
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/subscriptions/sub_SUBSCRIPTION_ID:cancel \
-H "Authorization: Api-Key $API_KEY"Response
{
"name": "accounts/acct_YOUR_ACCOUNT/subscriptions/sub_SUBSCRIPTION_ID",
"state": "CANCELLATION_REQUESTED",
"cancel_time": "2026-03-19T08:31:10Z",
"next_action_time": "2026-03-19T08:31:10Z"
}The cancel_time indicates when the subscription will transition to CANCELED.
Reactivating an unpaid subscription
If a subscription reaches UNPAID due to payment failures, you can reactivate it. This immediately attempts a new payment. You can optionally provide a new payment method.
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/subscriptions/sub_SUBSCRIPTION_ID:reactivate \
-H "Authorization: Api-Key $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"default_payment_method": "accounts/acct_YOUR_ACCOUNT/payment-methods/pm_NEW_PAYMENT_METHOD_ID"
}'If payment succeeds, the subscription returns to ACTIVE.
Updating a subscription
You can update the default_payment_method, default_tax_rates, grace_period_days, and metadata on a subscription. Updates are only allowed when the subscription is in TRIALING, ACTIVE, PAST_DUE, or UNPAID.
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/subscriptions/sub_SUBSCRIPTION_ID \
-H "Authorization: Api-Key $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"subscription": {
"name": "accounts/acct_YOUR_ACCOUNT/subscriptions/sub_SUBSCRIPTION_ID",
"default_payment_method": "accounts/acct_YOUR_ACCOUNT/payment-methods/pm_NEW_PAYMENT_METHOD_ID"
},
"update_mask": "default_payment_method"
}'Listing subscriptions
You can filter subscriptions by state, customer, and create_time range.
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/subscriptions:list \
-H "Authorization: Api-Key $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"filter": {
"state": "ACTIVE",
"customer": "accounts/acct_YOUR_ACCOUNT/customers/cus_CUSTOMER_ID"
}
}'Subscription states
| State | Description |
|---|---|
INCOMPLETE | Created but not yet activated — payment is being attempted. Expires after 23 hours |
INCOMPLETE_EXPIRED | Activation was not completed in time (terminal state) |
TRIALING | In a free trial period — converts automatically at trial_end |
ACTIVE | Healthy and billing on schedule |
PAST_DUE | A renewal payment failed — paying the invoice within the grace period restores to ACTIVE |
UNPAID | Grace period expired without payment — must use ReactivateSubscription to recover |
CANCELLATION_REQUESTED | Cancellation scheduled — remains active until cancel_time |
CANCELED | Subscription has ended — no further billing (terminal state) |
Key subscription fields
| Field | Description |
|---|---|
customer | Resource name of the customer (set at creation, cannot be changed) |
default_payment_method | Payment method used for automatic charges |
trial_end | End of the free trial period (set at creation, cannot be changed) |
billing_cycle_anchor | Reference timestamp for computing billing periods |
items | Resource names of the subscription's items |
latest_invoice | Most recently generated invoice |
next_action_time | When the next renewal or scheduled action occurs |
cancel_time | When a cancellation-requested subscription will be canceled |
grace_period_days | Days to wait after a payment failure before transitioning to UNPAID |
metadata | Key-value pairs for your own use (max 50 keys, key max 40 chars, value max 500 chars) |
Key subscription item fields
| Field | Description |
|---|---|
display_name | Label shown on invoices |
price | Resource name of the recurring price this item bills against |
quantity | Units billed per period (defaults to 1) |
tax_rates | Item-level tax rates — overrides the subscription's default_tax_rates when set |
current_period | The current billing period window (start and end timestamps) |
metadata | Key-value pairs for your own use (max 50 keys, key max 40 chars, value max 500 chars) |
Webhooks
Subscription state changes generate events that are sent to your webhook endpoints:
| Event type | Description |
|---|---|
subscription.active | Subscription became active (initial activation or recovery from past due) |
subscription.past_due | A payment failed and the subscription entered the grace period |
subscription.unpaid | Grace period expired without successful payment |
subscription.canceled | Subscription was canceled |
subscription.incomplete_expired | Incomplete subscription expired without activation |
See Webhooks for setup instructions.
Updated 27 days ago