Subscription (Recurring)
Subscription Merchant API v2.0
Subscription Merchant API v2.0 enables merchants to create and manage recurring billing plans backed by tokenized card payments. Create a plan, direct the customer through a one-time card linking flow, and SingaPay automatically charges the saved card on every cycle — with configurable retries and webhook notifications on each attempt.
Note: This is a Money In operation — each cycle charge transfers funds from the customer's saved card to your SingaPay Payment Gateway account.
API Overview
| API Version | v2.0 |
| Base Path | /api/v2.0/recurring/plans |
| Authentication | Bearer Token (JWT, OAuth 2.0 client credentials) |
| Content-Type | application/json |
| Supported Channel | Credit Card (recurring) |
Authentication
Merchants must obtain a JWT access token before calling Subscription endpoints.
Obtaining Access Token
- Endpoint:
POST /api/v1.0/access-token/b2borPOST /api/v1.1/access-token/b2b - Headers:
Authorization: Basic {base64(client_id:client_secret)},X-PARTNER-ID: {api_key},Content-Type: application/x-www-form-urlencodedorapplication/json - Body:
grant_type=client_credentials
Using Access Token: Include Authorization: Bearer {access_token} in all Subscription API requests.
IP Whitelist: Merchant API routes require IP whitelisting. Ensure your server IP is registered for the merchant account.
Base URL & Paths
| Environment | Base URL |
|---|---|
| Production | https://{your-domain}/api/v2.0/recurring/plans |
| Staging/Local | https://{your-domain}/api/v2.0/recurring/plans |
Subscription Lifecycle
Plan Status Lifecycle
- pending_card_linkingPlan created, awaiting customer to complete the initial card linking via
payment_link_url. - pending_paymentCard linked, awaiting scheduled start (used when
schedule.start_timeis in the future). - activeSubscription running. Cycles are auto-charged on schedule.
- pausedMerchant-initiated pause from the dashboard. No charges.
- suspendedAutomatically suspended after retries are exhausted (when
failed_payment_action = stop_plan). - cancelledCancelled by the merchant (via the Cancel API or dashboard) or auto-cancelled by the system when the initial card linking on a
charge_immediately = trueplan is rejected by the issuer / acquirer. Terminal state. - completedAll scheduled cycles have been charged (only applies to plans with
total_intervalset).
Initial Card Linking — Auto-Cancel on Hard Failure
When a plan is created with charge_immediately = true and the customer's first card-linking attempt is rejected by the issuer or acquirer, the platform auto-cancels the plan instead of queueing retries — there is no saved card to retry against, so retrying would never succeed.
- • The bill for that cycle is moved to
cancelled. - • The plan transitions from
pending_payment/pending_card_linkingdirectly tocancelled. - • The active payment link is expired so the customer cannot complete a charge against the now-cancelled plan.
- • A
subscription.plan.status_changedwebhook is fired withprevious_statusreflecting the prior pre-active state andplan.metadata.cancellation_reasonset toinitial_linking_failed.
Plans created with charge_immediately = false are not affected by this rule; they follow the regular scheduled-link flow with its own grace period. To re-attempt after a hard failure, create a new plan.
Path Parameters
- •
{id}: Subscription Plan ID (string, 26-char ULID returned at creation, e.g.01JAB3CD4E5F6G7H8J9K0M1N2)
Available Operations
- • Create Plan: Register a new recurring plan. Returns a
payment_link_urlfor the customer to complete one-time card linking. - • Show Plan: Fetch the current state of a plan (status, schedule progress, next payment date, retry policy, lineage).
- • Update / Upgrade Plan: Patch cosmetic fields in place, or change the recurring amount / items — which closes the existing plan and creates a new one with lineage tracked via
parent_plan_id, optional proration, and a fresh payment link. - • Cancel Plan: Stop a plan immediately. Pending plans are cancelled outright; active plans go through the lifecycle service with an immediate stop.
Amount vs. Items
A plan is either amount-only or itemized — never both. The Create and Update requests enforce this via validation.
- • Amount-only: Send
amount. The entire recurring charge is a single line. - • Itemized: Send
items[]. The per-cycle charge is the sum ofquantity × unit_priceacross items. - • Sending both
amountanditemsin the same request returns a422validation error. - • On Update, the patch shape must match the plan's existing type. Sending
itemsto an amount-only plan (or vice versa) returns a409with response codeSP102.
Retry Policy
When a cycle charge fails, SingaPay automatically retries based on the plan's configured policy. The policy is symmetric across the Create request body, API responses, and webhook payloads.
| Field | Type / Range | Description |
|---|---|---|
retry_policy.max_attempts | integer, 1–5 | Automatic retry attempts after the initial charge fails. |
retry_policy.interval_days | integer, 1–7 | Days to wait between consecutive retry attempts. |
retry_policy.failed_payment_action | continue_plan | stop_plan | Behavior after retries are exhausted. continue_plan keeps the subscription running on the next cycle; stop_plan suspends it. |
Error Responses
HTTP Status Codes
| Code | Description |
|---|---|
| 200 | Success (show, update, cancel) |
| 201 | Created (create plan) |
| 400 | Bad Request (validation or business rule error) |
| 401 | Unauthorized (invalid or missing token) |
| 404 | Plan or account not found |
| 409 | Plan cannot be updated / already cancelled |
| 422 | Validation error (missing field, amount/items conflict, etc.) |
| 500 | Internal server error |
Response Codes
| Code | Description | HTTP Status |
|---|---|---|
SP000 | Success | 200 / 201 |
SP002 | General Failure (internal error) | 500 |
SP020 | Merchant Account Not Found | 404 |
SP100 | Subscription Plan Not Found | 404 |
SP101 | Subscription Plan Already Cancelled | 409 |
SP102 | Subscription Plan Cannot Be Updated In Current State | 409 |
Important Notes
- • Card-Only: Subscriptions run on credit card recurring. The initial card linking uses one-time 3DS; subsequent cycle charges use the tokenized card (no customer action needed).
- • Minimum Amount: The per-cycle charge must clear the card channel minimum. Validation rejects plans below this floor on both Create and Update.
- • References:
subscription_idis a globally unique merchant-supplied plan identifier (unique across all plans for the merchant).merchant_reff_nois an optional, non-unique label for grouping and reporting — it is echoed back on every cycle webhook. - • Upgrade Semantics: A PATCH that changes
amountoritemstriggers the full upgrade flow: the existing plan is closed and a new plan is created withparent_plan_idpointing at it. A PATCH that only changesname/merchant_reff_no/metadatapatches the plan in place. - • IP Whitelist: Merchant API routes require IP whitelisting. Ensure your server IP is registered for the merchant account.
Changelog
| Date | Description |
|---|---|
| 2026-04-20 | Initial documentation — Subscription Merchant API v2.0 (create, show, update/upgrade, cancel) + Subscription Cycle webhook. |

