# Authentication and scopes

## Keys

Every request carries your business's API key:

```text
Authorization: Bearer ocost_sk_<id>_<secret>
```

- **Made** under **[Settings › API](/settings/api)** by the owner or the accountant, and shown once. Ocost keeps only a fingerprint: revoke and replace a lost key.
- **The start,** `ocost_sk_<id>_`, isn't secret. Settings lists keys by it, with their scopes and last use; secret scanners spot the `ocost_sk_` prefix in a leak.
- **One business per key,** and never people's papers (pay slips, payroll, health). One Odoo company is one business with its own key.
- **Revoking** refuses the very next request. A key may also expire on a date.
- **At most 10 live keys** per business. To rotate, make a new key, switch to it, revoke the old one.
- **Server only:** never in a browser, a mobile app or a log.

## Scopes

Settings › API ticks all three by default.

| Scope | Routes |
| --- | --- |
| `documents:write` | `POST /public/v1/files`, `DELETE /public/v1/files/{file_id}` |
| `documents:read` | `GET /public/v1/files`, `GET /public/v1/files/{file_id}`, `GET /public/v1/files/{file_id}/content`, `GET /public/v1/files/{file_id}/document`, `GET /public/v1/documents/{document_id}` |
| `search:read` | `GET /public/v1/search` |

## Refusals

| Answer | When |
| --- | --- |
| `401 urn:ocost:key:invalid` | No key, or a malformed, wrong, revoked or expired one, or a deleted business's. Always the same answer. |
| `403 urn:ocost:key:scope` | The key lacks the route's scope. |
| `429 urn:ocost:key:rate` | Too many requests, or too many wrong keys from one address. Wait `Retry-After` seconds. |
| `400` | An `Ocost-View…` header, which only Ocost's web app sends. |

## Example

```bash
curl -sS https://api.ocost.io/public/v1/files \
  -H "Authorization: Bearer $OCOST_KEY"
```

```python
import requests

session = requests.Session()
session.headers["Authorization"] = f"Bearer {key}"
files = session.get("https://api.ocost.io/public/v1/files", timeout=15).json()
```

```javascript
const files = await fetch('https://api.ocost.io/public/v1/files', {
  headers: { Authorization: `Bearer ${key}` },
}).then((r) => r.json());
```
