/api/v1/subscriptions/{external_ref}

Read and manage the subscription of one user of your app. A subscription is created by a successful mode: "subscription" checkout and belongs to a customer and a price. The full story (trials, reminders, renewals) is in 03.08 — Subscription lifecycle.

Endpoint Auth
GET /api/v1/subscriptions/{external_ref} HMAC or Firebase ID Token
PUT /api/v1/subscriptions/{external_ref}/customer HMAC or Firebase ID Token
PUT /api/v1/subscriptions/{external_ref}/plan HMAC only

With Firebase ID Token, external_ref in the URL must match the token’s own sub claim — any other value returns 403 forbidden. With HMAC, you can use any external_ref that belongs to your app (your backend is trusted to enforce its own authorization before calling this).

GET /api/v1/subscriptions/{external_ref}

Response — success

{
  "success": true,
  "data": {
    "plan_ref": "pro",
    "plan_id": "plan_3f9c1a7e5b2d4c8a91e0f6b2",
    "price_id": "price_8a1d6c3e9f024b7d5a2c1e90",
    "customer_id": "cus_5d2e7b9a1c4f8036ab12de45",
    "status": "active",
    "current_period_end": "2026-11-03 08:00:00",
    "in_trial": false,
    "trial_ends_at": null
  }
}
Field Meaning
plan_ref The plan’s code
plan_id / price_id The plan and price the subscription currently renews on (null if none is recorded)
customer_id The attached customer (cus_…), or null if none is attached yet
status The effective status — see below
current_period_end End of the paid (or trial) period, UTC YYYY-MM-DD HH:MM:SS. null = lifetime subscription: it never expires
in_trial true while the running period is a trial
trial_ends_at End of the trial (equals current_period_end during a trial), otherwise null

status is one of active / canceled / expired. It is computed at read time: a subscription reads as expired as soon as current_period_end has passed, even if no event has been sent yet. Grant access only while status is active. There is no auto-charge; the customer renews by paying again (see 03.08).

Response — no subscription yet

{ "success": false, "error": { "code": "not_found", "message": "No subscription found for this external_ref." } }

HTTP 404. Not necessarily an error condition in your own flow — a user who has never checked out simply has no subscription record.

PUT /api/v1/subscriptions/{external_ref}/customer

Attaches a customer to the subscription, or switches it to another customer. Reminder emails and webhook payloads refer to this customer.

curl -X PUT https://payments.example.com/api/v1/subscriptions/user-42/customer \
  -H "X-App-Key: pgk_xxx" -H "X-Timestamp: 1700000000" -H "X-Signature: <HMAC>" \
  -H "Content-Type: application/json" \
  -d '{ "customer_id": "cus_5d2e7b9a1c4f8036ab12de45" }'

The response is the same object as GET, with the new customer_id. A missing, unknown or foreign customer_id returns 400 customer_not_found (with Firebase ID Token, the customer must also have been created by the same end user). Works with both auth strategies.

PUT /api/v1/subscriptions/{external_ref}/plan

Moves the subscription to another price — for example monthly to yearly. HMAC only: a Firebase ID Token app gets 403 forbidden.

curl -X PUT https://payments.example.com/api/v1/subscriptions/user-42/plan \
  -H "X-App-Key: pgk_xxx" -H "X-Timestamp: 1700000000" -H "X-Signature: <HMAC>" \
  -H "Content-Type: application/json" \
  -d '{ "price_id": "price_1b7e4d0a8c3f4e29d6a5b310" }'

Response: the same object as GET, now showing the new plan_ref, plan_id and price_id.

  • price_id can be any price on sale (active, on an active plan) — a price of the same plan or of another plan. An unknown, archived or foreign price returns 400 price_not_found.
  • The change takes effect from the next renewal: the running period and what was already paid are untouched, and there is no proration. The next renewal link and reminder emails use the new price.

Errors

Code HTTP When
forbidden 403 Firebase ID Token with another user’s external_ref; or PUT .../plan from a Firebase ID Token app
not_found 404 No subscription for this external_ref
customer_not_found 400 PUT .../customer with a missing/unknown customer_id
price_not_found 400 PUT .../plan with a missing/unknown/archived price_id

See Errors for the full list.


This site uses Just the Docs, a documentation theme for Jekyll.