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

# Scopes

> Every scope, exactly which endpoints each one unlocks, and how to pick the narrowest set that works.

A scope is a permission attached to a key at creation. A key carries a fixed set
of them for its whole life — see [why they are not editable](/api/keys#what-you-can-change-afterwards).

| Scope           | Grants                                                     |
| --------------- | ---------------------------------------------------------- |
| `catalog:read`  | Reading the catalog and reference data                     |
| `rentals:read`  | Seeing the account's rentals and their history             |
| `rentals:write` | Starting and stopping rentals — **this one spends money**  |
| `radar:read`    | Reading [Radar](/api/radar) nodes and your own submissions |
| `radar:write`   | Sending Radar inquiries and proposing nodes                |

A key created without an explicit choice gets `catalog:read` alone.

`radar:write` spends nothing, which makes it easy to wave through — but it does
put text on a page under our name once a reviewer approves it. Grant it to the
integrations that should be able to list hardware, not to every key that reads
Radar.

## Endpoint by endpoint

| Endpoint                                 | Required scope           |
| ---------------------------------------- | ------------------------ |
| `GET /me`                                | *(none — any valid key)* |
| `GET /offers`                            | `catalog:read`           |
| `GET /offers/{id}`                       | `catalog:read`           |
| `GET /gpu-models`                        | `catalog:read`           |
| `GET /gpu-families`                      | `catalog:read`           |
| `GET /regions`                           | `catalog:read`           |
| `GET /prices`                            | `catalog:read`           |
| `POST /rental-quotes`                    | `rentals:read`           |
| `POST /rentals`                          | `rentals:write`          |
| `GET /rentals`                           | `rentals:read`           |
| `GET /rentals/{id}`                      | `rentals:read`           |
| `POST /rentals/{id}/stop`                | `rentals:write`          |
| `GET /rentals/{id}/events`               | `rentals:read`           |
| `GET /radar/nodes`                       | `radar:read`             |
| `GET /radar/nodes/{id}`                  | `radar:read`             |
| `POST /radar/nodes/{id}/inquiries`       | `radar:write`            |
| `GET /radar/submissions`                 | `radar:read`             |
| `POST /radar/submissions`                | `radar:write`            |
| `PUT /radar/submissions/{id}`            | `radar:write`            |
| `POST /radar/submissions/{id}/withdraw`  | `radar:write`            |
| `POST /radar/submissions/{id}/republish` | `radar:write`            |

<Note>
  `GET /me` needs no scope by design. It answers "does this key work and what may
  it do" — a question a key with no scopes at all still deserves an answer to, and
  one that reveals nothing to a holder who already has the key.
</Note>

Quoting sits under `rentals:read` rather than `catalog:read`: a quote reads your
account's balance to fill in `sufficient_balance`, which is more than catalog
data. It is not under `rentals:write` because it changes nothing and costs
nothing.

## Picking a set

<AccordionGroup>
  <Accordion title="A price monitor or capacity dashboard" icon="chart-line">
    **`catalog:read`**

    It reads offers and prices; it never rents. If this key leaks, the worst outcome
    is that someone else learns our public prices — which they could have read from
    the marketplace page anyway.
  </Accordion>

  <Accordion title="A read-only ops dashboard over your own fleet" icon="eye">
    **`catalog:read` + `rentals:read`**

    Shows what is running, what it is costing, and what happened to it. Cannot start
    or stop anything, so a mistake in it is a wrong number on a screen rather than a
    stopped training run.
  </Accordion>

  <Accordion title="A scheduler that starts and stops machines" icon="robot">
    **`catalog:read` + `rentals:read` + `rentals:write`**

    The full set. Pair it with a [daily spend limit](/api/spend-limits) — a scheduler
    is precisely the thing that can loop, and `rentals:write` without a cap is an
    unbounded bill.
  </Accordion>

  <Accordion title="A CI job that smoke-tests your integration" icon="flask">
    **A test key** with whatever scopes the code path touches.

    Test keys read the real catalog, so your parsing and pagination get exercised
    properly, and every write is refused with `test_mode_not_supported` — which your
    CI can assert on.
  </Accordion>
</AccordionGroup>

## When a scope is missing

`403` with `insufficient_scope`, and the error names the scope you needed:

```json theme={null}
{
  "error": {
    "type": "permission_error",
    "code": "insufficient_scope",
    "message": "This API key does not carry the scope required by this endpoint.",
    "required_scope": "rentals:write",
    "doc_url": "https://docs.gpuoutlet.ai/api/errors#insufficient_scope",
    "request_id": "req_5a1e88cd0b12"
  }
}
```

`required_scope` is there so your error handler can say *"this key needs
`rentals:write`"* instead of *"403"*. It lives inside the `error` object like
every other detail — nothing is added beside the envelope.

This is not a retryable error. The key will never grow the scope; mint a new one.

## Adding scopes later

You cannot. The migration is:

<Steps>
  <Step title="Mint a new key with the wider scopes">
    Name it so the two are distinguishable — `prod-scheduler-v2`, not `prod-scheduler copy`.
  </Step>

  <Step title="Deploy the new key">
    Both keys work at once, so there is no window where your integration is down.
  </Step>

  <Step title="Confirm the old key has gone quiet">
    Its [usage panel](/api/key-security#watching-what-a-key-does) should show nothing
    new. This is the step people skip and then get paged for.
  </Step>

  <Step title="Revoke the old key">
    Immediate, and final.
  </Step>
</Steps>

<Tip>
  If you already know a key will eventually need to rent, grant `rentals:read` and
  `rentals:write` when you create it. Unused scopes cost nothing while the key sits
  in a config file doing catalog reads, and granting them up front saves the
  rotation above.

  That said, do not grant them to a key that will genuinely never rent. "Might need
  it one day" is how every over-privileged credential starts.
</Tip>
