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:

Subscription lifecycle

  1. Create a recurring price that defines the amount, currency, and billing interval
  2. Create a subscription with at least one item referencing a recurring price
  3. An invoice is generated and the saved payment method is charged automatically
  4. On success, the subscription becomes ACTIVE and renews at the end of each billing period
  5. On payment failure, the subscription enters PAST_DUE and eventually UNPAID if not resolved

Step 1: Create a recurring price

API reference

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

IntervalExample
DAYEvery N days
WEEKEvery N weeks
MONTHEvery N months
YEAREvery 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

API reference

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:

  1. Creates an invoice with line items for each subscription item
  2. Finalizes the invoice
  3. 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.

ValueBehavior
CHARGE_AUTOMATICALLY (default)The billing engine auto-charges the subscription's default_payment_method whenever an invoice is generated.
REQUEST_PAYMENTThe 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

  • default_payment_method is 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_days controls how long it remains in PAST_DUE before transitioning to UNPAID.

REQUEST_PAYMENT

Use 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_method is optional. If supplied, it is stored on the subscription for later use (e.g. when flipping back to CHARGE_AUTOMATICALLY, where it becomes required) but is never auto-charged.
  • grace_period_days must be at least 1. It defines the customer's window to pay each invoice.
  • The subscription is ACTIVE as soon as the invoice is finalized — service is claimed at issuance, not at payment. Each generated invoice carries a due_time of create_time + grace_period_days.
  • No PAST_DUE detour. If an invoice is still unpaid when its due_time passes, the subscription transitions ACTIVE → UNPAID directly. 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_days window to pay. (If the previous invoice is unpaid at the deadline, the subscription will already be UNPAID and renewal will not fire — reactivate first.)
  • Voiding an open invoice does not extend the deadline. The deadline is anchored to the invoice's due_time regardless of VOID/UNCOLLECTIBLE status. To stop deadline tracking on a customer in difficulty, cancel the subscription.

grace_period_days is snapshotted at invoice finalization as the per-invoice due_time. Updating grace_period_days on the subscription affects only future invoices — the deadline on any in-flight invoice is frozen.

Invoices carry their own collection_method

When 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 in PAST_DUE (settle the outstanding invoice first). You may include default_payment_method in the same update_mask to clear it.
  • REQUEST_PAYMENT → CHARGE_AUTOMATICALLY: the subscription must have a default_payment_method. Set it in the same update_mask if it isn't already populated.
  • Either direction: rejected if the latest invoice is in a non-final state. OPEN invoices must be settled (pay or void) first. DRAFT is 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_method is 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

API reference

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

API reference

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.

API reference

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

API reference

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

API reference

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

API reference

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

API reference

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

API reference

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

StateDescription
INCOMPLETECreated but not yet activated — payment is being attempted. Expires after 23 hours
INCOMPLETE_EXPIREDActivation was not completed in time (terminal state)
TRIALINGIn a free trial period — converts automatically at trial_end
ACTIVEHealthy and billing on schedule
PAST_DUEA renewal payment failed — paying the invoice within the grace period restores to ACTIVE
UNPAIDGrace period expired without payment — must use ReactivateSubscription to recover
CANCELLATION_REQUESTEDCancellation scheduled — remains active until cancel_time
CANCELEDSubscription has ended — no further billing (terminal state)

Key subscription fields

FieldDescription
customerResource name of the customer (set at creation, cannot be changed)
default_payment_methodPayment method used for automatic charges
trial_endEnd of the free trial period (set at creation, cannot be changed)
billing_cycle_anchorReference timestamp for computing billing periods
itemsResource names of the subscription's items
latest_invoiceMost recently generated invoice
next_action_timeWhen the next renewal or scheduled action occurs
cancel_timeWhen a cancellation-requested subscription will be canceled
grace_period_daysDays to wait after a payment failure before transitioning to UNPAID
metadataKey-value pairs for your own use (max 50 keys, key max 40 chars, value max 500 chars)

Key subscription item fields

FieldDescription
display_nameLabel shown on invoices
priceResource name of the recurring price this item bills against
quantityUnits billed per period (defaults to 1)
tax_ratesItem-level tax rates — overrides the subscription's default_tax_rates when set
current_periodThe current billing period window (start and end timestamps)
metadataKey-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 typeDescription
subscription.activeSubscription became active (initial activation or recovery from past due)
subscription.past_dueA payment failed and the subscription entered the grace period
subscription.unpaidGrace period expired without successful payment
subscription.canceledSubscription was canceled
subscription.incomplete_expiredIncomplete subscription expired without activation

See Webhooks for setup instructions.