DLRSDLRS
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_SECRET

Tasks, 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: [...] }
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

CodeWhenFix
401No session or expiredLogin again
403Workspace not memberCheck workspace slug
400Zod validation failedCheck body fields, see error.issues
429Rate 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?"}' | jq

Beginner: 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.

On this page