Subscriptions
List your subscriptions, create new ones (returning a checkout URL for the buyer to pay), cancel them, and set the next billing date.
List subscriptions
GET /api/v1/subscriptions/
Lists the seller's active subscriptions. The paginated envelope adds status counts.
Response envelope
| Field | Type | Description |
|---|---|---|
count | integer | Total rows |
next / previous | string | null | Page URLs |
total_subscriptions | integer | Same as count |
active_subscriptions | integer | Status-classified count |
due_subscriptions | integer | — |
inactive_subscriptions | integer | — |
results | array | See subscription item |
Subscription item (results[])
| Field | Type | Description |
|---|---|---|
subscription_id | string (UUID) | — |
status | string | One of pending_checkout, active, due, cancelled, pending_cancellation, inactive — see Statuses. |
is_active | boolean | status == "active" |
client | object | {phone, full_name, email, address, billing, shipping} — billing/shipping nullable |
product | object | {product_id, name, plan_name, pbp_id, label, price, currency} |
current_period_start | datetime | null | — |
current_period_end | datetime | null | = billing_cycle |
next_billing_cycle | datetime | null | The date the subscription bills next (same value as billing_cycle). Use this as the next-billing date. |
created_at | datetime | — |
Statuses
| Status | Meaning |
|---|---|
pending_checkout | Newly created; the buyer hasn't completed checkout/payment yet. |
active | Currently active subscription. |
due | next_billing_cycle has passed but no payment has been received yet. |
cancelled | Cancelled immediately (a user-prompted cancellation). |
pending_cancellation | Scheduled to cancel — it stays active until the end of the current billing cycle, then cancels. |
inactive | Automatically ended after the grace period elapsed without payment. |
Example response
{
"count": 85,
"next": "https://be.suqo.ai/api/v1/subscriptions?page=2",
"previous": null,
"total_subscriptions": 85,
"active_subscriptions": 60,
"due_subscriptions": 10,
"inactive_subscriptions": 15,
"results": [
{
"subscription_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "active",
"is_active": true,
"client": {
"phone": "9841000100",
"full_name": "Ram Shrestha",
"email": "[email protected]",
"address": "Kathmandu, Nepal",
"billing": {
"business_name": "ABC Pvt Ltd.",
"email": "[email protected]",
"address": "Kathmandu, Nepal",
"pan_vat": "984100010"
},
"shipping": {
"phone": "9841000100",
"full_name": "Ram Shrestha",
"email": "[email protected]",
"address": "Kathmandu, Nepal"
}
},
"product": {
"product_id": "205293a1-84f4-426e-8f8c-5ddb239e5d2f",
"name": "Pro Plan Bundle",
"plan_name": "Basic",
"pbp_id": "pbp_3n9k2x",
"label": "Monthly",
"price": "999.00",
"currency": "NPR"
},
"current_period_start": "2026-07-03T00:00:00Z",
"current_period_end": "2026-08-03T00:00:00Z",
"next_billing_cycle": "2026-08-03T00:00:00Z",
"created_at": "2026-07-03T10:15:00Z"
}
]
}
Create a subscription
POST /api/v1/subscriptions/
Creates a subscription in pending_checkout state (no billing cycle set yet) and returns a checkout URL for the buyer to pay.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
pbp_id | string | Yes | Plan billing period public id (from GET /products) |
return_url | string (URL) | Yes | Where the buyer returns after checkout |
client | object | Yes | Buyer details — see below |
client object
| Field | Type | Required | Description |
|---|---|---|---|
phone | string | Yes | Buyer identity — matches or creates the buyer |
full_name | string | Yes | — |
email | string | Yes | — |
address | string | Yes | — |
billing | object | No | {billing_business_name*, billing_email*, billing_address*, billing_pan_vat} (* required if billing present; billing_pan_vat 9 numbers) |
shipping | object | No | {phone*, full_name*, email*, address} (* required if shipping present) |
When billing is omitted, billing info is filled from the buyer's profile. On a reused subscription, only non-null fields overwrite (partial refresh).
Example request
{
"pbp_id": "pbp_3n9k2x",
"return_url": "https://acme.example.com/thank-you",
"client": {
"phone": "9800000000",
"full_name": "Ram Bahadur",
"email": "[email protected]",
"address": "Kathmandu, Nepal",
"billing": {
"billing_business_name": "ABC Pvt Ltd.",
"billing_email": "[email protected]",
"billing_address": "Kathmandu, Nepal",
"billing_pan_vat": "100000000"
}
}
}
Response
Status: 201 Created
| Field | Type | Description |
|---|---|---|
subscription_id | string (UUID) | — |
pbp_id | string | Echoed |
status | string | Always pending_checkout |
checkout_url | string | <FRONTEND_URL>/pay/<subscription_id> |
next_billing_cycle | datetime | null | null until payment |
created_at | datetime | — |
{
"subscription_id": "sub_9f8a7b6c",
"pbp_id": "pbp_3n9k2x",
"status": "pending_checkout",
"checkout_url": "https://app.suqo.ai/pay/sub_9f8a7b6c",
"next_billing_cycle": null,
"created_at": "2026-07-09T10:15:00Z"
}
checkout_url. Send the customer to that
URL — SUQO handles the entire payment flow (collecting details, OTP, wallet/bank payment) and
returns them to your return_url afterward. You don't process the payment yourself.Getting the payment result (webhooks)
The return_url only brings the buyer back to your site — it is not proof of payment. Learn the actual outcome via webhooks, which SUQO POSTs to your endpoint for both cases:
| Outcome | Event |
|---|---|
| Payment succeeded — subscription is now active | checkout.succeeded |
| Payment failed — declined or abandoned | checkout.failed |
Later lifecycle changes (renewal, cancellation, expiry) arrive as subscription.status_changed, which names both the old and the new status. Configure endpoints and verify the signing secret from the Webhooks page. Don't mark an order paid until you receive checkout.succeeded — a status change alone is not a payment.
Errors
| Status | Trigger | Body |
|---|---|---|
400 | pbp_id unknown / not visible for this seller | {"pbp_id": "PlanBillingPeriod with public_id '...' does not exist or is not visible."} |
400 | Account has no API usage type | {"api_usage_type": "Your account does not have an API usage type configured."} |
400 | return_url missing | {"return_url": "A return URL is required for reminder-and-payment accounts."} |
400 | Active subscription already exists for this buyer + product + billing period | {"detail": "An active subscription already exists for this buyer, product, and billing period."} |
pending_checkout) rather than
duplicated. Only a currently active one triggers the duplicate error.Cancel a subscription
POST /api/v1/subscriptions/{id}/cancel/
Cancels a subscription. No request body. Sets is_inactive = true and clears billing_cycle.
Status: 200 OK
{ "message": "Subscription cancelled." }
Update the billing cycle
POST /api/v1/subscriptions/update-billing-cycle/
Sets a subscription's next billing date. This is a collection-level action — the subscription id goes in the body.
Request body
| Field | Type | Required | Constraints |
|---|---|---|---|
subscription_id | string (UUID) | Yes | Must belong to the seller |
next_billing_cycle | date (YYYY-MM-DD) | Yes | Today or future |
{
"subscription_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"next_billing_cycle": "2026-09-01"
}
Status: 200 OK
{ "message": "Billing cycle updated." }
Errors
| Status | Trigger | Body |
|---|---|---|
400 | Date in the past | {"next_billing_cycle": ["next_billing_cycle must be in the future or today."]} |
400 | Subscription not found / not owned | {"subscription_id": "Subscription '...' does not exist."} |