Skip to content

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 /entitlements cover subscription state, lifecycle, and entitlements.

The compact current state of the signed-in user’s workspace subscription: its plan and status.

GET https://api.thebridge.dev/billing/state

| 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"
}

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.

GET https://api.thebridge.dev/account/subscription/status

| 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" }
    ]
  }
}

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.

GET https://api.thebridge.dev/entitlements

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
  }
}

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.

GET https://api.thebridge.dev/account/subscription/plans

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" }
    ]
  }
]

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.

POST https://api.thebridge.dev/account/subscription/select

Body Parameters

ParameterTypeRequiredDescription
planKeystringRequiredThe key of the free plan to assign

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" }'

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.

POST https://api.thebridge.dev/account/subscription/checkout

Body Parameters

ParameterTypeRequiredDescription
planKeystringRequiredThe key of the plan to check out
priceOfferobjectOptionalWhich price to use: { currency, recurrenceInterval }. Defaults to the plan's first price
successUrlstringOptionalAbsolute URL to return to after successful payment
cancelUrlstringOptionalAbsolute URL to return to if the user cancels

{ 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"
}

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.

POST https://api.thebridge.dev/account/subscription/change

Body Parameters

ParameterTypeRequiredDescription
planKeystringRequiredThe key of the plan to switch to
priceOfferobjectOptionalWhich price to use: { currency, recurrenceInterval }. Defaults to the plan's first price

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" }
}'

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 canonical subscription for the workspace. Idempotent: returns the existing subscription if one already exists.

POST https://api.thebridge.dev/billing/subscriptions

Body Parameters

ParameterTypeRequiredDescription
planSlugstringOptionalSlug of the plan to subscribe to

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"
}

Schedule the workspace’s subscription to end at the current period end (status becomes cancel_at_period_end).

POST https://api.thebridge.dev/billing/cancel

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'

Undo a scheduled cancellation: move a cancel_at_period_end subscription back to active. Idempotent on other statuses.

POST https://api.thebridge.dev/billing/reactivate

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 on the workspace’s subscription, using the plan’s trial policy. Returns HTTP 400 if the plan is not trial-enabled.

POST https://api.thebridge.dev/billing/trial/start

Body Parameters

ParameterTypeRequiredDescription
planSlugstringOptionalSlug of the plan to trial. Defaults to 'pro'

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" }'

Get a Stripe Billing Portal URL where the user can manage their own payment method, invoices, and subscription. Redirect the user to portalUrl.

GET https://api.thebridge.dev/account/subscription/portal

{ "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'