Reference
API Reference — All Endpoints
Workspace-scoped, tenancy-guarded REST API. Every request goes through session + DAL. Covers auth, all routes, examples, and how to extend — beginner-friendly with curl snippets.
Authentication & Tenancy (Beginner)
Beginner: How Auth Works
Session cookie signed with
SESSION_SECRET (like login token). Every request calls getCurrentUser() → if no session, 401 → redirect /login. Then workspaceId from URL (/w/[workspace]) is validated via DAL — you can only access your workspace's rows.// lib/auth/dal.ts
export async function getCurrentUser() {
const session = await getSession() // decrypt cookie
if (!session) throw new Error("Unauthorized")
return db.user.findUnique({ where: { id: session.userId } })
}
export async function requireWorkspaceMember(workspaceId: string, userId: string) {
const member = await db.workspace_members.findFirst({ where: { workspace_id: workspaceId, user_id: userId }})
if (!member) throw new Error("Forbidden")
}
// In every route handler (app/api/*):
export async function POST(req, { params }: { params: { workspace: string } }) {
const user = await getCurrentUser()
await requireWorkspaceMember(params.workspace, user.id)
// now safe to query with workspace_id = params.workspace
}All handlers use lib/db/tenant.ts wrapper. Check: npm run check:tenancy scans lib/** for missing workspace_id in findMany/update/delete.
Inbox & Capture Endpoints
POST /api/inbox # or /api/w/:workspace/inbox (scoped)
Headers: Cookie: session=...
Body: { content: "Make 3 reels for GB by Friday", source_type: "text" }
Response: { inbox_item: { id, workspace_id, content, type }, job: { id, status: "queued" } }
POST /api/inbox/upload
Body: multipart/form-data (file, source_type="image"|"file")
Response: { file: { id, storage_path }, inbox_item: { id } }
POST /api/webhooks/inbox
Headers: X-Webhook-Secret: WEBHOOK_SECRET
Body: { content, source: "gmail"|"telegram" }
Use: Gmail push Pub/Sub → verifies WEBHOOK_SECRETTasks, Projects, Clients
GET /api/tasks?workspace=MyStudio&status=InProgress
POST /api/tasks Body: { title, project_id, due_date, priority }
GET /api/tasks/:id
PATCH /api/tasks/:id Body: { status, priority, due_date, estimated_minutes }
DELETE /api/tasks/:id → archive (soft delete, sets is_archived)
POST /api/tasks/:id/start Body: {} → creates work_sessions
Example curl:
curl -X POST http://localhost:3000/api/tasks \
-H "Content-Type: application/json" \
-b cookie.txt \
-d '{"workspace_id":"MyStudio","title":"Create reel","project_id":"proj_123","due_date":"2025-12-06"}'Finance Endpoints
POST /api/finance/import
Body: multipart/form-data (file: bank statement PDF, workspace_id)
Response: { import: { id, status: "parsing", job_id } }
GET /api/finance/transactions?workspace=MyStudio&from=2025-11-01&to=2025-11-30
Response: { transactions: [{ id, date, description, amountMinor, category, confidence }] }
PATCH /api/finance/transactions/:id Body: { category }
GET /api/finance/subscriptions?workspace=MyStudio
Response: { subscriptions: [{ name: "Netflix", amountMinor: 64900, next_expected_payment, frequency }] }
GET /api/finance/invoices?workspace=MyStudio
POST /api/finance/invoices Body: { title, amountMinor, currency, issuer, file_id }Calendar & Files
GET /api/calendar/events?workspace=MyStudio&from=2025-12-01&to=2025-12-07
Response: { events: [{ id, title, starts_at, ends_at, attendees }] }
POST /api/calendar/events Body: { title, starts_at, ends_at, attendees: [] }
POST /api/integrations/calendar/sync → job: calendar_sync
Linked commitment: GET /api/calendar/events/:id/context
Response: { project, tasks: [...], files: [...] }Agent & Search
POST /api/agent/:workspace
Body: { message: "What should I do now?" }
Headers: Cookie
Flow: Orchestrator → Task Agent + Calendar → scored recommendation
Response: { answer: "Do GB Reel now (45m, due today, score 88)", actions: [{ tool, args }] }
POST /api/assistant/ask (legacy)
Body: { query, workspace_id }
Response: { answer, sources: [{ type, id, snippet }] }
POST /api/search (as above)Integrations & Realtime
GET /api/integrations/gmail/connect?workspace=MyStudio
→ Redirect to Google OAuth
GET /api/integrations/gmail/callback?code=...&state=workspace:MyStudio
→ Exchange, encrypt, store vault_items, redirect to /w/MyStudio/settings/integrations
Same for /drive/callback, /calendar/callback
GET /api/integrations/:provider/status?workspace=MyStudio
Response: { connected: true, email: "[email protected]" }Error Codes & Rate Limits
| Code | When | Fix |
|---|---|---|
| 401 | No session or expired | Login again |
| 403 | Workspace not member | Check workspace slug |
| 400 | Zod validation failed | Check body fields, see error.issues |
| 429 | Rate limit (100 req/min) | Backoff, retry after header |
Example: Full Flow with curl
# 1. Login (get cookie)
curl -c cookie.txt -X POST http://localhost:3000/api/login -d "[email protected]&password=..."
# 2. Create capture
curl -b cookie.txt -X POST http://localhost:3000/api/inbox \
-H "Content-Type: application/json" \
-d '{"workspace_id":"MyStudio","content":"Make 3 reels for GB before Saturday","source_type":"text"}'
# → inbox_item id: inbox_123
# 3. Extract
curl -b cookie.txt -X POST http://localhost:3000/api/inbox/inbox_123/extract | jq
# 4. Confirm create tasks
curl -b cookie.txt -X POST http://localhost:3000/api/inbox/inbox_123/confirm \
-H "Content-Type: application/json" \
-d '{"tasks":[{"title":"Reel 1"},{"title":"Reel 2"}]}'
# 5. List tasks
curl -b cookie.txt http://localhost:3000/api/tasks?workspace=MyStudio | jq
# 6. Ask agent
curl -b cookie.txt -X POST http://localhost:3000/api/agent/MyStudio \
-H "Content-Type: application/json" \
-d '{"message":"What should I do now?"}' | jqBeginner: How to Add a New Endpoint
Create file
app/api/my-feature/route.ts → export GET/POST → validate session + workspace via DAL → use db from lib/db → return NextResponse.json. Add to docs here.