Billing

Subscriptions and the Stripe checkout.5 operations

Headers on every response

Every response on this page carries these headers.
X-Trace-Id
— The W3C trace id of this request — the same id in the log and in the traceId of a problem. Quote it when reporting.
Server-Timing
— Where the time went: total, db;dur=…;desc="N queries", and every step the handler named (ADR-163).
X-Diagnostics
— Only when the request asked with X-Diagnostics: 1 or ?diagnostics=1: the same breakdown as one JSON object, with the trace id (ADR-163).

Operations

POST/api/v1/billing/webhookNo session needed

Receive a billing event from Stripe

Not for the apps: public and unauthenticated, verified by the Stripe-Signature header over the raw body (#603); a bad signature is a 400 (code billing_webhook_rejected). Applies checkout completed, subscription changed or deleted, and payment failed. Safe to replay: the same event twice converges on the same state.

Parameters

No parameters

Responses

200OK
400Bad Request
application/problem+jsonProblemDetails
426The build named in X-App-Version is below its platform's version floor (ADR-070). Problem type app-version-unsupported; the only way forward is the store. Callers without the header are never turned away.
application/problem+jsonProblemDetails
429Too many calls in the window; Retry-After says how long.
application/problem+jsonProblemDetails
Headers
Retry-After
— Seconds to wait when a rate limit answered, or an HTTP date when the household's AI is paused. Absent when the household's AI quota for the period is used up — that resets by itself.

Example

curl \
  -X POST \
  "https://bryti.app/api/v1/billing/webhook"
GET/api/v1/households/{householdId}/billing

Read the household's plan, billing status and the plans it could buy

Any member. PaymentsEnabled is false when no provider keys are configured — the apps then hide the upgrade buttons. CanManage means a provider-side customer exists to open the portal for.

Parameters

NameInTypeRequiredDescription
householdIdpathstring (uuid)Required—

Responses

200OK
application/jsonBillingStatusResponse
401No session, or the session is no longer valid.
application/problem+jsonProblemDetails
404Not Found
application/problem+jsonProblemDetails
426The build named in X-App-Version is below its platform's version floor (ADR-070). Problem type app-version-unsupported; the only way forward is the store. Callers without the header are never turned away.
application/problem+jsonProblemDetails
429Too many calls in the window; Retry-After says how long.
application/problem+jsonProblemDetails
Headers
Retry-After
— Seconds to wait when a rate limit answered, or an HTTP date when the household's AI is paused. Absent when the household's AI quota for the period is used up — that resets by itself.

Example

curl -b "bryti_session=…" \
  "https://bryti.app/api/v1/households/3fa85f64-5717-4562-b3fc-2c963f66afa6/billing"

Example response

200application/json
{
  "paymentsEnabled": false,
  "planCode": "string",
  "planName": "string",
  "pendingPlanCode": "string",
  "status": "string",
  "canManage": false,
  "plans": [
    {
      "code": "string",
      "name": "string",
      "priceMonthMinor": 0,
      "priceCurrency": "string",
      "monthlyCredits": 0,
      "purchasable": false,
      "current": false,
      "pending": false
    }
  ]
}
POST/api/v1/households/{householdId}/billing/checkout

Start a hosted checkout for a plan and get the URL to send the caller to

Adults only; a Junior member gets 403. ReturnUrl must be an absolute http(s) URL; the provider sends the caller back to it with ?checkout=success or ?checkout=cancelled. 400 for an unknown plan or a plan that cannot be bought online. 409 (code already_subscribed) when the household already has a paid subscription — refetch the billing status and change plan from the portal instead (#3577). 503 (code billing_disabled) when online payments are switched off in this environment (ADR-026). 403 (code email_not_confirmed) when the caller's own e-mail address has not been confirmed — the receipts go there (ADR-290).

Parameters

NameInTypeRequiredDescription
householdIdpathstring (uuid)Required—

Request body

application/jsonRequiredStartCheckoutRequest

Responses

200OK
application/jsonRedirectResponse
400Bad Request
application/problem+jsonProblemDetails
401No session, or the session is no longer valid.
application/problem+jsonProblemDetails
403Forbidden
application/problem+jsonProblemDetails
409Conflict
application/problem+jsonProblemDetails
426The build named in X-App-Version is below its platform's version floor (ADR-070). Problem type app-version-unsupported; the only way forward is the store. Callers without the header are never turned away.
application/problem+jsonProblemDetails
429Too many calls in the window; Retry-After says how long.
application/problem+jsonProblemDetails
Headers
Retry-After
— Seconds to wait when a rate limit answered, or an HTTP date when the household's AI is paused. Absent when the household's AI quota for the period is used up — that resets by itself.
503Service Unavailable
application/problem+jsonProblemDetails

Problem codes

The `code` in the problem body is what to branch on. `detail` is English, written for a log.

email_not_confirmed
— Confirm your email before you pay.

Example

curl -b "bryti_session=…" \
  -X POST \
  "https://bryti.app/api/v1/households/3fa85f64-5717-4562-b3fc-2c963f66afa6/billing/checkout" \
  -H "Content-Type: application/json" \
  -d '{"planCode":"string","returnUrl":"string"}'

Example response

200application/json
{
  "url": "string"
}
POST/api/v1/households/{householdId}/billing/preview

Preview what changing to a plan would charge now, give now and charge next

Adults only; a Junior member gets 403. Nothing is charged or changed. A bigger plan applies now and pays the difference for the days left of the period (ADR-392 (#3342)): the amount comes from Stripe's preview of the upcoming invoice when the household pays through Stripe, otherwise from the same proration done here with Estimated set. A smaller plan pays nothing now and applies from the next period. From the free plan the full month is paid now. 400 for an unknown plan or one that cannot be bought online; 503 (code billing_disabled) when online payments are switched off.

Parameters

NameInTypeRequiredDescription
householdIdpathstring (uuid)Required—

Request body

application/jsonRequiredBillingPreviewRequest

Responses

200OK
application/jsonBillingPreviewResponse
400Bad Request
application/problem+jsonProblemDetails
401No session, or the session is no longer valid.
application/problem+jsonProblemDetails
403Forbidden
application/problem+jsonProblemDetails
404Not Found
application/problem+jsonProblemDetails
426The build named in X-App-Version is below its platform's version floor (ADR-070). Problem type app-version-unsupported; the only way forward is the store. Callers without the header are never turned away.
application/problem+jsonProblemDetails
429Too many calls in the window; Retry-After says how long.
application/problem+jsonProblemDetails
Headers
Retry-After
— Seconds to wait when a rate limit answered, or an HTTP date when the household's AI is paused. Absent when the household's AI quota for the period is used up — that resets by itself.
503Service Unavailable
application/problem+jsonProblemDetails

Example

curl -b "bryti_session=…" \
  -X POST \
  "https://bryti.app/api/v1/households/3fa85f64-5717-4562-b3fc-2c963f66afa6/billing/preview" \
  -H "Content-Type: application/json" \
  -d '{"planCode":"string"}'

Example response

200application/json
{
  "currentPlanCode": "string",
  "planCode": "string",
  "kind": "NewSubscription",
  "dueNowMinor": 0,
  "currency": "string",
  "daysLeft": 0,
  "periodDays": 0,
  "creditsNow": 0,
  "creditsUsed": 0,
  "creditsLeftAfter": 0,
  "nextPaymentAt": "2026-09-07T16:30:00Z",
  "nextPaymentMinor": 0,
  "appliesAt": "2026-09-07T16:30:00Z",
  "estimated": false,
  "canManage": false
}
POST/api/v1/households/{householdId}/billing/portal

Open the provider's billing portal and get the URL to send the caller to

Adults only; a Junior member gets 403. For managing or cancelling the subscription. 400 when the household has not paid online yet; 503 (code billing_disabled) when online payments are switched off.

Parameters

NameInTypeRequiredDescription
householdIdpathstring (uuid)Required—

Request body

application/jsonRequiredOpenPortalRequest

Responses

200OK
application/jsonRedirectResponse
400Bad Request
application/problem+jsonProblemDetails
401No session, or the session is no longer valid.
application/problem+jsonProblemDetails
426The build named in X-App-Version is below its platform's version floor (ADR-070). Problem type app-version-unsupported; the only way forward is the store. Callers without the header are never turned away.
application/problem+jsonProblemDetails
429Too many calls in the window; Retry-After says how long.
application/problem+jsonProblemDetails
Headers
Retry-After
— Seconds to wait when a rate limit answered, or an HTTP date when the household's AI is paused. Absent when the household's AI quota for the period is used up — that resets by itself.
503Service Unavailable
application/problem+jsonProblemDetails

Example

curl -b "bryti_session=…" \
  -X POST \
  "https://bryti.app/api/v1/households/3fa85f64-5717-4562-b3fc-2c963f66afa6/billing/portal" \
  -H "Content-Type: application/json" \
  -d '{"returnUrl":"string"}'

Example response

200application/json
{
  "url": "string"
}

Schemas

BillingPlanResponse

PropertyTypeRequired
codestringRequired
namestringRequired
priceMonthMinorinteger (int32) | nullRequired
priceCurrencystringRequired
monthlyCreditsinteger (int32)Required
purchasablebooleanRequired
currentbooleanRequired
pendingbooleanRequired

BillingPreviewKind

Type

Values

NewSubscriptionUpgradeDowngradeSame

BillingPreviewRequest

PropertyTypeRequired
planCodestringRequired

BillingPreviewResponse

PropertyTypeRequired
currentPlanCodestringRequired
planCodestringRequired
kindBillingPreviewKindRequired
dueNowMinorinteger (int32)Required
currencystringRequired
daysLeftinteger (int32)Required
periodDaysinteger (int32)Required
creditsNowinteger (int32)Required
creditsUsedinteger (int32)Required
creditsLeftAfterinteger (int32)Required
nextPaymentAtstring (date-time)Required
nextPaymentMinorinteger (int32)Required
appliesAtstring (date-time)Required
estimatedbooleanRequired
canManagebooleanRequired

BillingStatusResponse

PropertyTypeRequired
paymentsEnabledbooleanRequired
planCodestringRequired
planNamestringRequired
pendingPlanCodestring | nullRequired
statusstringRequired
canManagebooleanRequired
plansBillingPlanResponse[]Required

OpenPortalRequest

PropertyTypeRequired
returnUrlstringRequired

ProblemDetails

PropertyTypeRequired
typestring | null—
titlestring | null—
statusinteger (int32) | null—
detailstring | null—
instancestring | null—
codestring | null—

RedirectResponse

PropertyTypeRequired
urlstringRequired

StartCheckoutRequest

PropertyTypeRequired
planCodestringRequired
returnUrlstringRequired