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

# Authentication

> How Gomry Cloud API keys work: the gck_live_ format, scopes per operation, and the two independent checks every request passes.

Gomry Cloud authenticates machines with API keys, sent as a bearer token. There is no session, no OAuth flow and no browser-facing credential — a Cloud key is a server-side secret.

<h2 id="sending-a-key">
  Sending a key
</h2>

```http theme={null}
POST /v1/scrape HTTP/1.1
Host: api.cloud.gomry.com
Authorization: Bearer gck_live_...
Content-Type: application/json
```

<Warning>
  Never put a Cloud key in browser JavaScript, a mobile app, or anything else you ship to a user. The data plane sends no CORS headers, deliberately — a key that a browser can read is a key anyone can spend.
</Warning>

<h2 id="key-format">
  Key format
</h2>

A key is the `gck_live_` prefix plus 32 random bytes. There is one kind of key: it does the work and bills for it. Spend is bounded by the project's monthly budget cap, not by the credential.

<Warning>
  `gck_test_` keys are retired. They ran the real request against the real upstream and were simply not invoiced, which made "test" a promise of a sandbox that never existed. Existing ones no longer authenticate — mint a replacement in the console.
</Warning>

We store only a SHA-256 hash of the key. The plaintext is returned once, at mint time, and cannot be recovered — if it is lost, revoke it and mint another. The console shows a key by its prefix and last four characters, which is enough to tell two keys apart and useless to anyone who steals the display.

<h2 id="scopes">
  Scopes
</h2>

A scope names exactly one operation a key may perform, in the form `service:action`. A key carries the scopes you grant it and nothing else.

| Field           | Description                                                                                                                                                                                                                                                                                                             |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `scrape:run`    | Call POST /v1/scrape — render a page and extract structured data from it.                                                                                                                                                                                                                                               |
| `scrape:fetch`  | Call POST /v1/fetch — render a page and return its HTML and metadata.                                                                                                                                                                                                                                                   |
| `toolsapi:read` | Search and inspect the tool catalogue — POST /v1/toolsapi/discover, POST /v1/toolsapi/inspect, and polling a run with GET /v1/toolsapi/runs/\{runId}. Reads only; it cannot execute anything.                                                                                                                           |
| `toolsapi:run`  | Call POST /v1/toolsapi/run — execute a tool and pay its upstream cost. This is the scope that spends: grant it separately from toolsapi:read.                                                                                                                                                                           |
| `tasks:run`     | Call POST /v1/superagent — start a research task. This is the scope that spends, and it commits to work that continues after the request returns.                                                                                                                                                                       |
| `tasks:read`    | Poll a task with GET /v1/superagent/\{taskId} and stop one with DELETE. Neither is billed, so a worker that only collects results needs nothing more than this.                                                                                                                                                         |
| `inference:run` | Call POST /v1/inference — one model call, billed per input and output token at the capability you asked for. Nothing implies it: it is the only scope whose cost per call is set by how much you send rather than by the operation, so a key that may run a task must still be granted this to call the model directly. |

<Warning>
  Scopes are flat: there are no wildcards, and `scrape:run` does **not** imply `scrape:fetch`. They are separately priced operations, so they are separately granted — a key minted for a cheap fetch loop must not be able to spend four times the rate on extractions.
</Warning>

<h2 id="two-checks">
  Two independent checks
</h2>

A request is authorized by two separate facts, and both must hold. They fail with different codes so you can tell them apart without guessing.

| Check                                        | Failure               | Fix                                             |
| -------------------------------------------- | --------------------- | ----------------------------------------------- |
| The key carries the scope for this operation | `403 forbidden`       | Mint a key with the scope, or grant it.         |
| The project has this API enabled             | `403 api_not_enabled` | Turn the API on for the project in the console. |

Keeping them separate is what stops each from silently widening the other: enabling an API must not grant new power to keys that already exist, and a broadly scoped key must not reach an API the project never turned on.

<h2 id="rotation">
  Rotation and revocation
</h2>

Revocation takes effect on the next request — there is no cache of authorization decisions to wait out. To rotate without downtime, mint the new key, deploy it, then revoke the old one. A key can also be given an expiry at mint time, which is the better default for anything handed to a third party.
