Skip to main content

billing

POST /billing/checkout-session​

Create a Stripe Checkout session onto a usage-billed plan

Creates (or reuses, keyed by org) the org's Stripe customer, syncs the stored billing profile to it (WP-B3 req 9), then creates a subscription-mode Checkout Session whose metered line items are exactly the price-map entries for the requested plan at its latest catalog version (req 1). automatic_tax and tax_id_collection follow platform config flags. Until checkout completes, retries for the same org/plan reuse one Stripe idempotency identity; once a current subscription exists, callers must use the billing portal. Plans whose pricing_mode is not usage_based (Free — no payment method ever required; Enterprise — operator-managed, req 12) fail with 400 invalid_argument. Session role floor admin (owner/admin manage billing — 003 §3).

Operation ID: createBillingCheckoutSession · Service: gateway-api

Authentication​

  • consoleSession (http (bearer)) — Console session (003 §2): Identity Platform JWT. Data-plane calls add X-Tessera-Project + X-Tessera-Environment headers, validated against org membership.

Parameters​

NameInRequiredTypeDescription
Idempotency-KeyheadernostringIdempotency key accepted by all POSTs (003 §1): 24 h dedup window, keyed per project. POSTs that are reads (batchSlotScores, scoreSlotEvent, deriveSlotBands, validateSourceRawPreset, sampleSource, watchEventFeedHeads and the preview operations: previewSourceMapping, previewCatalogMapping, previewLabel, previewPatternLabel, previewMarker, previewDatasetEligibility, previewSlotAudience, previewSharedCheckpointImport, previewSlotAtlasInquiry) accept the key but never replay a stored response: a retry with the same key is executed again (and metered again) against current data. A stored response that names an entity is removed when that entity is erased. A key that already answered answers only a retry of the request that stored its response: the same caller, environment, operation, path, query, scoping headers and body (a JSON body by its content, a gzip body by its inflated content). The same key with any other request is 409 already_exists with details[0].reason idempotency_key_reused, and that request is not run. The window holds no in-flight marker: two first requests sent at once can both run. A response stored by a release before this rule cannot be matched to its request and is refused the same way until it expires; send the request again with a new key. A body over 8 MiB (ingest: over its 5 MiB request limit) is run and its response is not stored. The ingest batch (POST /events) is the exception, except for an application's credentials: under a key that answered another batch it runs again, uncached, and its events' ids deduplicate them; so it does when its stored response cannot be checked. The operations that return a one-time secret store their response sealed: the stored copy opens only for the same key and credential, and one that cannot be opened is 409 idempotency_key_reused. While the server cannot check a stored response (it cannot read the key that checks it, for one), the request is 503 unavailable with Retry-After: retry with the same key. An operation whose handler claims the key itself answers the same 503, before it runs, while the key-hash pepper cannot be read; any other request sent with a key runs meanwhile, and its response is not stored.

Idempotency​

This operation accepts an Idempotency-Key header (003 §1): within 24 hours, keyed per project and caller, a retry of the same request gets the first response, and the same key with another request is 409 already_exists (idempotency_key_reused). See the Idempotency-Key parameter for the exceptions.

Request body​

CheckoutSessionRequest

Responses​

StatusSchemaDescription
200BillingSessionResponseThe hosted Checkout Session to redirect the browser to.
defaultErrorEnvelopeError response (003 §1). Every non-2xx response uses this envelope; the HTTP status matches error.code. Rate-limited requests get 429 resource_exhausted with retry_after_seconds and a Retry-After header; the X-RateLimit-* headers ride on EVERY response, success or error (003 §1; plan-driven per API key + endpoint class — WP-B2). A 503 unavailable carries retry_after_seconds too for a lock-wait timeout (SQLSTATE 55P03), a rule preview at capacity (preview_capacity_exhausted), or a label preview during which the erasure of every entity holding its censor clock began (label_preview_clock_entity_erased): how long to wait before retrying.

POST /billing/portal-session​

Create a Stripe customer-portal session

Creates a Stripe Billing Portal session for the org's existing customer (payment methods, invoices, billing details — Stripe-native surfaces). The stored billing profile is synced to the customer first (WP-B3 req 9). Orgs without a billing customer fail with 400 failed_precondition. Session role floor admin.

Operation ID: createBillingPortalSession · Service: gateway-api

Authentication​

  • consoleSession (http (bearer)) — Console session (003 §2): Identity Platform JWT. Data-plane calls add X-Tessera-Project + X-Tessera-Environment headers, validated against org membership.

Parameters​

NameInRequiredTypeDescription
Idempotency-KeyheadernostringIdempotency key accepted by all POSTs (003 §1): 24 h dedup window, keyed per project. POSTs that are reads (batchSlotScores, scoreSlotEvent, deriveSlotBands, validateSourceRawPreset, sampleSource, watchEventFeedHeads and the preview operations: previewSourceMapping, previewCatalogMapping, previewLabel, previewPatternLabel, previewMarker, previewDatasetEligibility, previewSlotAudience, previewSharedCheckpointImport, previewSlotAtlasInquiry) accept the key but never replay a stored response: a retry with the same key is executed again (and metered again) against current data. A stored response that names an entity is removed when that entity is erased. A key that already answered answers only a retry of the request that stored its response: the same caller, environment, operation, path, query, scoping headers and body (a JSON body by its content, a gzip body by its inflated content). The same key with any other request is 409 already_exists with details[0].reason idempotency_key_reused, and that request is not run. The window holds no in-flight marker: two first requests sent at once can both run. A response stored by a release before this rule cannot be matched to its request and is refused the same way until it expires; send the request again with a new key. A body over 8 MiB (ingest: over its 5 MiB request limit) is run and its response is not stored. The ingest batch (POST /events) is the exception, except for an application's credentials: under a key that answered another batch it runs again, uncached, and its events' ids deduplicate them; so it does when its stored response cannot be checked. The operations that return a one-time secret store their response sealed: the stored copy opens only for the same key and credential, and one that cannot be opened is 409 idempotency_key_reused. While the server cannot check a stored response (it cannot read the key that checks it, for one), the request is 503 unavailable with Retry-After: retry with the same key. An operation whose handler claims the key itself answers the same 503, before it runs, while the key-hash pepper cannot be read; any other request sent with a key runs meanwhile, and its response is not stored.

Idempotency​

This operation accepts an Idempotency-Key header (003 §1): within 24 hours, keyed per project and caller, a retry of the same request gets the first response, and the same key with another request is 409 already_exists (idempotency_key_reused). See the Idempotency-Key parameter for the exceptions.

Request body​

PortalSessionRequest

Responses​

StatusSchemaDescription
200BillingSessionResponseThe hosted portal session to redirect the browser to.
defaultErrorEnvelopeError response (003 §1). Every non-2xx response uses this envelope; the HTTP status matches error.code. Rate-limited requests get 429 resource_exhausted with retry_after_seconds and a Retry-After header; the X-RateLimit-* headers ride on EVERY response, success or error (003 §1; plan-driven per API key + endpoint class — WP-B2). A 503 unavailable carries retry_after_seconds too for a lock-wait timeout (SQLSTATE 55P03), a rule preview at capacity (preview_capacity_exhausted), or a label preview during which the erasure of every entity holding its censor clock began (label_preview_clock_entity_erased): how long to wait before retrying.

PUT /billing/profile​

Replace and sync the org billing profile

Stores the org's complete billing profile and immediately reconciles it to its Stripe customer before any later checkout/portal session (WP-B3 req 9). tax_ids is replacement state: omitted IDs are removed from Stripe, retained IDs are not recreated, and new IDs are created once. The org resolves only from the scoped console session; no request-supplied org/customer id is accepted. Session role floor admin.

Operation ID: updateBillingProfile · Service: gateway-api

Authentication​

  • consoleSession (http (bearer)) — Console session (003 §2): Identity Platform JWT. Data-plane calls add X-Tessera-Project + X-Tessera-Environment headers, validated against org membership.

Request body​

BillingProfileUpdateRequest

Responses​

StatusSchemaDescription
200BillingProfileSyncResponseThe profile is stored and synchronized to Stripe.
defaultErrorEnvelopeError response (003 §1). Every non-2xx response uses this envelope; the HTTP status matches error.code. Rate-limited requests get 429 resource_exhausted with retry_after_seconds and a Retry-After header; the X-RateLimit-* headers ride on EVERY response, success or error (003 §1; plan-driven per API key + endpoint class — WP-B2). A 503 unavailable carries retry_after_seconds too for a lock-wait timeout (SQLSTATE 55P03), a rule preview at capacity (preview_capacity_exhausted), or a label preview during which the erasure of every entity holding its censor clock began (label_preview_clock_entity_erased): how long to wait before retrying.

GET /billing/subscription​

Read the org's billing subscription and upcoming invoice

The org's pinned plan, dunning state (active|past_due|restricted|downgraded) and — for orgs with a Stripe subscription — the current period bounds plus an upcoming_invoice whose line quantities are computed from the same WP-B1 rollups GET /usage serves (never from Stripe; transparency invariant, WP-B3 req 5). Invoice line amounts, subtotal, tax, and total come from Stripe, preserving included tiers and fractional rates (the platform never computes tax — req 9). Orgs without a Stripe subscription (Free, operator-managed Enterprise without a subscription row) get upcoming_invoice: null. Session role floor member; sessions scope tenancy via X-Tessera-Project + X-Tessera-Environment (003 §2) — billing is org-level, the org resolves from the scoped project.

Operation ID: getBillingSubscription · Service: gateway-api

Authentication​

  • apiKey (http (bearer)) — Project API key (003 §2): tsk_live_… or tsk_test_… in Authorization: Bearer. Scoped to exactly one project + environment; scopes are a subset of ingest, read, admin. Keys are stored hashed and shown once at creation.
  • consoleSession (http (bearer)) — Console session (003 §2): Identity Platform JWT. Data-plane calls add X-Tessera-Project + X-Tessera-Environment headers, validated against org membership.

Responses​

StatusSchemaDescription
200BillingSubscriptionThe org's subscription view.
defaultErrorEnvelopeError response (003 §1). Every non-2xx response uses this envelope; the HTTP status matches error.code. Rate-limited requests get 429 resource_exhausted with retry_after_seconds and a Retry-After header; the X-RateLimit-* headers ride on EVERY response, success or error (003 §1; plan-driven per API key + endpoint class — WP-B2). A 503 unavailable carries retry_after_seconds too for a lock-wait timeout (SQLSTATE 55P03), a rule preview at capacity (preview_capacity_exhausted), or a label preview during which the erasure of every entity holding its censor clock began (label_preview_clock_entity_erased): how long to wait before retrying.

POST /billing/webhooks/stripe​

Inbound Stripe billing webhook

Stripe-only inbound endpoint (WP-B3 req 3), authenticated exclusively by the Stripe-Signature header (t=\<ts\>,v1=\<hex\>, v1 = HMAC-SHA256(secret, "\{t\}.\{raw_body\}"), ±5 min tolerance, constant-time comparison against the Secret Manager signing secret) — no API keys, no sessions, excluded from the plan rate-limit classes with its own modest fixed limit. Signature failures answer 401 unauthenticated and record nothing. Processing is idempotent: the Stripe event id is unique-inserted; an already-seen id answers 200 with no side effects. Handled types: checkout.session.completed, customer.subscription.updated, customer.subscription.deleted, invoice.finalized, invoice.paid, invoice.payment_failed; everything else is 200-and-ignored. The handler answers < 5 s — heavy work belongs to the hourly usage sync.

Operation ID: receiveStripeBillingWebhook · Service: gateway-api

Authentication​

  • stripeSignature (apiKey) — Stripe webhook signature (WP-B3 req 3): t=\<ts\>,v1=\<hex\> where v1 = HMAC-SHA256(signing_secret, "\{t\}.\{raw_body\}"), ±5 minute tolerance, constant-time comparison. The signing secret lives in Secret Manager (004 §2 rule 4). Documented for POST /billing/webhooks/stripe only — no platform credential is accepted there.

Responses​

StatusSchemaDescription
200StripeWebhookReceiptDelivery acknowledged (processed, duplicate, or ignored).
defaultErrorEnvelopeError response (003 §1). Every non-2xx response uses this envelope; the HTTP status matches error.code. Rate-limited requests get 429 resource_exhausted with retry_after_seconds and a Retry-After header; the X-RateLimit-* headers ride on EVERY response, success or error (003 §1; plan-driven per API key + endpoint class — WP-B2). A 503 unavailable carries retry_after_seconds too for a lock-wait timeout (SQLSTATE 55P03), a rule preview at capacity (preview_capacity_exhausted), or a label preview during which the erasure of every entity holding its censor clock began (label_preview_clock_entity_erased): how long to wait before retrying.

Errors​

Every non-2xx response uses the 003 §1 error envelope; error.request_id matches the X-Request-Id response header, and the HTTP status always matches error.code:

{
"error": {
"code": "invalid_argument",
"details": [
{
"type": "field_violation"
}
],
"message": "human-readable",
"request_id": "req_01hxample"
}
}

Closed error-code set (003 §1): invalid_argument, unauthenticated, permission_denied, not_found, already_exists, failed_precondition, resource_exhausted, internal, unavailable.

resource_exhausted responses (HTTP 429) additionally carry retry_after_seconds. See ErrorEnvelope.