Subscriptions

A subscription is recurring billing tied to a customer: the buyer agreed to a plan at a price, on a cadence, and Storlaunch (or Plugipay, when the module is on) charges them on schedule. State, dunning, cancellation, trial-end, and webhook fan-out are all on this resource.

All endpoints require an sk_* key.

Endpoints

Method Path Purpose
POST /v1/payment/subscriptions Create a subscription
GET /v1/payment/subscriptions List subscriptions
GET /v1/payment/subscriptions/:id Retrieve a subscription
PATCH /v1/payment/subscriptions/:id Update (change plan, pause, resume)
DELETE /v1/payment/subscriptions/:id Cancel

Create

POST /v1/payment/subscriptions

Idempotency-Key required. Tier-limited — some plans cap subscription count.

Request body

Field Type Required Description
customerId string yes Buyer (cust_ or cus_ prefix).
planId string yes The plan being subscribed to.
trialDays integer no Free trial length. During trial, the subscription is trialing and no charges fire.
metadata object no Free-form.

Response201 Created. Returns the subscription object.

Events emittedsubscription.created.

curl -X POST https://storlaunch.forjio.com/api/v1/payment/subscriptions \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"customerId":"cust_01HX...","planId":"plan_01HX...","trialDays":7}'

Retrieve

GET /v1/payment/subscriptions/:id

List

GET /v1/payment/subscriptions?limit=50&cursor=…&status=…&customerId=…

Filter by status (active, trialing, past_due, paused, canceled, unpaid) and by customerId.

Update

PATCH /v1/payment/subscriptions/:id

Change the plan (which re-prorates the current cycle), pause, or resume. Idempotency-Key supported.

Request body

Field Type Description
planId string Switch to a different plan. Pro-rated against the current cycle.
status paused | active Pause to skip the next renewal, resume to charge again. Cannot set to canceled here; use DELETE.
metadata object Replace metadata map.

Cancel

DELETE /v1/payment/subscriptions/:id

Cancels at period-end by default. Pass ?immediate=true to terminate now (no refund — refund separately via checkout sessions).

Events emittedsubscription.canceled.

Subscription lifecycle

[trialing] --trial_end--> [active] <----------> [paused]
                            |                      |
                            v                      v
                       [past_due] --grace--> [unpaid] --> [canceled]
                            |
                            +--retry_success--> [active]

The dunning flow:

  1. Renewal charge attempted at currentPeriodEnd. On success: subscription.renewed + subscription.payment_succeeded.
  2. On failure: subscription.past_due fires; status moves to past_due. Retry schedule kicks in (default: T+1d, T+3d, T+7d).
  3. Each retry attempt that fails emits subscription.payment_failed with the attempt count.
  4. After the final retry, status moves to unpaid and ultimately canceled if the merchant's dunning config calls it.

A successful retry returns the subscription to active and emits subscription.payment_succeeded.

The subscription object

Field Type Nullable Description
id string no sub_ + 26-char ULID.
accountId string no Workspace.
customerId string no The buyer.
planId string no The plan.
status enum no active / trialing / past_due / paused / canceled / unpaid.
currentPeriodStart / currentPeriodEnd string (ISO 8601) no Current billing window.
trialEnd string (ISO 8601) yes When the trial ends.
cancelAtPeriodEnd boolean no Set by DELETE (deferred cancel).
canceledAt string (ISO 8601) yes When the cancel actually executed.
pausedAt string (ISO 8601) yes When pause was set.
failedPaymentCount integer no Consecutive failed-retry count. Reset to 0 on a successful charge.
metadata object no Free-form.
createdAt / updatedAt string no Timestamps.

Events

Event type Fires on
subscription.created POST /v1/payment/subscriptions succeeds.
subscription.renewed Successful renewal charge.
subscription.payment_succeeded Any successful charge (initial, renewal, retry).
subscription.payment_failed Charge attempt failed. Includes attempt counter.
subscription.past_due Status flipped to past_due.
subscription.canceled Cancel executed (immediate or end-of-period).

Trial-end notifications (subscription.trial_will_end) are scheduled in Plugipay's cron when the module is on; in legacy mode this isn't currently emitted.

subscription.trial_will_end is reserved but not emitted in legacy mode. Free-tier workspaces (no Plugipay module) don't fire a trial-ending warning. Subscribe defensively if you handle this in your app.

Next

  • Plans — what the subscription bills for.
  • Customers — the buyer side.
  • Invoices — per-cycle billing receipts.