
Subscription (Recurring)
Immediately cancel a subscription plan. No further cycle charges will be attempted.
Cancels a subscription plan immediately. After cancellation:
cancelled (a terminal state).Cancellation is final — to resume billing you must create a new plan.
Cancellation paths: Plans in pending_card_linking or pending_payment (not yet started) are cancelled outright via the subscription plan service. Plans in active, paused, or suspended go through the lifecycle service with an immediate stop. Both paths produce the same terminal state.
| Method | Path | Format | Authentication |
|---|---|---|---|
| POST | /api/v2.0/recurring/plans/cancel/{id} | json | OAuth 2.0 with Access Token |
| Parameter | Type | Mandatory | Description | Example |
|---|---|---|---|---|
| id | String | Required | Subscription Plan ID (26-char ULID) | 01JAB3CD4E5F6G7H8J9K0M1N2 |
| Field | Value | Type | Mandatory | Description | Example |
|---|---|---|---|---|---|
| Authorization | Bearer {access_token} | Alphanumeric | Mandatory | Bearer token obtained from the access token endpoint. | Bearer eyJ0eXAiOiJKV1{…} |
| X-PARTNER-ID | Alphanumeric | Mandatory | Your API Key from the merchant dashboard. | pk_live_abc123def456 | |
| Content-Type | application/json | Alphabetic | Mandatory | Specifies JSON as the request body format. | application/json |
| Parameter | Type | Mandatory | Validation | Description | Example |
|---|---|---|---|---|---|
| reason | String | Optional | - | Human-readable cancellation reason for audit. Defaults to merchant_api_cancel. | customer_request |
{
"reason": "customer_request"
}
An empty body is also valid — the default reason is used.
cURL Example:
curl -X POST "https://your-domain.com/api/v2.0/recurring/plans/cancel/01JAB3CD4E5F6G7H8J9K0M1N2" \
-H "X-PARTNER-ID: {api_key}" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc..." \
-H "Content-Type: application/json" \
-d '{ "reason": "customer_request" }'
Same shape as Show Plan, with status: "cancelled".
Success: Plan cancelled. No further charges will be attempted.
{
"response_code": "SP000",
"response_message": "Successfully",
"data": {
"id": "01JAB3CD4E5F6G7H8J9K0M1N2",
"name": "Premium Monthly",
"amount": "150000",
"currency": "IDR",
"status": "cancelled",
"schedule": {
"interval": 1,
"interval_unit": "month",
"current_interval": 2,
"total_interval": 12,
"start_time": "2026-05-01T00:00:00+07:00",
"previous_payment_at": "2026-06-01T00:00:15+07:00",
"next_payment_at": null
},
"payment_type": "credit_card",
"retry_policy": {
"max_attempts": 3,
"interval_days": 3,
"failed_payment_action": "continue_plan"
},
"subscription_id": "PLAN-20260420-001",
"merchant_reff_no": "SUB-CUST-ACME-001",
"payment_link_url": null,
"parent_plan_id": null,
"created_from": null
}
}
Error: Plan is already in a terminal state.
{
"response_code": "SP101",
"response_message": "Plan already cancelled.",
"data": {}
}
The same response code (SP101) is returned when the plan is already completed, with the message reflecting the actual status.
{
"response_code": "SP100",
"response_message": "Subscription Plan Not Found",
"data": {}
}
schedule.next_payment_at becomes null on cancellation.reason field is stored on the plan’s audit log and is surfaced on the merchant dashboard.PATCH returns SP102). If you intend to change the plan, PATCH first, then cancel if needed.