> ## Documentation Index
> Fetch the complete documentation index at: https://docs.glot.com/llms.txt
> Use this file to discover all available pages before exploring further.

# How billing works

> Prepaid credit, per-minute metering, and how a room's charge is calculated

Glot bills for **connected minutes**, paid from a **prepaid credit balance**. Nothing is
charged for creating a room, minting join tokens, or calling the REST API — you pay for
the time participants and the translator spend connected to a room.

<CardGroup cols={3}>
  <Card title="Prepaid credit" icon="wallet">
    You top up a balance in advance. Usage is drawn down from it as rooms run.
  </Card>

  <Card title="Metered per participant" icon="users">
    Each connection to a room is metered separately, not the room's wall-clock length.
  </Card>

  <Card title="Priced per class" icon="tags">
    A person and the translator serving the room have their own per-minute rates.
  </Card>
</CardGroup>

## The calculation

A room's charge is the sum of every participant's connected minutes, each priced at the
rate for its own class:

```
room charge = Σ (participant connection minutes × rate for that participant's class)
```

Each connection's minutes are **rounded up to the whole minute**: a participant connected
for 1.5 minutes is billed for 2, and a 10-second connection is billed for 1.

`participant_class` is `human` for each person in the room, or `agent` for the translator
that serves it. The translator bills at a **higher** per-minute rate than a person, which
is why it is not present in an idle room — it joins on its own once the room holds two
languages, and leaves when fewer than two remain.

Read the current rates from `GET /v1/billing/credit` rather than hardcoding them, because
they can change:

```bash title="cURL" theme={null}
curl https://api.staging.glot.com/v1/billing/credit \
  -H "Authorization: Bearer $GLOT_API_KEY"
```

```json title="Response" theme={null}
{
  "credit_cents": 24150,
  "currency": "usd",
  "rates": [
    { "participant_class": "human", "cents_per_minute": 2 },
    { "participant_class": "agent", "cents_per_minute": 5 }
  ],
  "min_topup_cents": 1000,
  "max_topup_cents": 500000
}
```

<Note>
  All amounts across the API are integers in the smallest unit of `currency` — cents, not
  dollars. `credit_cents`, `charged_cents`, `total_charged_cents` and `delta_cents` all
  follow this.
</Note>

### Worked example

Two people hold a translated conversation. Speaker A joins first and stays for the whole
call; Speaker B joins a minute and a half later, and the translator joins with them, since
that is the point at which the room holds two languages. Everyone leaves when the room is
closed at `14:12:10`.

| Participant | Class | Joined | Left | Duration | Billed minutes | Rate | Charge |
| - | - | - | - | - | - | - | - |
| Speaker A | `human` | 14:02:10 | 14:12:10 | 10m 00s | 10 | 2¢/min | 20¢ |
| Speaker B | `human` | 14:03:40 | 14:12:10 | 8m 30s | 9 | 2¢/min | 18¢ |
| Translator | `agent` | 14:03:40 | 14:12:10 | 8m 30s | 9 | 5¢/min | 45¢ |
| **Room total** | | | | | **28** | | **83¢** |

Two things to read out of this:

* **Partial minutes round up.** Speaker B and the translator were each connected for
  8m 30s, and each is billed 9 minutes — the half minute is a whole billed minute.
* **Minutes are summed per participant, not per room.** The room lasted 10 minutes on the
  clock, but `total_connection_minutes` is `28`. Adding a third person to the same call
  would add their minutes on top, not spread the room's 10.

<Warning>
  Because metering is per participant, leaving a client connected to a room after the
  conversation is over keeps accruing minutes. Close the room with
  `DELETE /v1/rooms/{room_id}` rather than relying on participants to disconnect or on
  their join tokens to expire.
</Warning>

## What counts as a connected minute

<AccordionGroup>
  <Accordion title="Metering starts when a participant connects">
    A participant's meter runs from the moment their connection to the room is
    established — recorded as `joined_at` — until it ends, recorded as `left_at`. Time
    between creating a room and anyone joining it costs nothing.
  </Accordion>

  <Accordion title="Partial minutes round up">
    `connection_minutes` is the ceiling of the connection's duration — 1.5 minutes
    connected is 2 billed minutes, and 90.1 minutes is 91. There is no minimum charge
    beyond this: the shortest possible connection costs one minute.
  </Accordion>

  <Accordion title="Every connection is metered on its own">
    A participant who drops and rejoins appears **once per connection** in the room's
    usage, each with its own `joined_at`, `left_at`, `connection_minutes` and
    `charged_cents`. The same is true of the translator, which may join and leave more
    than once as languages come and go.

    Rounding applies **per connection**, not per participant, so a broken connection can
    cost more than an unbroken one of the same total length. A participant who is
    connected for 30 seconds, drops, and comes back for another 30 seconds is billed two
    minutes — where a single unbroken minute would have been one.
  </Accordion>

  <Accordion title="Silence is billed the same as speech">
    The meter measures connected time, not audio. A participant who is connected but not
    speaking is billed for those minutes.
  </Accordion>

  <Accordion title="Totals are provisional until the room ends">
    `connection_minutes` and `charged_cents` are `null` while a participant is still
    connected. A room in progress reports what has been metered so far; its
    `total_connection_minutes` and `total_charged_cents` are final once `ended_at` is set.
    Closing a room settles the totals a moment after the call returns, once the realtime
    provider confirms the room has ended — so a `GET` issued immediately afterwards may
    still show the room as in progress.
  </Accordion>
</AccordionGroup>

## Credit, overage and the 402

Credit is money: a settled payment of `amount_cents` adds exactly `amount_cents` to the
balance. There is no separate currency to convert.

<Steps>
  <Step title="Top up before you start">
    `POST /v1/billing/checkout` with an `amount_cents` inside the accepted range and
    redirect the browser to the `checkout_url` it returns. Credit lands on the balance
    only once the payment **settles**, so it may not be visible the instant the browser
    comes back. Abandoning checkout changes nothing.
  </Step>

  <Step title="Rooms require a positive balance">
    `POST /v1/rooms` returns **402** when the organization's balance is at or below zero.
    Check `credit_cents` before starting a call if you want to fail earlier than that.
  </Step>

  <Step title="A call in progress is never cut off">
    Once a room is running it keeps running, so usage past zero is billed as overage and
    the balance can go negative. What a negative balance blocks is starting the **next**
    room.
  </Step>
</Steps>

## Reading back what you were charged

Three endpoints answer three different questions.

<CardGroup cols={3}>
  <Card title="Per room" icon="receipt">
    `GET /v1/rooms/{room_id}` returns the room with a per-participant breakdown — who
    joined, when they left, their minutes and their charge.
  </Card>

  <Card title="Over time" icon="chart-line">
    `GET /v1/usage/overview` returns totals and a bucketed series for a window of up to
    90 days, for charting.
  </Card>

  <Card title="Line by line" icon="list">
    `GET /v1/billing/movements` returns every credit bought and spent, newest first, with
    the purchase or room behind each one.
  </Card>
</CardGroup>

A room's per-participant breakdown is the authoritative account of a single call:

```bash title="cURL" theme={null}
curl https://api.staging.glot.com/v1/rooms/$ROOM_ID \
  -H "Authorization: Bearer $GLOT_API_KEY"
```

```json title="Response" theme={null}
{
  "id": "3f2b1c8e-5a4d-4e0b-9f7a-2c1d8e6b4a30",
  "name": "clinic-intake-4821",
  "status": "ended",
  "streaming_mode": "dual",
  "started_at": "2026-08-19T14:02:10Z",
  "ended_at": "2026-08-19T14:12:10Z",
  "total_connection_minutes": 28,
  "total_charged_cents": 83,
  "participants": [
    {
      "participant_identity": "patient-118",
      "participant_kind": "human",
      "languages": ["en"],
      "joined_at": "2026-08-19T14:02:10Z",
      "left_at": "2026-08-19T14:12:10Z",
      "connection_minutes": 10,
      "charged_cents": 20
    }
  ]
}
```

<Note>
  A room is a billing record and outlives the call it measured. Closing a room does not
  delete its usage — the record stays available on `GET /v1/rooms/{room_id}` with its
  final status, minutes and charge.
</Note>

### Reconciling the numbers

* **A room's total** equals the sum of its participants' `charged_cents`, including the
  translator's — each already rounded up to the whole minute, so the total is not the
  room's raw duration times a rate.
* **`total_minutes`** in a usage overview sums billed connection minutes across
  participants and **includes** the translator's minutes.
* **`total_participants`** counts the people who connected — a participant who drops and
  rejoins counts twice, and the translator is not counted, even though its minutes are.
* **`avg_room_minutes`** in a bucket is the mean wall-clock length of rooms that started
  in that bucket and have finished. It is not proportional to `minutes`, which is a
  per-participant sum.
* **Balance** is a level rather than a total over a window, so it is not part of a usage
  overview — read it from `GET /v1/billing/credit`.

## Keeping costs down

* **Close rooms explicitly.** `DELETE /v1/rooms/{room_id}` disconnects everyone, the
  translator with them, and stops the meter. It is a no-op on an already-ended room and
  still returns `204`, so a retry on the way out is safe.
* **Don't hold a room open between calls.** Rooms are cheap to create — create one per
  conversation rather than parking an idle one.
* **Avoid churning connections.** Every connection is rounded up on its own, so a client
  that reconnects repeatedly pays a whole minute each time. Keep one stable connection per
  participant for the life of the call.
* **Remember the translator's rate.** A room with two languages present is paying for the
  translator as well as the people in it, so idle time with both languages connected is
  the most expensive kind of idle time.
* **Watch usage per bucket.** `GET /v1/usage/overview` at `hour` granularity makes an
  unexpected overnight room obvious.

<CardGroup cols={2}>
  <Card title="Close a room" icon="circle-stop" href="/api-reference/rooms/close-a-room">
    Stop the meter as soon as the conversation is over.
  </Card>

  <Card title="Billing endpoints" icon="credit-card" href="/api-reference/billing/get-credit-balance">
    Credit balance, top-up checkout, and credit history.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.