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, which the creator approves in their browser; the token lasts 90 days. A 401 means it expired or was revoked: log in again.
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 |
POST /api/agent/token | None | Exchanges the device_code for a token once the creator approves. Details |
GET /api/agent/me | Token | The creator the token belongs to. |
POST /api/spaces | Token | Lists a space. Details |
GET /api/spaces?mine=1 | Token | The creator's listed spaces. Details |
POST /api/spaces/verify | Token | Claims a space Velven listed. Details |
POST /api/spaces/recapture | Token | Asks for a new clip. Details |
GET, PUT /api/spaces/{slug}/boards | Token | Reads or writes the space's boards. Details |
GET, POST /api/spaces/{slug}/secret | Token | The board secret's status, or a new secret. Details |
DELETE /api/spaces/{slug}/scores/{id} | Token | Deletes one submission. Details |
GET, POST, DELETE /api/spaces/{slug}/bans | Token | Lists, adds or lifts bans. Details |
POST /api/v1/scores | Secret | A space's own server posts a score. Details |
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 and writes only the boards named; the page's next sync overwrites a key the page also names.
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 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.
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.
# delete one submissioncurl -s -X DELETE https://velven.ai/api/spaces/<slug>/scores/<id> -H "authorization: Bearer $VELVEN_TOKEN"# list, add and lift banscurl -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. |