---
title: "Billing summary for a workspace"
method: GET
path: "/v2/workspaces/{id}/billing"
tags: ["Billing"]
---

# Billing summary for a workspace

`GET /v2/workspaces/{id}/billing`

## Request

- Base URL: `https://api.infolang.ai`
- URL: `https://api.infolang.ai/v2/workspaces/{id}/billing`
- Auth: one of:
  - HTTP bearer
  - HTTP bearer
  - HTTP bearer

The PLAN is the OWNER's — a workspace's billing account is whoever owns it, quota pools at that account, and the plan is what sizes the pool. The subscription STATUS and invoices are the caller's own. `workspace_meta.plan` is a database-maintained mirror for Kairos and is not what this reports.

Prefer this over `GET /v2/billing` whenever you are rendering a particular workspace: the caller is not always its owner.

`self_serve_checkout` reports whether this deployment can take a payment RIGHT NOW, at the price the pricing page shows. It is `true` only when a payment processor is wired AND its configured prices reconcile with the published catalog — amount, currency, and monthly interval. When it is `false` the checkout and portal routes answer `503 not_configured`, and a client should hide Upgrade / Manage billing and offer a contact route instead.

It is deliberately NOT just "is Stripe configured": a price edited in the Stripe dashboard takes the button away rather than breaking it, which is the difference between a contact route and a failed checkout.

Requires the `billing:read` scope. Accepts all three lanes: a session JWT (bounded by the caller's role on this workspace) or an `il_*` machine credential whose own scope set, workspace set and validity window cover it — both ceilings apply and neither can raise the other, so a credential carrying `billing:read` is still refused where the role does not permit it.

A credential with no scope record resolves to `LEGACY_DEFAULT`, which does NOT include `billing:read` — mint a key with it explicitly (`POST /v2/workspaces/{id}/keys`).

## Path parameters

- `id` string, required

## Response `200`

Plan, status, and invoice history

- object
  - `plan` string, required
  - `billing_status` 'active' | 'past_due' | 'suspended' | 'free', required
  - `invoices` object[], required
    - `id` string, required
    - `date` string, required
    - `amount_usd` number, required
    - `status` string, required
    - `url` string
  - `self_serve_checkout` boolean, required
  - `has_active_subscription` boolean, required
  - `cancel_at_period_end` boolean, required
  - `current_period_end` string, nullable, required

## Other responses

- `401`: Missing/invalid credential, or `credential_expired` — the credential is outside its `[not_before, expires_at)` window and must be replaced
- `403`: `insufficient_scope` — the credential's own scope set, or the caller's role on this workspace, does not cover the scope this route requires (the missing scope is named in the message). Or `forbidden` — the credential is not scoped to this workspace, or holds no grant on it
- `500`: internal_error — unexpected failure (e.g. billing backend misconfigured or unreachable)

---

[API](https://skmtc.dev/infolang/apis/infolang-api.md) · [All operations](https://skmtc.dev/infolang/apis/infolang-api/llms.txt) · [OpenAPI document](https://skmtc.dev/infolang/apis/infolang-api/revisions/d04bf6de72c8?raw)
