# 03 — API Contract - Base URL: `/api/v1` - Auth - Tenant apps: OAuth2 Authorization Code + PKCE, refresh tokens. - Super Admin: session-authenticated Filament (web only). - Common - Pagination: `page`, `per_page` (max 100). - Errors: `{ error: { code, message, trace_id }, details?: {...} }`. - Rate limits: per-tenant and per-device for tenant apps; 429 with `x-rate-limit-*` headers. Key Endpoints (abridged) - Auth: `/oauth/authorize`, `/oauth/token`, `/oauth/token/refresh`. - Tenants (Super Admin only): list/read; no create via API for MVP. - Events (tenant): CRUD, publish, archive; unique by `tenant_id + slug`. - Photos (tenant): signed upload URL, create, list, moderate, feature. - Emotions & Tasks: list, tenant overrides; task library scoping. - Purchases & Ledger: create purchase intent, webhook ingest, ledger list. - Settings: read/update tenant theme, limits, legal page links. Guest Polling (no WebSockets in v1) - GET `/events/{token}/stats` — lightweight counters for Home info bar. - Response: `{ online_guests: number, tasks_solved: number, latest_photo_at: ISO8601 }`. - Cache: `Cache-Control: no-store`; include `ETag` for conditional requests. - GET `/events/{token}/photos?since=` — incremental gallery refresh. - Response: `{ data: Photo[], next_cursor?: string, latest_photo_at: ISO8601 }`. - Use `If-None-Match` or `If-Modified-Since` to return `304 Not Modified` when unchanged. Webhooks - Payment provider events, media pipeline status, and deletion callbacks. All signed with shared secret per provider.