REST API
The Flowstate REST API provides HTTP endpoints for the web application. All requests are routed through the Astro server-side proxy, which forwards them to the backend service.
Base URL
Section titled “Base URL”All API endpoints are accessed through the Astro server proxy:
https://app.goflowstate.com/api/*The web application uses relative URLs (/api/*) which are automatically proxied to the backend service via the SERVER_API_URL environment variable.
Authentication
Section titled “Authentication”The REST API uses cookie-based JWT authentication. After successful login, the server sets a flowstate_session cookie containing a signed JWT token.
Authentication Flow:
- User requests magic link via
/api/auth/request-login - User clicks link in email, which redirects to
/api/auth/authorize - Server validates token and sets
flowstate_sessioncookie - Subsequent requests include cookie automatically
- Server validates JWT on protected routes
Cookie Details:
- Name:
flowstate_session - HttpOnly:
true - Secure:
true(production only) - SameSite:
Lax - Max-Age: 30 days
Response Format
Section titled “Response Format”All API responses follow a consistent structure.
Success Response (Single Resource)
Section titled “Success Response (Single Resource)”{ "data": { "id": "spark_abc123", "title": "My Spark", "createdAt": "2026-02-28T10:30:00.000Z" }}Success Response (List)
Section titled “Success Response (List)”{ "data": [ { "id": "room_001", "name": "Design Team" }, { "id": "room_002", "name": "Engineering" } ], "pagination": { "page": 1, "limit": 20, "total": 42, "totalPages": 3 }}Error Response
Section titled “Error Response”{ "error": "ValidationError", "message": "Invalid room name: must be between 1 and 100 characters", "details": [ { "field": "name", "issue": "String must contain at least 1 character(s)" } ]}Pagination
Section titled “Pagination”List endpoints support pagination via query parameters:
page(default: 1) - Page number (1-indexed)limit(default: 20, max: 100) - Items per page
Example Request:
GET /api/rooms?page=2&limit=50Response:
{ "data": [...], "pagination": { "page": 2, "limit": 50, "total": 127, "totalPages": 3 }}Route Groups
Section titled “Route Groups”The API is organized into logical route groups:
| Group | Path | Description |
|---|---|---|
| Auth | /api/auth | Magic link authentication, login, logout, session refresh, account deletion |
| Rooms | /api/rooms | Room creation, listing, updates, deletion |
| Sparks | /api/sparks | Spark creation, editing, positioning, deletion |
| Offices | /api/offices | Office management, invites, membership, settings |
| Passport | /api/passport | User profile management, CV import, skills |
| Shelves | /api/shelves | Shelf and link management, ordering |
| Canvas | /api/canvas | Tile position updates, layout computation, collision detection |
| Sync | /api/sync | Server-Sent Events subscription for real-time updates |
| Uploads | /api/uploads | File upload and serving (avatars, attachments) |
| Agora | /api/agora | Video/voice chat token generation, configuration |
| Users | /api/users | User profile and display information |
| Reports | /api/reports | Content reporting (spam, abuse, etc.) |
| Appeals | /api/appeals | Content appeal submission and tracking |
| Widgets | /api/widgets | Widget management, installation, settings |
| MOTD | /api/motd | Message of the day management |
| Health | /health | System health check and service status |
Authentication Endpoints
Section titled “Authentication Endpoints”Request Magic Link
Section titled “Request Magic Link”POST /api/auth/request-loginContent-Type: application/json
{ "email": "[email protected]"}Sends a magic link to the provided email address. Returns success even if email doesn’t exist (prevents email enumeration).
Authorize Login
Section titled “Authorize Login”GET /api/auth/authorize?token=<magic_link_token>Validates the magic link token and sets the session cookie. Redirects to the application on success.
Get Current User
Section titled “Get Current User”GET /api/auth/meReturns the currently authenticated user’s profile.
Response:
{ "data": { "id": "user_abc123", "displayName": "Jane Doe", "avatarUrl": "https://storage.goflowstate.com/avatars/abc123.jpg", "createdAt": "2026-01-15T08:00:00.000Z" }}Logout
Section titled “Logout”POST /api/auth/logoutClears the session cookie and invalidates the JWT token.
Request Account Deletion
Section titled “Request Account Deletion”POST /api/auth/request-deletionInitiates account deletion flow. Sends confirmation email with deletion token.
Confirm Account Deletion
Section titled “Confirm Account Deletion”POST /api/auth/confirm-deletionContent-Type: application/json
{ "token": "<deletion_token>"}Permanently deletes the user account and all associated data.
Room Endpoints
Section titled “Room Endpoints”List Rooms
Section titled “List Rooms”GET /api/rooms?page=1&limit=20Returns all rooms the authenticated user has access to.
Create Room
Section titled “Create Room”POST /api/roomsContent-Type: application/json
{ "name": "Design Team", "description": "Collaborative design workspace", "isPublic": false}Get Room
Section titled “Get Room”GET /api/rooms/:roomIdUpdate Room
Section titled “Update Room”PATCH /api/rooms/:roomIdContent-Type: application/json
{ "name": "Updated Room Name", "description": "New description"}Delete Room
Section titled “Delete Room”DELETE /api/rooms/:roomIdOnly the room creator can delete a room.
Spark Endpoints
Section titled “Spark Endpoints”Create Spark
Section titled “Create Spark”POST /api/sparksContent-Type: application/json
{ "title": "Project Kickoff", "content": "Let's discuss the roadmap for Q2", "roomId": "room_abc123", "x": 100, "y": 200}Update Spark
Section titled “Update Spark”PATCH /api/sparks/:sparkIdContent-Type: application/json
{ "title": "Updated Title", "content": "Updated content"}Move Spark
Section titled “Move Spark”PATCH /api/sparks/:sparkId/positionContent-Type: application/json
{ "x": 300, "y": 400}Delete Spark
Section titled “Delete Spark”DELETE /api/sparks/:sparkIdOffice Endpoints
Section titled “Office Endpoints”List Offices
Section titled “List Offices”GET /api/offices?page=1&limit=20Create Office
Section titled “Create Office”POST /api/officesContent-Type: application/json
{ "name": "Engineering Office", "slug": "engineering", "description": "Daily standup and collaboration space"}Get Office
Section titled “Get Office”GET /api/offices/:officeIdInvite to Office
Section titled “Invite to Office”POST /api/offices/:officeId/invitesContent-Type: application/json
{ "email": "[email protected]", "role": "member"}Roles: owner, admin, member
List Office Members
Section titled “List Office Members”GET /api/offices/:officeId/membersRemove Member
Section titled “Remove Member”DELETE /api/offices/:officeId/members/:userIdCanvas Endpoints
Section titled “Canvas Endpoints”Update Tile Position
Section titled “Update Tile Position”PATCH /api/canvas/positionContent-Type: application/json
{ "tileId": "spark_abc123", "x": 500, "y": 600}Applies snap-to-grid and collision detection automatically.
Batch Update Positions
Section titled “Batch Update Positions”PATCH /api/canvas/positions/batchContent-Type: application/json
{ "updates": [ { "tileId": "spark_001", "x": 100, "y": 100 }, { "tileId": "spark_002", "x": 200, "y": 200 } ]}Compute Layout
Section titled “Compute Layout”POST /api/canvas/layout/computeContent-Type: application/json
{ "roomId": "room_abc123", "algorithm": "force-directed"}Automatically arranges tiles using the specified layout algorithm.
Upload Endpoints
Section titled “Upload Endpoints”Upload File
Section titled “Upload File”POST /api/uploadsContent-Type: multipart/form-data
file: <binary data>type: "avatar" | "attachment"Returns the uploaded file’s URL.
Response:
{ "data": { "url": "https://storage.goflowstate.com/uploads/abc123.jpg", "filename": "profile.jpg", "size": 245678, "mimeType": "image/jpeg" }}Get File
Section titled “Get File”GET /api/uploads/:fileIdServes the uploaded file with appropriate Content-Type headers.
Sync Endpoint
Section titled “Sync Endpoint”Subscribe to Updates
Section titled “Subscribe to Updates”GET /api/sync/subscribeAccept: text/event-streamEstablishes a Server-Sent Events connection for real-time updates. See Real-Time Sync for details.
Health Check
Section titled “Health Check”System Health
Section titled “System Health”GET /healthReturns the health status of all services.
Response:
{ "status": "healthy", "timestamp": "2026-02-28T10:30:00.000Z", "services": { "database": { "status": "healthy", "latency": 12 }, "redis": { "status": "healthy", "latency": 3 }, "storage": { "status": "healthy", "latency": 45 } }, "uptime": 86400}Error Codes
Section titled “Error Codes”The API uses standard HTTP status codes:
| Code | Meaning | Description |
|---|---|---|
| 200 | OK | Request succeeded |
| 201 | Created | Resource created successfully |
| 204 | No Content | Request succeeded with no response body |
| 400 | Bad Request | Invalid request parameters or validation failure |
| 401 | Unauthorized | Missing or invalid authentication |
| 403 | Forbidden | Authenticated but lacking permissions |
| 404 | Not Found | Resource doesn’t exist |
| 409 | Conflict | Resource conflict (duplicate, constraint violation) |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Unexpected server error |
| 503 | Service Unavailable | Service temporarily unavailable |
Rate Limiting
Section titled “Rate Limiting”All endpoints are rate-limited to prevent abuse:
- Default limit: 100 requests per minute per IP
- Auth endpoints: 10 requests per minute per IP
- Upload endpoints: 20 requests per minute per user
When rate limited, the API returns a 429 status with a Retry-After header indicating when to retry.
Rate Limit Headers:
X-RateLimit-Limit: 100X-RateLimit-Remaining: 42X-RateLimit-Reset: 1709118600CORS Policy
Section titled “CORS Policy”The backend enforces CORS based on the CORS_ORIGIN environment variable:
- Production:
https://app.goflowstate.com
Credentials (cookies) are allowed for same-origin requests.
Request Validation
Section titled “Request Validation”All request bodies are validated using Zod schemas. Validation errors return a 400 status with detailed error information:
{ "error": "ValidationError", "message": "Invalid request body", "details": [ { "field": "email", "issue": "Invalid email address" }, { "field": "name", "issue": "String must contain at least 1 character(s)" } ]}Best Practices
Section titled “Best Practices”- Always check response status before parsing the body
- Handle rate limits gracefully by respecting
Retry-Afterheaders - Use pagination for list endpoints to avoid large payloads
- Include error handling for all API calls
- Validate input client-side before sending requests (reduces round trips)
- Use relative URLs (
/api/*) in client code (works with the proxy) - Never expose
SERVER_API_URLto the browser (server-side only)