Subscriptions & Entitlements
These endpoints operate on a workspace’s live subscription: its current plan, checkout, lifecycle transitions, and the entitlements its plan grants. They’re the runtime counterpart to the Payments reference, which covers the merchant catalog (defining the plans and taxes you sell).
Authentication is different here. Every endpoint on this page acts on the signed-in user’s own workspace, so it authenticates with that user’s access token, not the app’s static
x-api-key:Authorization: Bearer <USER_ACCESS_TOKEN> x-app-id: <YOUR_APP_ID>The access token is the one your app already holds after login (see Authentication). The catalog endpoints on the Payments page, by contrast, use the app
x-api-key.
Two surfaces sit behind these endpoints and this page groups them by task:
/account/subscription/*drives plan selection and checkout. It’s what the Bridge SDKs call./billing/*and/entitlementscover subscription state, lifecycle, and entitlements.
Reading subscription state
Section titled “Reading subscription state”Get subscription state
Section titled “Get subscription state”The compact current state of the signed-in user’s workspace subscription: its plan and status.
HTTP Request
Section titled “HTTP Request”GET https://api.thebridge.dev/billing/state
Response HTTP 200
Section titled “Response HTTP 200”| Field | Type | Description |
|---|---|---|
| plan.slug | string | The plan’s slug |
| plan.name | string | The plan’s display name |
| status | string | One of trial, active, past_due, cancel_at_period_end, canceled |
| endsAt | string? | ISO-8601 timestamp; present while status is trial |
| gateEngaged | boolean? | true when access is gated (e.g. dunning exhausted), in which case the SDK renders a locked notice |
Returns HTTP 404 when the workspace has no subscription yet.
Request example
curl --request GET 'https://api.thebridge.dev/billing/state' \
--header 'Authorization: Bearer USER_ACCESS_TOKEN' \
--header 'x-app-id: YOUR_APP_ID'Response example:
{
"plan": { "slug": "pro", "name": "Pro" },
"status": "active"
}
Get detailed subscription status
Section titled “Get detailed subscription status”A richer status object used to decide what to prompt the user for: whether they still need to pick a plan, set up payment, or are in a failed-payment state. This is what the SDK reads to drive its plan-selection and billing-notice UI.
HTTP Request
Section titled “HTTP Request”GET https://api.thebridge.dev/account/subscription/status
Response HTTP 200
Section titled “Response HTTP 200”| Field | Type | Description |
|---|---|---|
| paymentsEnabled | boolean | Whether the app has payments configured |
| shouldSelectPlan | boolean | The workspace still needs to choose a plan |
| shouldSetupPayments | boolean | The workspace still needs to enter payment details |
| paymentFailed | boolean | The most recent payment failed |
| paymentsAutoRedirect | boolean | When false, the app has opted out of the native plan-selection gate |
| trial | boolean | Whether the workspace is on a trial |
| trialDaysLeft | number | Days remaining in the trial |
| plan | object? | The current plan (key, name, description, trial, trialDays, prices) |
Request example
curl --request GET 'https://api.thebridge.dev/account/subscription/status' \
--header 'Authorization: Bearer USER_ACCESS_TOKEN' \
--header 'x-app-id: YOUR_APP_ID'Response example:
{
"paymentsEnabled": true,
"shouldSelectPlan": false,
"shouldSetupPayments": false,
"paymentFailed": false,
"paymentsAutoRedirect": true,
"trial": false,
"trialDaysLeft": 0,
"plan": {
"key": "pro",
"name": "Pro",
"description": "For growing teams",
"trial": false,
"trialDays": 0,
"prices": [
{ "id": "price_123", "amount": 4900, "currency": "usd", "recurrenceInterval": "month" }
]
}
}
Get entitlements
Section titled “Get entitlements”The entitlement snapshot for the signed-in user’s workspace: a map of entitlement name to a boolean of whether the current plan grants it. The SDK reads this once on attach and then keeps it live over the entitlements.changed push channel.
HTTP Request
Section titled “HTTP Request”GET https://api.thebridge.dev/entitlements
Response HTTP 200
Section titled “Response HTTP 200”Returns { "entitlements": { <name>: boolean } }. Prefer gating features on an entitlement rather than a raw plan name (see Lock features to a plan).
Request example
curl --request GET 'https://api.thebridge.dev/entitlements' \
--header 'Authorization: Bearer USER_ACCESS_TOKEN' \
--header 'x-app-id: YOUR_APP_ID'Response example:
{
"entitlements": {
"ai_completions": true,
"sso": false
}
}
Selecting and changing a plan
Section titled “Selecting and changing a plan”List available plans
Section titled “List available plans”The plans the workspace can choose from. This is the customer-facing view of the catalog; the full catalog with CRUD lives under Payments → Plans.
HTTP Request
Section titled “HTTP Request”GET https://api.thebridge.dev/account/subscription/plans
Response HTTP 200
Section titled “Response HTTP 200”Array of plans, each { key, name, description, trial, trialDays, prices }. Each entry in prices is { id, amount, currency, recurrenceInterval } with recurrenceInterval one of month, year, week, day.
Request example
curl --request GET 'https://api.thebridge.dev/account/subscription/plans' \
--header 'Authorization: Bearer USER_ACCESS_TOKEN' \
--header 'x-app-id: YOUR_APP_ID'Response example:
[
{
"key": "pro",
"name": "Pro",
"description": "For growing teams",
"trial": false,
"trialDays": 0,
"prices": [
{ "id": "price_123", "amount": 4900, "currency": "usd", "recurrenceInterval": "month" }
]
}
]
Select a free plan
Section titled “Select a free plan”Assign a free plan to the workspace directly, with no checkout. Use this only for plans with no cost. Paid plans must go through checkout (below), which returns HTTP 400 if you call this with a paid plan.
HTTP Request
Section titled “HTTP Request”POST https://api.thebridge.dev/account/subscription/select
Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
planKey | string | Required | The key of the free plan to assign |
Response HTTP 201
Section titled “Response HTTP 201”No body.
Request example
curl --request POST 'https://api.thebridge.dev/account/subscription/select' \
--header 'Authorization: Bearer USER_ACCESS_TOKEN' \
--header 'x-app-id: YOUR_APP_ID' \
--header 'Content-Type: application/json' \
--data-raw '{ "planKey": "free" }'Start checkout
Section titled “Start checkout”Create a Stripe Checkout session for a paid plan and get back a URL to send the user to. If the app has no Stripe configured, the plan is assigned directly and sessionId/checkoutUrl come back null.
HTTP Request
Section titled “HTTP Request”POST https://api.thebridge.dev/account/subscription/checkout
Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
planKey | string | Required | The key of the plan to check out |
priceOffer | object | Optional | Which price to use: { currency, recurrenceInterval }. Defaults to the plan's first price |
successUrl | string | Optional | Absolute URL to return to after successful payment |
cancelUrl | string | Optional | Absolute URL to return to if the user cancels |
Response HTTP 201
Section titled “Response HTTP 201”{ sessionId, publicKey, checkoutUrl }: redirect the user to checkoutUrl. publicKey is your Stripe publishable key, for client-side Stripe.js flows.
Request example
curl --request POST 'https://api.thebridge.dev/account/subscription/checkout' \
--header 'Authorization: Bearer USER_ACCESS_TOKEN' \
--header 'x-app-id: YOUR_APP_ID' \
--header 'Content-Type: application/json' \
--data-raw '{
"planKey": "pro",
"priceOffer": { "currency": "usd", "recurrenceInterval": "month" },
"successUrl": "https://your-app.example.com/billing?ok=1",
"cancelUrl": "https://your-app.example.com/billing?canceled=1"
}'Response example:
{
"sessionId": "cs_test_a1b2c3",
"publicKey": "pk_live_xxx",
"checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3"
}
Change plan
Section titled “Change plan”Move an existing subscriber to a different plan/price. If the workspace has an active Stripe subscription, the change is applied through Stripe; otherwise the plan is updated directly.
HTTP Request
Section titled “HTTP Request”POST https://api.thebridge.dev/account/subscription/change
Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
planKey | string | Required | The key of the plan to switch to |
priceOffer | object | Optional | Which price to use: { currency, recurrenceInterval }. Defaults to the plan's first price |
Response HTTP 201
Section titled “Response HTTP 201”No body.
Request example
curl --request POST 'https://api.thebridge.dev/account/subscription/change' \
--header 'Authorization: Bearer USER_ACCESS_TOKEN' \
--header 'x-app-id: YOUR_APP_ID' \
--header 'Content-Type: application/json' \
--data-raw '{
"planKey": "enterprise",
"priceOffer": { "currency": "usd", "recurrenceInterval": "year" }
}'Subscription lifecycle
Section titled “Subscription lifecycle”These endpoints act on the canonical Subscription record and each return the updated subscription object: { id, workspaceId, plan: { slug, name }, status, createdAt, endsAt?, gateEngaged? }.
Create a subscription
Section titled “Create a subscription”Create a canonical subscription for the workspace. Idempotent: returns the existing subscription if one already exists.
HTTP Request
Section titled “HTTP Request”POST https://api.thebridge.dev/billing/subscriptions
Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
planSlug | string | Optional | Slug of the plan to subscribe to |
Response HTTP 201
Section titled “Response HTTP 201”The subscription object (see above).
Request example
curl --request POST 'https://api.thebridge.dev/billing/subscriptions' \
--header 'Authorization: Bearer USER_ACCESS_TOKEN' \
--header 'x-app-id: YOUR_APP_ID' \
--header 'Content-Type: application/json' \
--data-raw '{ "planSlug": "pro" }'Response example:
{
"id": "sub_650cd8510ccba777cc9623de",
"workspaceId": "650cd8510ccba777cc9623d0",
"plan": { "slug": "pro", "name": "Pro" },
"status": "active",
"createdAt": "2026-07-09T10:00:00.000Z"
}
Cancel
Section titled “Cancel”Schedule the workspace’s subscription to end at the current period end (status becomes cancel_at_period_end).
HTTP Request
Section titled “HTTP Request”POST https://api.thebridge.dev/billing/cancel
Response HTTP 201
Section titled “Response HTTP 201”The updated subscription object. endsAt is set to the period end.
Request example
curl --request POST 'https://api.thebridge.dev/billing/cancel' \
--header 'Authorization: Bearer USER_ACCESS_TOKEN' \
--header 'x-app-id: YOUR_APP_ID'Reactivate
Section titled “Reactivate”Undo a scheduled cancellation: move a cancel_at_period_end subscription back to active. Idempotent on other statuses.
HTTP Request
Section titled “HTTP Request”POST https://api.thebridge.dev/billing/reactivate
Response HTTP 201
Section titled “Response HTTP 201”The updated subscription object.
Request example
curl --request POST 'https://api.thebridge.dev/billing/reactivate' \
--header 'Authorization: Bearer USER_ACCESS_TOKEN' \
--header 'x-app-id: YOUR_APP_ID'Start a trial
Section titled “Start a trial”Start a trial on the workspace’s subscription, using the plan’s trial policy. Returns HTTP 400 if the plan is not trial-enabled.
HTTP Request
Section titled “HTTP Request”POST https://api.thebridge.dev/billing/trial/start
Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
planSlug | string | Optional | Slug of the plan to trial. Defaults to 'pro' |
Response HTTP 201
Section titled “Response HTTP 201”The updated subscription object with status: "trial" and endsAt set to the trial end.
Request example
curl --request POST 'https://api.thebridge.dev/billing/trial/start' \
--header 'Authorization: Bearer USER_ACCESS_TOKEN' \
--header 'x-app-id: YOUR_APP_ID' \
--header 'Content-Type: application/json' \
--data-raw '{ "planSlug": "pro" }'Billing portal
Section titled “Billing portal”Open the billing portal
Section titled “Open the billing portal”Get a Stripe Billing Portal URL where the user can manage their own payment method, invoices, and subscription. Redirect the user to portalUrl.
HTTP Request
Section titled “HTTP Request”GET https://api.thebridge.dev/account/subscription/portal
Response HTTP 200
Section titled “Response HTTP 200”{ "portalUrl": "https://billing.stripe.com/p/session/..." }
Request example
curl --request GET 'https://api.thebridge.dev/account/subscription/portal' \
--header 'Authorization: Bearer USER_ACCESS_TOKEN' \
--header 'x-app-id: YOUR_APP_ID'