Skip to content

Subscriptions

A subscription (sub_…) binds a customer to a price and drives recurring billing from the customer’s wallet.

Terminal window
curl -s "$TRILE_API/v1/subscriptions" \
-H "x-api-key: $TRILE_KEY" -H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"customerId": "cus_01ARZ3NDEKTSV4RRFFQ69G5FAW",
"priceId": "price_01ARZ3NDEKTSV4RRFFQ69G5FAY"
}'

What happens on create:

  • No trial → the first cycle is charged immediately from the wallet. If the balance is short, you get 400 insufficient_funds and no subscription is created.
  • Trial price → status starts trialing; the first charge is deferred to the trial’s end.
  • Subscribing a customer to a price they already hold returns 409 subscription_already_exists.
StatusMeaning
trialingIn a free trial; not yet charged.
activeCurrent cycle paid; billing normally.
past_dueA renewal couldn’t be covered by the wallet. Recoverable — customer tops up, Trile retries.
canceledEnded. No further billing.

Each subscription carries currentPeriodStart / currentPeriodEnd so you know when the next charge is due.

Terminal window
curl -X PATCH "$TRILE_API/v1/subscriptions/sub_01ARZ3..." \
-H "x-api-key: $TRILE_KEY" -H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "cancelAtPeriodEnd": true, "metadata": { "plan_note": "downgrade pending" } }'

Use cancelAtPeriodEnd to schedule a cancellation without cutting off access immediately.

Terminal window
# Cancel at end of current period (customer keeps access until then)
curl -X POST "$TRILE_API/v1/subscriptions/sub_01ARZ3.../cancel" \
-H "x-api-key: $TRILE_KEY" \
-H "Content-Type: application/json" \
-d '{ "atPeriodEnd": true }'
# Cancel immediately
curl -X POST "$TRILE_API/v1/subscriptions/sub_01ARZ3.../cancel" \
-H "x-api-key: $TRILE_KEY" \
-H "Content-Type: application/json" \
-d '{ "atPeriodEnd": false }'

Don’t poll for renewals. Subscribe to webhooks:

  • subscription.created / subscription.updated / subscription.canceled
  • invoice.paid — a cycle was charged successfully
  • invoice.payment_failed — wallet couldn’t cover the cycle; subscription is now past_due

See The wallet model for how past_due recovers when the customer tops up.