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

# Authentication and limits

> API keys, scopes, plans, rate limits, idempotency, revisions and errors.

## Keys

Every request carries an API key as a bearer token:

```http theme={null}
Authorization: Bearer ptr_live_8fa2_…
```

A presenter makes a key under **Settings › API**. The secret is shown once; only a hash is stored, so a lost key is replaced, never recovered. A key belongs to a **team**, and reaches the decks of the person who made it plus everything the team shared. It never reaches a colleague's private draft.

A key can be revoked at any time from the same screen. Revocation takes effect on the next request.

## Scopes

A key carries scopes, chosen when it is made. A request that needs a scope the key lacks answers `403` and names it.

| Scope          | Lets a key                               |
| -------------- | ---------------------------------------- |
| `decks:read`   | Read decks and their slides              |
| `decks:write`  | Create decks, add slides, change widgets |
| `rooms:read`   | See rooms and who joined                 |
| `rooms:start`  | Start and stop a live room               |
| `results:read` | Read answers and results                 |

## Plans

Scopes are checked against the team's plan on every request, not only when the key is made, so a plan change takes effect at once.

| Plan | Scopes                                                    | Requests an hour |
| ---- | --------------------------------------------------------- | ---------------- |
| Free | `decks:read`, `decks:write`, `rooms:read`, `results:read` | 60               |
| Pro  | all five                                                  | 1000             |

Creating and changing decks always works, on every plan. Going live from the API is Pro.

## Rate limits

The limit is per key, per hour, on a sliding window. Every reply carries three headers so a client can pace itself:

```http theme={null}
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 41
X-RateLimit-Reset: 2130
```

`X-RateLimit-Reset` is the number of seconds until the oldest request leaves the window. A request over the limit answers `429` with `Retry-After`.

## Idempotency

A `POST` or `PUT` may carry an `Idempotency-Key` header. A retry with the same key within a day gets the same answer back, marked `Idempotent-Replayed: true`, instead of making a second deck. Use it whenever a client may retry after a timeout.

```http theme={null}
POST /api/v1/decks
Idempotency-Key: 8c3a1e7b-first-deck
```

## Revisions

Every deck read and write returns a `revision`, the deck's last save as a timestamp. Send it back with a write and the write lands only if nobody changed the deck since you read it; otherwise you get `409` and read again.

```json theme={null}
{ "title": "Renamed", "revision": "2026-09-07T13:26:56.407Z" }
```

The presenter's editor does the same, so an agent and a person working on one deck refuse each other instead of silently overwriting. The step verbs always write onto the revision they read.

## Errors

Errors are JSON with `error`, what went wrong in a sentence, and, when we can tell, `fix`, what to do about it. A body that does not fit answers `400` and `fix` names the field.

```json theme={null}
{
  "error": "Bad body.",
  "fix": "step.overlays.0.poll.mcStyle: Arc (pairs) needs multiSelect"
}
```

| Status | When                                                                                   |
| ------ | -------------------------------------------------------------------------------------- |
| `400`  | The body did not fit. `fix` names the field.                                           |
| `401`  | No usable key.                                                                         |
| `403`  | The key lacks the scope, or the plan does not sell it.                                 |
| `404`  | Nothing by that id within this key's reach.                                            |
| `409`  | The deck changed since you read it, or the room is in the wrong state for that action. |
| `413`  | A file over 24 MB.                                                                     |
| `429`  | Over the hourly limit. `Retry-After` says how long.                                    |

<Note>
  OAuth for third-party apps and for the connector directories of claude.ai and ChatGPT is coming. Today every integration uses a key.
</Note>
