Skip to main content
Subscription Merchant API v2.0 enables recurring billing via credit card. Create a plan, direct the customer through a one-time card linking flow, and Singapay automatically charges the saved card on every cycle — no customer action required after the initial setup.
This is a Money In operation. Each cycle charge transfers funds from the customer’s tokenized card to your Singapay Payment Gateway account.

API Version

v2.0

Base Path

/api/v2.0/recurring/plans

Auth

Bearer Token (JWT)

Channel

Credit Card (recurring)

Subscription lifecycle


Plan status lifecycle

StatusDescription
pending_card_linkingPlan created, awaiting customer to complete card linking via payment_link_url.
pending_paymentCard linked, awaiting scheduled start (schedule.start_time is in the future).
activeSubscription running — cycles auto-charged on schedule.
pausedMerchant-initiated pause from the dashboard. No charges while paused.
suspendedAuto-suspended after retries exhausted (when failed_payment_action = stop_plan).
cancelledCancelled by merchant or auto-cancelled on hard failure during initial card linking. Terminal state.
completedAll scheduled cycles charged (only for plans with total_interval set).

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 immediately.There is no saved card to retry against, so retrying would never succeed.
When an auto-cancel occurs:
  • The bill for that cycle moves to cancelled
  • The plan transitions from pending_payment / pending_card_linking directly to cancelled
  • The active payment link is expired — the customer cannot complete a charge against the cancelled plan
  • A subscription.plan.status_changed webhook fires with plan.metadata.cancellation_reason = initial_linking_failed
Plans created with charge_immediately = false are not affected by this rule. To re-attempt after a hard failure, create a new plan.

Amount vs. items

A plan is either amount-only or itemized — never both. This is enforced on both Create and Update.

Amount-only

Send amount. The entire recurring charge is a single line item — simple and straightforward.

Itemized

Send items[]. The per-cycle charge is the sum of quantity × unit_price across all items.
Sending both amount and items in the same request returns a 422 validation error. On Update, the patch shape must match the plan’s existing type — mixing returns 409 with response code SP102.

Retry policy

When a cycle charge fails, Singapay automatically retries based on the plan’s configured policy.
FieldType / RangeDescription
retry_policy.max_attemptsinteger, 1–5Automatic retry attempts after the initial charge fails.
retry_policy.interval_daysinteger, 1–7Days to wait between consecutive retry attempts.
retry_policy.failed_payment_actioncontinue_plan | stop_planBehavior after retries exhausted. continue_plan keeps the subscription running on the next cycle; stop_plan suspends it.

Error reference

CodeDescription
200Success (show, update, cancel)
201Created (create plan)
400Bad Request — validation or business rule error
401Unauthorized — invalid or missing token
404Plan or account not found
409Plan cannot be updated or already cancelled
422Validation error — missing field, amount/items conflict, etc.
500Internal server error
CodeDescriptionHTTP Status
SP000Success200 / 201
SP002General failure (internal error)500
SP020Merchant account not found404
SP100Subscription plan not found404
SP101Subscription plan already cancelled409
SP102Subscription plan cannot be updated in current state409

Important notes

Subscriptions run exclusively on credit card recurring. The initial card linking uses one-time 3DS; all subsequent cycle charges use the tokenized card silently — no customer action needed.
The per-cycle charge must clear the card channel minimum. Plans below this floor are rejected on both Create and Update with a 422 validation error.
subscription_id is a globally unique merchant-supplied identifier across all plans for the merchant. merchant_reff_no is an optional, non-unique label for grouping and reporting — echoed back on every cycle webhook.
A PATCH that changes amount or items triggers the full upgrade flow — the existing plan is closed and a new plan is created with parent_plan_id pointing at it, plus an optional proration and a fresh payment link.A PATCH that only changes name, merchant_reff_no, or metadata patches the plan in place without creating a new plan.
All Merchant API routes require IP whitelisting. Ensure your server IP is registered for the merchant account before making any API calls.

Available endpoints

Create Recurring Plan

Register a new plan. Returns a payment_link_url for the customer’s one-time card linking.

Get Recurring Plan

Fetch current plan state — status, schedule progress, next payment date, retry policy, and lineage.

Update or Upgrade Plan

Patch cosmetic fields in place, or change amount / items to trigger the upgrade flow with a new plan and optional proration.

Cancel Plan

Stop a plan immediately. Active plans go through the lifecycle service; pending plans are cancelled outright.
All endpoints require authentication. See Authentication.

Changelog

DateDescription
2026-04-20Initial documentation — Subscription Merchant API v2.0 (create, show, update/upgrade, cancel) + Subscription Cycle webhook.