Files
bincio-activity/docs/reference/api.md
T
2026-04-08 19:37:33 +02:00

4.1 KiB
Raw Blame History

API reference

bincio serve exposes a JSON API on /api/*. In production, nginx proxies these routes from the public domain. In local development, Vite proxies them from astro dev.

All request and response bodies are application/json. Authentication uses an httpOnly session cookie (bincio_session).


Authentication

GET /api/me

Returns the currently authenticated user, or 404 if not logged in.

Response 200

{
  "handle": "dave",
  "display_name": "Dave",
  "is_admin": true
}

Response 404 — not authenticated


POST /api/auth/login

Rate-limited: 10 attempts per 15 minutes per IP.

Request

{ "handle": "dave", "password": "your-password" }

Response 200 — sets bincio_session cookie (httpOnly, SameSite=Lax, 30-day max-age)

{ "ok": true, "handle": "dave", "display_name": "Dave" }

Response 401 — invalid credentials
Response 429 — rate limit exceeded


POST /api/auth/logout

Deletes the session from the database and clears the cookie.

Response 200

{ "ok": true }

Registration

POST /api/register

Creates a new user account using a valid invite code.

Request

{
  "code": "ABCD1234",
  "handle": "alice",
  "password": "my-password",
  "display_name": "Alice"
}

Handle rules: lowercase letters, numbers, _, -; 130 characters.
Password: minimum 8 characters.

Response 200 — sets session cookie, logs in immediately

{ "ok": true, "handle": "alice" }

Response 400 — invalid handle, password too short, or invalid/used invite code
Response 409 — handle already taken


Invites

All invite endpoints require authentication.

GET /api/invites

Lists invite codes created by the current user.

Response 200

[
  {
    "code": "ABCD1234",
    "used": false,
    "used_by": null,
    "created_at": "2026-04-01T10:00:00Z",
    "used_at": null
  }
]

POST /api/invites

Generates a new invite code for the current user. Regular users are limited to 3 invites; admins are unlimited.

Response 200

{ "ok": true, "code": "EFGH5678" }

Response 400 — invite limit reached


Admin

GET /api/admin/users

Lists all users. Admin only.

Response 200

[
  {
    "handle": "dave",
    "display_name": "Dave",
    "is_admin": true,
    "created_at": "2026-03-01T00:00:00Z"
  }
]

Response 403 — not an admin


Write API

All write endpoints require authentication. Users can only read/write their own activities.

GET /api/activity/{activity_id}

Returns the full activity JSON for an activity owned by the current user.

Response 200 — BAS activity detail object
Response 404 — activity not found or not owned by user


POST /api/activity/{activity_id}

Writes a sidecar edit for an activity. Triggers an incremental shard rebuild if --site-dir was passed to bincio serve.

Request

{
  "title": "Epic climb",
  "description": "Rode with friends.",
  "sport": "cycling",
  "private": false,
  "highlight": false,
  "gear": "Trek Domane"
}

All fields are optional. Only provided fields are written to the sidecar.

Response 200

{ "ok": true }

POST /api/strava/sync

Triggers a Strava sync for the current user's data directory. Uses the stored OAuth token in {handle}/strava_token.json.

Response 200

{ "new_count": 3, "error_count": 0 }

Error format

All errors follow FastAPI's default format:

{ "detail": "Invalid credentials" }

Notes

  • The session cookie is SameSite=Lax. The server sets secure=False because TLS termination is handled by nginx/caddy. If you serve bincio serve directly on HTTPS (not recommended), set secure=True in server.py.
  • There is no CSRF protection — the API relies on the same-origin constraint enforced by SameSite=Lax cookies.
  • The CORS policy allows localhost:* origins for local development only. Cross-origin requests from production domains are blocked — all traffic must go through the nginx proxy.