# REST API

The REST API lists and manages spaces on a creator's behalf. Every endpoint answers JSON, and an error answers with a code in `error`. The calls the Velven page makes for a space, such as sign-in and saves, are not part of it.

## Authentication

Send a creator's token in the `authorization` header. An agent gets one through the [device-code login](https://velven.ai/docs/agent#login), which the creator approves in their browser; the token lasts 90 days. A 401 means it expired or was revoked: log in again.

```bash
curl -s https://velven.ai/api/agent/me -H "authorization: Bearer $VELVEN_TOKEN"
```

A token works only on its own creator's spaces. For another creator's space, the board endpoints answer 404.

## Endpoints

| Endpoint | Auth | What it does |
| --- | --- | --- |
| `POST /api/agent/login` | None | Starts a login; answers a `device_code` and a link for the creator. [Details](https://velven.ai/docs/agent#login) |
| `POST /api/agent/token` | None | Exchanges the `device_code` for a token once the creator approves. [Details](https://velven.ai/docs/agent#approve) |
| `GET /api/agent/me` | Token | The creator the token belongs to. |
| `POST /api/spaces` | Token | Lists a space. [Details](https://velven.ai/docs/agent#submit) |
| `GET /api/spaces?mine=1` | Token | The creator's listed spaces. [Details](https://velven.ai/docs/agent#reply) |
| `POST /api/spaces/verify` | Token | Claims a space Velven listed. [Details](https://velven.ai/docs/agent#claim) |
| `POST /api/spaces/recapture` | Token | Asks for a new clip. [Details](https://velven.ai/docs/agent#picture) |
| `GET`, `PUT /api/spaces/{slug}/boards` | Token | Reads or writes the space's boards. [Details](https://velven.ai/docs/api#boards) |
| `GET`, `POST /api/spaces/{slug}/secret` | Token | The board secret's status, or a new secret. [Details](https://velven.ai/docs/api#secret) |
| `DELETE /api/spaces/{slug}/scores/{id}` | Token | Deletes one submission. [Details](https://velven.ai/docs/api#moderation) |
| `GET`, `POST`, `DELETE /api/spaces/{slug}/bans` | Token | Lists, adds or lifts bans. [Details](https://velven.ai/docs/api#moderation) |
| `POST /api/v1/scores` | Secret | A space's own server posts a score. [Details](https://velven.ai/docs/sdk/server#server-body) |
| `GET /.well-known/jwks.json` | None | The public keys player tokens are signed with. |

## Boards

`GET` answers `{ "boards": [...] }`, every board as it stands. `PUT` takes the same shape as the page's [boards block](https://velven.ai/docs/sdk/leaderboards#board-fields) and writes only the boards named; the page's next sync overwrites a key the page also names.

```bash
curl -s https://velven.ai/api/spaces/<slug>/boards -H "authorization: Bearer $VELVEN_TOKEN"

curl -s -X PUT https://velven.ai/api/spaces/<slug>/boards -H "authorization: Bearer $VELVEN_TOKEN" \
  -H "content-type: application/json" \
  -d '{"boards":[{"key":"main","trust":"client","metric":"points","sort":"desc"}]}'
```

| Status | Body | Meaning |
| --- | --- | --- |
| 200 | `{ "boards": [...] }` | The boards after the write. |
| 409 | `{ "error": "too_many", "message" }` | The write would take the space past its board limit. |
| 422 | `{ "error": "invalid", "issues" }` | A board did not validate; `issues` names the field. |
| 503 | `{ "error": "failed", "message" }` | The boards could not be saved just now. Try again. |

## Board secret

A [server board](https://velven.ai/docs/sdk/server) takes posts only with the space's secret. `POST` issues one and answers 201 with `{ "secret", "message" }`; the secret is shown this once, only its hash is kept, and issuing again replaces it. `GET` answers `{ "secret": { "created_at", "rotated_at" } }`, or `{ "secret": null }` before the first, never the secret itself.

```bash
curl -s -X POST https://velven.ai/api/spaces/<slug>/secret -H "authorization: Bearer $VELVEN_TOKEN"
curl -s https://velven.ai/api/spaces/<slug>/secret -H "authorization: Bearer $VELVEN_TOKEN"
```

## Moderation

Delete one submission; the player is re-ranked from what is left. Or ban a player, by handle, from every board of the space, and lift the ban later.

No endpoint lists submissions yet. The newest are on the space's edit page, each with Delete.

```bash
# delete one submission
curl -s -X DELETE https://velven.ai/api/spaces/<slug>/scores/<id> -H "authorization: Bearer $VELVEN_TOKEN"

# list, add and lift bans
curl -s https://velven.ai/api/spaces/<slug>/bans -H "authorization: Bearer $VELVEN_TOKEN"
curl -s -X POST https://velven.ai/api/spaces/<slug>/bans -H "authorization: Bearer $VELVEN_TOKEN" \
  -H "content-type: application/json" -d '{"handle":"@mara"}'
curl -s -X DELETE https://velven.ai/api/spaces/<slug>/bans -H "authorization: Bearer $VELVEN_TOKEN" \
  -H "content-type: application/json" -d '{"handle":"@mara"}'
```

| Status | Body | Meaning |
| --- | --- | --- |
| 200 | `{ "deleted": true }` or `{ "banned", "handle" }` | Done. |
| 400 | `{ "error": "invalid" }` | The submission id is not a positive integer. |
| 404 | `not_found` or `no_such_player` | No such submission on this space's boards, or no account has that handle. |
| 422 | `{ "error": "invalid", "issues" }` | The body was not `{ handle }`, or it named you. |
