Skip to content

Subscriptions API

🤖 Explain with AI

The Subscriptions API lets you create and manage customer subscriptions and subscription checkout flows. It provides endpoints to generate checkout links, list and retrieve subscriptions, and cancel subscriptions.

Supported subscription flows include:

  • Create subscription (checkout link): POST /v1/subscriptions — returns a hosted checkout URL to complete payment and create the subscription.
  • List subscriptions: GET /v1/subscriptions — paginated list for the caller (customer or developer).
  • Retrieve subscription by ID: GET /v1/subscriptions/{subscription_id} — get subscription details.
  • List by plan (public key): GET /v1/subscriptions/{key_plan} — subscriptions associated to a given public plan key.
  • Cancel subscription: DELETE /v1/subscriptions/{subscription_id} — cancel an existing subscription.

Base URL (development): http://127.0.0.1:8000

Authentication: the Subscriptions API supports API Key authentication using HTTP Basic Auth where the API key is the username and the password is empty. Example test key: sk_test_51O62xYzAbcDef123 (Base64 for sk_test_51O62xYzAbcDef123: is c2tfdGVzdF81MU82MnhZekFiY0RlZjEyMzo=).


Endpoint

POST /v1/subscriptions

Creates a local pending subscription and returns a hosted checkout URL. The client (frontend) should redirect the customer to this URL to complete payment. The subscription activation is asynchronous — webhooks will notify your system when the subscription becomes active.

Important: Use the test key sk_test_51O62xYzAbcDef123 for local development. Do not expose live keys in client-side code.

Request fields

Parameter Description Type Required
plan_id Local price/plan identifier used to create the subscription string true
customer_id Existing customer identifier (if omitted, a guest/one-off flow may be created) string conditional
quantity Number of units for recurring billing integer false
trial_days Trial period in days (optional) integer false
return_url URL where the user will be redirected after checkout (required) string true

cURL (Basic Auth - shorthand)

curl -X POST 'http://127.0.0.1:8000/v1/subscriptions' \
  -u "sk_test_51O62xYzAbcDef123:" \
  -H 'Content-Type: application/json' \
  -d '{
    "plan_id": "price_1AbCdEfG",
    "customer_id": "cus_1234567890",
    "quantity": 1,
    "return_url": "https://example.com/subscription-result"
  }'

cURL (Manual Authorization header)

curl -X POST 'http://127.0.0.1:8000/v1/subscriptions' \
  -H 'Authorization: Basic c2tfdGVzdF81MU82MnhZekFiY0RlZjEyMzo=' \
  -H 'Content-Type: application/json' \
  -d '{"plan_id":"price_1AbCdEfG","customer_id":"cus_1234567890","return_url":"https://example.com/subscription-result"}'

Python (requests)

import requests
from requests.auth import HTTPBasicAuth

BASE = 'http://127.0.0.1:8000'
API_KEY = 'sk_test_51O62xYzAbcDef123'

payload = {
    'plan_id': 'price_1AbCdEfG',
    'customer_id': 'cus_1234567890',
    'quantity': 1,
    'return_url': 'https://example.com/subscription-result'
}

resp = requests.post(f"{BASE}/v1/subscriptions", auth=HTTPBasicAuth(API_KEY, ''), json=payload)
print(resp.status_code)
print(resp.json())

JavaScript (fetch, Node.js or modern browsers)

const apiKey = 'sk_test_51O62xYzAbcDef123';
const auth = 'Basic ' + Buffer.from(`${apiKey}:`).toString('base64');

fetch('http://127.0.0.1:8000/v1/subscriptions', {
  method: 'POST',
  headers: {
    'Authorization': auth,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    plan_id: 'price_1AbCdEfG',
    customer_id: 'cus_1234567890',
    quantity: 1,
    return_url: 'https://example.com/subscription-result'
  })
})
  .then(r => r.json())
  .then(console.log)
  .catch(console.error)

Angular HttpClient (TypeScript)

import { HttpClient, HttpHeaders } from '@angular/common/http';

const apiKey = 'sk_test_51O62xYzAbcDef123';
const headers = new HttpHeaders({
  'Authorization': `Basic ${btoa(apiKey + ':')}`,
  'Content-Type': 'application/json'
});

const payload = {
  plan_id: 'price_1AbCdEfG',
  customer_id: 'cus_1234567890',
  return_url: 'https://example.com/subscription-result'
};

this.http.post('http://127.0.0.1:8000/v1/subscriptions', payload, { headers })
  .subscribe(console.log, console.error);

Postman

  1. Method: POST
  2. URL: http://127.0.0.1:8000/v1/subscriptions
  3. Authorization: Basic Auth — Username: sk_test_51O62xYzAbcDef123, Password: (leave empty)
  4. Body: raw JSON with plan_id, customer_id, return_url.

Successful Response (201 Created)

{
  "code": 201,
  "title": "Subscription created",
  "content": "Checkout URL generated. Redirect the customer to complete payment.",
  "data": {
    "subscription_id": "sub_abc21d23",
    "checkout_url": "https://console.4geeks.io/checkout/?data=eyJ...",
    "test": true
  }
}

Note: The checkout_url is a one-time URL for the checkout session. Store the returned subscription_id to reconcile webhooks and callbacks.


Retrieve a subscription

Endpoint

GET /v1/subscriptions/{subscription_id}

Retrieve a subscription by its ID. The response includes plan details, status, next billing date, customer information and trial data when applicable.

Path parameter

Parameter Description Type Required
subscription_id Subscription identifier (e.g., sub_abc21d23) string true

Request example

curl -X GET 'http://127.0.0.1:8000/v1/subscriptions/sub_abc21d23' \
  -u "sk_test_51O62xYzAbcDef123:" \
  -H 'Accept: application/json'

Response (200 OK)

{
  "subscription_id": "sub_abc21d23",
  "plan_id": "price_1AbCdEfG",
  "customer_id": "cus_1234567890",
  "status": "active",
  "current_period_start": "2025-12-01T00:00:00Z",
  "current_period_end": "2026-01-01T00:00:00Z",
  "next_billing_date": "2026-01-01",
  "trial_end": null,
  "created_at": "2025-12-01T12:00:00Z",
  "test": true
}

Error (404 Not Found)

{
  "error": {
    "code": 404,
    "type": "not_found",
    "message": "Subscription not found"
  }
}

List subscriptions

Endpoint

GET /v1/subscriptions

Returns a paginated list of subscriptions for the caller. When called with a customer token (or scoped key) it returns that customer’s subscriptions; when called with a developer key it returns subscriptions for that developer’s plans.

Query parameters

Parameter Description Type Required
page Page number (default: 1) integer false
page_size Items per page (default: 10) integer false
status Filter by status (active, canceled, trialing) string false
plan_id Filter by plan id string false
test Filter by test mode (true/false) boolean false

Request example

curl -X GET 'http://127.0.0.1:8000/v1/subscriptions?page=1&page_size=10' \
  -u "sk_test_51O62xYzAbcDef123:" \
  -H 'Accept: application/json'

Response (200 OK)

{
  "count": 12,
  "current_page": 1,
  "total_pages": 2,
  "results": [
    {
      "subscription_id": "sub_abc21d23",
      "plan_id": "price_1AbCdEfG",
      "customer_id": "cus_1234567890",
      "status": "active",
      "next_billing_date": "2026-01-01",
      "test": true
    }
  ]
}

List subscriptions by public plan key

Endpoint

GET /v1/subscriptions/{key_plan}

Return subscriptions associated with a public plan key. This endpoint is useful to list subscribers for a given public plan.

Path parameter

Parameter Description Type Required
key_plan Public plan key (e.g., plan_abcdef) string true

Request example

curl -X GET 'http://127.0.0.1:8000/v1/subscriptions/plan_abcdef' \
  -u "sk_test_51O62xYzAbcDef123:" \
  -H 'Accept: application/json'

Response (200 OK)

[
  {
    "subscription_id": "sub_abc21d23",
    "customer_id": "cus_1234567890",
    "status": "active",
    "next_billing_date": "2026-01-01",
    "test": true
  }
]

Cancel subscription

Endpoint

DELETE /v1/subscriptions/{subscription_id}

Cancels a subscription. The endpoint sets the local PlanSubscription.active field to false and attempts to cancel external billing where applicable. Canceling may also trigger webhooks and notification emails.

Request example

curl -X DELETE 'http://127.0.0.1:8000/v1/subscriptions/sub_abc21d23' \
  -u "sk_test_51O62xYzAbcDef123:" \
  -H 'Accept: application/json'

Response (200 OK)

{
  "code": 200,
  "title": "Subscription cancelled",
  "content": "The subscription has been successfully cancelled.",
  "subscription_id": "sub_abc21d23"
}

Errors

Status Reason Description
400 Bad Request Invalid or malformed request (e.g., invalid plan_id).
401 Unauthorized Missing or invalid API key.
403 Forbidden Caller lacks permission to cancel the target subscription.
404 Not Found Subscription not found.
409 Conflict Cancellation cannot be completed (e.g., billing reconciliation conflict).
500 Server Error Internal error while processing cancellation.

Error Handling (common)

400 Bad Request

{
  "error": {"code": 400, "type": "bad_request", "message": "Missing required field: plan_id"}
}

401 Unauthorized

{
  "error": {"code": 401, "type": "unauthorized", "message": "Invalid API key or missing Authorization header"}
}

403 Forbidden

{
  "error": {"code": 403, "type": "forbidden", "message": "Insufficient permissions"}
}

404 Not Found

{
  "error": {"code": 404, "type": "not_found", "message": "Subscription not found"}
}

500 Internal Server Error

{
  "error": {"code": 500, "type": "server_error", "message": "Unexpected error"}
}

Security Best Practices

  • Use HTTPS in production. The examples above use the local dev URL for convenience.
  • Store API keys in environment variables or a secret manager (do not hardcode in code or commit them).
  • Use test keys for development and live keys for production only.
  • Rotate API keys regularly and remove unused keys.
  • Validate return_url host and scheme to prevent open redirect vulnerabilities.
  • Rely on webhooks to confirm subscription activation — do not assume success only from the checkout redirect.

Troubleshooting

Problem: I get 401 Unauthorized - Verify you are sending Authorization: Basic <base64(key:)> or using curl -u 'sk_test_...:'. - Confirm the API key hasn’t been rotated.

Problem: Checkout URL returns error or is blank - Ensure return_url is valid and accessible. - Check server logs and webhook processing for errors.

Problem: Webhook events not arriving - Verify webhook endpoint is publicly reachable and uses HTTPS in production. - Confirm the webhook signing secret and signature verification on your side.


FAQ

Q: What is returned after creating a subscription?

A: A subscription_id and a checkout_url (hosted checkout). The checkout URL must be visited to complete payment and activation.

Q: Are subscriptions activated synchronously?

A: No. Subscriptions are activated asynchronously. Use webhooks to detect when the subscription becomes active.

Q: Can I use API keys in frontend code?

A: Do not embed live API keys in client-side code. For web integrations, generate the hosted checkout server-side and return the checkout URL to the client.


If you want, I can also:

  • Add a Postman collection JSON for these endpoints (importable)
  • Add examples of webhook payloads and signature verification
  • Add a short subscriptions.md summary card for the docs index

Which would you like next?