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

FieldTypeDescription
countintegerTotal rows
next / previousstring | nullPage URLs
total_subscriptionsintegerSame as count
active_subscriptionsintegerStatus-classified count
due_subscriptionsinteger
inactive_subscriptionsinteger
resultsarraySee subscription item

Subscription item (results[])

FieldTypeDescription
subscription_idstring (UUID)
statusstringOne of pending_checkout, active, due, cancelled, pending_cancellation, inactive — see Statuses.
is_activebooleanstatus == "active"
clientobject{phone, full_name, email, address, billing, shipping}billing/shipping nullable
productobject{product_id, name, plan_name, pbp_id, label, price, currency}
current_period_startdatetime | null
current_period_enddatetime | null= billing_cycle
next_billing_cycledatetime | nullThe date the subscription bills next (same value as billing_cycle). Use this as the next-billing date.
created_atdatetime

Statuses

StatusMeaning
pending_checkoutNewly created; the buyer hasn't completed checkout/payment yet.
activeCurrently active subscription.
duenext_billing_cycle has passed but no payment has been received yet.
cancelledCancelled immediately (a user-prompted cancellation).
pending_cancellationScheduled to cancel — it stays active until the end of the current billing cycle, then cancels.
inactiveAutomatically 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

FieldTypeRequiredDescription
pbp_idstringYesPlan billing period public id (from GET /products)
return_urlstring (URL)YesWhere the buyer returns after checkout
clientobjectYesBuyer details — see below

client object

FieldTypeRequiredDescription
phonestringYesBuyer identity — matches or creates the buyer
full_namestringYes
emailstringYes
addressstringYes
billingobjectNo{billing_business_name*, billing_email*, billing_address*, billing_pan_vat} (* required if billing present; billing_pan_vat 9 numbers)
shippingobjectNo{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

FieldTypeDescription
subscription_idstring (UUID)
pbp_idstringEchoed
statusstringAlways pending_checkout
checkout_urlstring<FRONTEND_URL>/pay/<subscription_id>
next_billing_cycledatetime | nullnull until payment
created_atdatetime
{
  "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"
}
Redirect the buyer to 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:

OutcomeEvent
Payment succeeded — subscription is now activecheckout.succeeded
Payment failed — declined or abandonedcheckout.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

StatusTriggerBody
400pbp_id unknown / not visible for this seller{"pbp_id": "PlanBillingPeriod with public_id '...' does not exist or is not visible."}
400Account has no API usage type{"api_usage_type": "Your account does not have an API usage type configured."}
400return_url missing{"return_url": "A return URL is required for reminder-and-payment accounts."}
400Active subscription already exists for this buyer + product + billing period{"detail": "An active subscription already exists for this buyer, product, and billing period."}
Reuse behaviour: An inactive or expired subscription for the same buyer / product / billing period is reactivated (reset to a clean 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

FieldTypeRequiredConstraints
subscription_idstring (UUID)YesMust belong to the seller
next_billing_cycledate (YYYY-MM-DD)YesToday or future
{
  "subscription_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "next_billing_cycle": "2026-09-01"
}

Status: 200 OK

{ "message": "Billing cycle updated." }

Errors

StatusTriggerBody
400Date in the past{"next_billing_cycle": ["next_billing_cycle must be in the future or today."]}
400Subscription not found / not owned{"subscription_id": "Subscription '...' does not exist."}