Manage Subscriptions
A subscription binds one customer to one plan and is the object that actually gets billed. It issues an invoice every billing cycle, tracks its own status through that cycle, and posts a webhook every time that status changes.
This page covers creating a subscription, the status lifecycle end to end, what happens each billing cycle, and how to pause, resume, cancel or terminate one.
A plan to subscribe to, and a customer to subscribe. If you want cycles billed automatically, you also need a saved card token for that customer before the first invoice falls due.
The whole lifecycle, in motion
One subscription, from creation through a trial, a paid cycle, a missed cycle, and the end of the plan.

Create a subscription
| Field | Required | What it does |
|---|---|---|
plan | Yes | The plan id this subscription is attached to. |
customer | Yes | The customer UUID being subscribed. |
start_date | Yes | Start of the trial, or of the first billing cycle when there is no trial. |
charge_automatically | No | true bills the saved card at each due date. Requires primary_card_token. Defaults to false. |
primary_card_token | Conditional | The saved card token used for automatic charges. Required when charge_automatically is true. |
customization | No | Per-subscription overrides of the plan's terms. See below. |
webhook_url | No | Where MoneyHash POSTs subscription status updates. |
custom_fields | No | Key/value pairs stored with the subscription and filterable on list. |
flow_id | No | The payment flow used for this subscription's invoice payments. |
curl -X POST https://web.moneyhash.io/api/v1.4/subscriptions/ \
-H "x-api-key: <YOUR_ACCOUNT_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"plan": "<YOUR_PLAN_ID>",
"customer": "<YOUR_CUSTOMER_ID>",
"start_date": "2026-10-01",
"charge_automatically": true,
"primary_card_token": "<YOUR_CARD_TOKEN_ID>",
"webhook_url": "https://your-server.com/webhooks/subscriptions",
"custom_fields": { "tier": "basic" }
}'The response is the full subscription object. The fields worth knowing:
{
"status": { "code": 200, "message": "success", "errors": [] },
"data": {
"id": "<YOUR_SUBSCRIPTION_ID>",
"account": { "id": "<YOUR_ACCOUNT_ID>", "name": "Demo Account" },
"plan": { "id": "<YOUR_PLAN_ID>", "name": "Basic Monthly" },
"customer": { "id": "<YOUR_CUSTOMER_ID>", "name": "Test Customer" },
"current_status": { "id": "...", "value": "NEW", "created": "..." },
"statuses": [ { "id": "...", "value": "NEW", "created": "..." } ],
"start_date": "2026-10-01",
"end_date": null,
"charge_automatically": true,
"remaining_recurring_cycles": null,
"remaining_discount_cycles": null,
"configuration": {
"amount": 49,
"one_time_fee": null,
"trial_period": null,
"recurrency": 1,
"recurring_cycles": null,
"discount_amount": null,
"discount_percentage": null,
"discount_cycles": null
},
"due_invoices": [],
"next_recurring_cycle_invoice": {},
"all_invoices": [],
"transactions": [],
"scheduled_updates": [],
"flow": null,
"webhook_url": "https://your-server.com/webhooks/subscriptions",
"custom_fields": { "tier": "basic" },
"is_live": false,
"created": "2026-09-20T00:01:41.500439Z"
},
"count": 1, "next": null, "previous": null
}configuration is the effective configuration for this subscription: the plan's terms with any customization already applied. Read it rather than the plan when you want to know what this customer is actually being charged.
statuses is the full history, current_status the head of it. scheduled_updates holds changes you have queued for the next cycle - see Update Subscriptions.
Automatic billing, or a link each cycle
charge_automatically is the single most consequential field on a subscription. It decides whether MoneyHash collects each cycle for you, or whether you have to.
charge_automatically: true - at each due date MoneyHash charges primary_card_token as a merchant-initiated transaction. The invoice is paid without the customer doing anything, so the subscription cycles inside ACTIVE and never drops to PAST_DUE.
charge_automatically: false - nothing is collected for you. Each cycle issues an invoice carrying a payment_intent_url, and it is on you to get that link to the customer. Until they pay it, the due date passes and the subscription moves to PAST_DUE.
"due_invoices": [
{
"id": "<INVOICE_ID>",
"subscription_id": "<YOUR_SUBSCRIPTION_ID>",
"status": { "id": "...", "value": "DUE", "created": "..." },
"amount": 49,
"currency": "SAR",
"due_date": "2026-11-01",
"payment_intent_url": "https://embed.moneyhash.io/embed/payment/<INTENT_ID>",
"payment_data": { "intent_id": "<INTENT_ID>", "intent": { "...": "..." } }
}
]You can switch a manual subscription onto automatic billing later by storing a card token against it. See Update Subscriptions.
Customizing one subscription
customization overrides the plan's terms for this subscription only. The plan is untouched, and every other subscriber on it is unaffected. Use it for a negotiated price, a longer trial for one account, or a one-off discount.
| Override | Effect |
|---|---|
amount | Charge this customer a different amount per cycle. |
one_time_fee | Add, change or remove the upfront fee. |
trial_period | Give this customer a different trial length. |
recurrency | Change the interval between cycles. |
recurring_cycles | Change how many cycles this subscription runs for. |
discount_amount | A fixed discount, instead of the plan's. |
discount_percentage | A percentage discount, instead of the plan's. |
discount_cycles | How many cycles the discount applies to. |
{
"plan": "<YOUR_PLAN_ID>",
"customer": "<YOUR_CUSTOMER_ID>",
"start_date": "2026-10-01",
"customization": {
"amount": "39.00",
"trial_period": 30,
"discount_percentage": "10.00",
"discount_cycles": 3
}
}The subscription lifecycle
A subscription starts at NEW and moves through the states below as time passes and invoices are paid or missed.
| Status | What it means | Moves to |
|---|---|---|
NEW | The default status of a new subscription, before its start_date. | TRIAL if the plan has a trial period. INCOMPLETE if it does not, as it awaits the first payment. |
TRIAL | The subscription is in its trial period. | INCOMPLETE if the trial ends and cycle one is unpaid. ACTIVE if the trial ends and cycle one is already paid. PENDING_CANCELLATION if cancelled during the trial. TERMINATED on termination. |
INCOMPLETE | The first billing cycle has not been paid. | ACTIVE once cycle one is paid. ENDED if the final recurring cycle completes. TERMINATED on termination. |
ACTIVE | The customer is paying correctly. This is the steady state. | ACTIVE again each time the next invoice is paid on or before the due date. PAST_DUE if a due date passes unpaid. PAUSED on pause. ENDED on the last recurring cycle. PENDING_CANCELLATION on cancellation. TERMINATED on termination. |
PAST_DUE | An invoice is overdue. Access is still technically live, so this is your cue to chase or restrict. | ACTIVE once the due invoice is paid. PAST_DUE again if another due date passes. ENDED on the last cycle. PENDING_CANCELLATION on cancellation. TERMINATED on termination. |
PAUSED | Billing is temporarily suspended. No charges happen until it is resumed. | ACTIVE on resume. ENDED on the last cycle. TERMINATED on termination. |
ENDED | The final recurring cycle completed. | Final. |
PENDING_CANCELLATION | Cancellation is requested. The customer keeps access until the next billing cycle date. | CANCELLED when that date arrives. |
CANCELLED | The cancellation took effect and access is revoked. | Final. |
TERMINATED | Access revoked at the moment of the request. Reachable from any status except a cancellation already in progress. | Final. |
Ending a subscription
Three ways out, and they differ only in when the customer loses access.
- Ended - let it run out. When the subscription reaches its last recurring cycle it ends on its own. A plan with an empty
recurring_cyclesnever gets here. - Cancelled - cancel at any point in the cycle. The customer keeps access until the current billing cycle date is reached. The status sits at
PENDING_CANCELLATIONin the meantime, then flips toCANCELLED. - Terminated - stop now. Billing ceases and access is revoked from the moment of the request, whatever the current status.
Day-to-day operations
Beyond creating one, four operations manage a subscription's rhythm:
| Operation | What it does |
|---|---|
| Pause | Temporarily suspends the billing cycle without cancelling. The subscription moves to PAUSED. |
| Resume | Reinstates a paused subscription so billing continues. Back to ACTIVE. |
| Cancel | Concludes the subscription. Access persists until the end of the ongoing billing cycle. |
| Terminate | Immediately ceases billing and revokes access. |
For changing what a subscription costs or which plan it is on, see Update Subscriptions.
Retrying a failed automatic charge
When charge_automatically is true and the card is declined, you can re-attempt the outstanding invoices without waiting for the next cycle.
This only works on subscriptions with charge_automatically: true. A manual subscription has no card to retry against, so send the customer the invoice's payment_intent_url instead.
Subscription invoices
Every billing cycle creates one invoice. It carries its own status, independent of the subscription's.
| Status | What it means | Moves to |
|---|---|---|
NEW | The default status of a new invoice. | OPEN if the due date has not passed. DUE if it has. |
OPEN | The due date is still ahead and the customer has not paid. | DUE when the due date passes. PAID when paid. CANCELLED if cancelled. |
DUE | The due date has passed and the invoice is unpaid. | PAID when paid. CANCELLED if cancelled. |
PAID | The invoice has been paid. | Final. |
CANCELLED | The invoice was cancelled. | Final. |
The subscription object exposes invoices in three places: due_invoices (outstanding), next_recurring_cycle_invoice (the one coming), and all_invoices (the history).
Reading subscriptions back
The list endpoint pages with limit and offset, and filters on the custom fields you stored at creation:
curl "https://web.moneyhash.io/api/v1.4/subscriptions/?custom_fields_name=tier&custom_fields_value=basic&limit=50" \
-H "x-api-key: <YOUR_ACCOUNT_API_KEY>"Webhooks
Every subscription status change posts a webhook. The payload carries "type": "subscription" and the event type is subscription.<STATUS> - for example subscription.ACTIVE or subscription.PAST_DUE.
Set the destination per subscription with webhook_url at creation. The body is the same subscription object returned by the API, so current_status.value is what you switch on, and statuses gives you the whole history if you missed a delivery.
Nothing about a renewal, a missed payment, or a cancellation taking effect is returned synchronously - those all happen on a schedule, long after your API call returned. Configure webhook_url and handle every status in the table above, or your side of the integration will drift out of sync with MoneyHash.
Testing the lifecycle without waiting
Waiting a real month to see a renewal is not a test strategy. MoneyHash ships pre-configured scenarios you can drive from the Simulate subscription endpoint or from the Sandbox.
Replace the simulation_subscription parameter with the subscription id you want to simulate, and send a command in the body:
command | What it simulates |
|---|---|
jump_to_the_next_cycle_start_date | Advances the subscription to the next cycle's start date, so you can exercise renewal without waiting for the real due date. |
pay_all_issued_invoices | Marks every invoice issued so far as paid, simulating a customer who is fully up to date. |
Between them you can walk a subscription through INCOMPLETE, ACTIVE, PAST_DUE and ENDED in minutes, and confirm your webhook handler reacts correctly to each one.
Where to next
- Manage Plans - the offer a subscription is attached to.
- Update Subscriptions - change plan, amount, discount, cycles or card mid-flight.
- Customers - the entity a subscription belongs to.
- Tokenize Cards - how to get the
primary_card_tokenthat automatic billing needs.
Updated 23 days ago