Keep user-owned integrations flexible (GitHub, model providers, webhooks)
Keep operational state inspectable (job status, preview checks, webhook deliveries)
Most routes live under app/api/** and return JSON. The frontend consumes them from localized pages, but API paths themselves are not locale-prefixed.
Authentication model
User-scoped routes
Many routes require an authenticated user session and are scoped to the current user (for example integrations settings, project/session data, and builder flows).
Service-role operations
Some internal writes use service-role access server-side only (for example operational tables and background-style writes). Never expose service-role keys to the browser.
Third-party credentials
Motivd can use:
Per-user GitHub OAuth tokens stored in github_oauth_connections
Project environment variables (fallback) for server actions
Per-user model provider keys in integrations settings
Core route groups
Builder lifecycle
POST /api/projects/create — create project + brief shell from onboarding
POST /api/prd/generate — generate PRD from onboarding context
PATCH /api/projects/prd-review — persist per-project PRD approval state (reviewed true/false) for returning users
POST /api/projects/link-repo — associate a project with a GitHub repo/preview URL; existing-repo connects now sync the project name to the repo name unless renameProjectToRepo: false is sent
PATCH /api/projects/update-sandbox — update a project’s editable sandbox slug (body: projectId, sandboxSlug); slug is globally unique and used in /s/{sandboxSlug}
PATCH /api/projects/[projectId] — update project (body: optional name, optional backend_type, optional previewUrl as string or null to clear); renames the project and derives slug when name is sent; owner only; 409 if name/slug already exists for user
DELETE /api/projects/[projectId] — delete project and cascade (briefs, PRD, repos, sessions, stars, files); owner only
POST /api/build — start a build/deploy job
GET /api/build?jobId=... — poll build/deploy lifecycle status and logs
Workspace and sessions
GET /api/agent/sessions — list/load sessions by project
POST /api/agent/sessions — create a new session
POST /api/agent/chat — send a message and persist assistant reply; for authenticated project sessions, the route now injects a structured workspace context block (project summary, brief, PRD excerpt, backend type, preview URL, and best-effort top-level repo paths) before model generation. Workspace tool read_file / write_file on path PRD.md update builder_prds (the PRD step), not only builder_project_files. Covered by the global Anthropic guardrail: when the active 5-hour spend bucket is exhausted, returns 429 with { error, code: "AI_RATE_LIMIT_ACTIVE", aiRateLimit }.
POST /api/agent/sdk — run Claude Agent SDK / Claude Code style workspace execution as an SSE stream. Covered by the same 5-hour Anthropic guardrail and blocked up front with the same 429 contract when the bucket is exhausted.
GET /api/projects/[projectId]/tasks — load the project task tree, open human requests, runs, events, summary counts, and bootstrap readiness
POST /api/projects/[projectId]/tasks — create a project task manually (or from AI/agent sources) with optional dependencies and initial status
PATCH /api/projects/[projectId]/tasks/[taskId] — update a task title, description, status, priority, automation mode, and dependency links
POST /api/projects/[projectId]/tasks/bootstrap — preview or confirm a PRD-derived suggested task plan (mode: preview | confirm)
POST /api/projects/[projectId]/tasks/[taskId]/run — start a task planning/execution/validation run and optionally create a blocking approval request before execution
POST /api/projects/[projectId]/tasks/[taskId]/human-request — create a blocking human checkpoint (approval, secret, single_choice, multi_choice, text)
POST /api/projects/[projectId]/tasks/[taskId]/human-request/[requestId]/answer — submit a response to a human checkpoint and let the task recompute readiness
POST /api/projects/[projectId]/tasks/[taskId]/approve — resolve open approval gates and reopen the task for execution
POST /api/projects/[projectId]/tasks/[taskId]/reject — reject an approval gate and keep the task in a human-required state
GET /api/projects/[projectId]/agent-schedules — list recurring agent automations for the project (owner)
— create a schedule (, , : | | , , , optional 0–6 for weekly, optional 1–28 for monthly, , optional , )
PATCH /api/projects/[projectId]/agent-schedules/[scheduleId] — update schedule fields (same shape as create)
DELETE /api/projects/[projectId]/agent-schedules/[scheduleId] — remove a schedule
GET|POST /api/cron/agent-schedules — Vercel Cron (hourly): runs one due schedule, executes the Claude Agent SDK in the chosen session under the project owner’s AI budget; requires CRON_SECRET when set
Preview and sandbox
GET /api/preview/discover?repo=owner/name — discover likely preview URL from providers
GET /api/preview/status?url=... — check preview reachability; JSON state includes online, not_ready, restricted, gone (HTTP 404 — deployment removed/expired), or error
GET /api/projects/[projectId]/preview/session — inspect the current workspace-preview runtime status (workspace source of truth = builder_project_files)
POST /api/projects/[projectId]/preview/session — ensure the workspace-preview runtime exists and is synced to the latest saved snapshot
POST /api/projects/[projectId]/preview/session/restart — restart the workspace-preview runtime after a failure or dependency change
GET /api/projects/[projectId]/preview/proxy/[...path] — same-origin proxy for the workspace-preview runtime iframe and its assets/data requests
GET /s/[sandboxSlug] — redirect to the project’s preview URL by globally unique sandbox slug (e.g. motivd.com/s/todo-spark); 404 if no preview URL set
GET /sandbox/[projectId] — redirect to the project’s preview URL by project UUID (fallback when sandbox slug is not used)
Version history (workspace)
Owner-scoped; drives the workspace History panel (Motivd Cloud snapshots + GitHub commits when a repo is linked).
GET /api/projects/[projectId]/versions — list Motivd Cloud snapshots, newest first (id, description, is_bookmarked, created_at, message_id)
POST /api/projects/[projectId]/versions — create a snapshot from current builder_project_files; body { description: string, messageId?: string }
PATCH /api/projects/[projectId]/versions/[versionId] — update bookmark or description; body { is_bookmarked?: boolean, description?: string } (at least one field required)
POST /api/projects/[projectId]/versions/[versionId]/revert — restore files from that snapshot into builder_project_files; inserts a before revert snapshot first; JSON response includes beforeRevertId, filesRestored, restoredVersion description
GET /api/projects/[projectId]/git/commits — commit/snapshot timeline; query source: all (merged GitHub + Motivd Cloud when a repo is linked), github (GitHub only), or snapshots (default: Motivd Cloud snapshots only); for GitHub, branch (default main), page, per_page (max 100)
PATCH /api/projects/[projectId]/git/commits/[ref]/bookmark — per-user GitHub commit bookmark; body { bookmarked: boolean }; ref must be a full 40-character commit SHA
GET|POST /api/settings/integrations/webhooks/deliveries — supports optional locale (en | fr) so delivery error messages are localized in responses
POST /api/settings/integrations/webhooks/test — supports optional locale (en | fr) so guardrail/test error messages are localized in responses
Repo + utility routes
POST /api/repos/create — create GitHub repository
POST /api/repos/connect — validate/connect existing repository
POST /api/brief/extract — AI-assisted brief extraction from a founder prompt; returns optional JSON fields (appName, audience, mvpFeatures, targetPlatforms, integrations, repoSetupPreference, deploymentNotes, constraints, contentNotes) plus aiRateLimit on success and the shared AI_RATE_LIMIT_ACTIVE 429 contract when blocked. The builder caches results in sessionStorage keyed by a hash of the prompt to avoid repeat calls for the same idea text.
GET /api/reference-url-analyze?url=... — AI-assisted website reference analysis; returns analysis plus aiRateLimit on success and the shared 429 contract when blocked
POST /api/faq/ask — streamed FAQ follow-up assistant; covered by the same 5-hour Anthropic guardrail
POST /api/prd/generate — generate/beautify a PRD; returns prd, brief, beautified, and aiRateLimit when the Anthropic path runs
POST /api/releases/drafts/[id]/generate-draft — admin-only AI draft generation for release content; covered by the same guardrail
POST /api/telemetry/repo-handoff — internal builder telemetry sink for repo→workspace handoff metrics (writes structured payloads to site_visits under /_telemetry/repo-handoff for admin reporting, including optional projectId correlation used by admin incident drilldown)
GET|POST /api/telemetry/repo-handoff-alerts — hourly policy evaluator for repo-handoff telemetry; checks recent metrics against thresholds and emits Slack/email alerts when prep failure rate or median open-workspace time crosses limits. Includes cooldown + escalation logic backed by internal policy-run telemetry (/_telemetry/repo-handoff-alert-policy) to reduce repeated noise while still escalating persistent regressions; those policy-run telemetry rows are consumed in /[locale]/admin for suppression/streak/last-alert visibility.
GET|POST /api/telemetry/repo-handoff-alerts/weekly — weekly policy evaluator with longer window and separate thresholds/cadence settings for trend-level alerting
POST /api/telemetry/repo-handoff-alerts/test-channel — admin-only channel check endpoint to send a manual Slack or email test delivery from the admin remediation panel (for on-call validation during notification outages). Successful and failed checks are persisted to internal telemetry (/_telemetry/repo-handoff-alert-channel-test) so admin can show per-channel last successful test/failure context.
— admin-only (session + ); paginated directory. Query: , , optional (email / name / handle / repo substring), , ( | ), (returns for the user map, up to 500 pins). With no search or segment/actor filters, listing uses Supabase pages only; when any of those filters is set, the handler scans up to 50 × 250 auth users server-side (returns , when the cap may truncate). Joins and ; returns activity (with emoji + label), (most recent by ), repos, , map coordinates, , and suggested per segment.
GET /api/admin/investors — admin-only; paginated investor directory. Query: page, perPage, optional (ilike across email, name, company, notes, city, country), ( | ), ( | ), (returns , up to 500 geocoded rows for the admin map).
GET /api/admin/partners — same contract as investors (mapPartners when includeMap=1).
GET /api/admin/tribe-v2/status — admin-only; returns whether TRIBE_V2_WORKER_URL + TRIBE_V2_WORKER_SECRET are set, plus official TRIBE v2 links and a short CC BY-NC license reminder.
POST /api/admin/tribe-v2/analyze — admin-only; multipart/form-data with image (PNG/JPEG/WebP screenshot, max 8MB), optional page_label, and context (motivd | founder_app). Forwards to the configured worker when present. See docs/TRIBE_V2.md.
GET /api/admin/forecasting/daily — admin-only; query days (7–730, default 180), optional metricKeys (comma-separated), segmentKey, segmentValue. Returns rows from the internal forecasting_metric_daily warehouse table for forecasting QA and future admin reporting.
POST /api/admin/forecasting/refresh — admin-only; body accepts either days or exact startDate + endDate (YYYY-MM-DD) and triggers a bounded rebuild of forecasting_metric_daily for internal QA without exposing the cron secret.
GET /api/admin/support-inbox/threads — admin-only; list inbound threads. Query: optional status (open | needs_human | replied | closed), to (substring match on to_address), (subject/from search), (default 50, max 100),
GET /api/admin/support-inbox/threads/[threadId] — admin-only; thread plus ordered messages
POST /api/admin/support-inbox/reply — admin-only; body threadId, text, optional html; sends via Resend with Message-ID / In-Reply-To / References for threading
POST /api/admin/support-inbox/compose — admin-only; JSON { to, subject, text }. Sends a new outbound support email via Resend, creates support_inbound_threads + outbound support_inbound_messages (same from-address semantics as …/reply). Used by Admin → Support Compose.
POST /api/admin/support-inbox/ai-draft — admin-only; body threadId, optional notifyEscalation; optional env SUPPORT_AI_BRAND_CONTEXT, SUPPORT_AI_KNOWN_ISSUES_JSON; can notify ADMIN_EMAIL when the model returns the fixed escalation phrase (see .env.example)
Inbound email (Resend → support inbox)
POST /api/webhooks/resend/inbound — provider webhook for Resend email.received; verifies Svix signature with RESEND_WEBHOOK_SECRET; not end-user session auth. Ingests into support inbound tables (see project overview / .env.example for MX + operational setup).
Referral
GET /api/referral/summary?locale=en|fr — authenticated session only. Returns JSON { referralUrl, loadError, signedUp, converted, creditsEarned, inviteSlots, invitesRemaining } for the referral modal, workspace invite banner, and tooling. Responds 401 when not signed in.
Stripe
POST /api/stripe/checkout — create Stripe Checkout Session for plan subscriptions (recurring prices) and one-time payments (e.g. credit top-ups, gift cards). Request body can include priceId, planSlug, locale, returnPath, embed, and optional idempotencyKey; also accepts x-idempotency-key header. Supports locale-safe returnPath for post-checkout redirect (EN/FR).
POST /api/webhooks/stripe — Stripe webhook endpoint. Configure in Stripe Dashboard (or use Stripe CLI locally: stripe listen --forward-to localhost:3000/api/webhooks/stripe). Handles subscription and one-time payment events; set STRIPE_WEBHOOK_SECRET to the endpoint’s signing secret.
GET|POST /api/cron/forecasting-refresh — internal cron route; rebuilds forecasting_metric_daily from trustworthy source tables over a bounded date window. Protected by CRON_SECRET when set; accepts either days or exact startDate + endDate (YYYY-MM-DD).
Upload and utility
POST /api/upload — handle user file upload flows
POST /api/transcribe — voice transcription (Groq Whisper). Requires GROQ_API_KEY; returns 503 with Transcription not configured when unset (hero shows a toast instead of surfacing a console error).
POST /api/feature-request — submit product/demo/ops requests (used by demo, gift-card redemption, and student verification intake forms)
GET /api/db/status — operational database status check
Webhook event contract
Configured integration webhooks can receive lifecycle events such as:
build.started
build.completed
build.failed
deployment.ready
deployment.failed
workspace.approval.requested
workspace.approval.approved
workspace.approval.rejected
Delivery attempts are tracked in integration_webhook_deliveries with response status, body, duration, retry metadata, and timestamps.
Per-endpoint controls support optional:
rate_limit_per_minute
failure_backoff_seconds
Validation note: webhook guardrail numeric inputs are strict integers (for example, 1.5 or 4abc are rejected with 400 instead of being coerced).
Error handling conventions
At a high level, routes follow these patterns:
2xx for successful operations with structured JSON
Preserve user flow whenever possible (show retry paths on transient failures)
Keep polling responses stable for build lifecycle UIs
For malformed JSON payloads on mutation routes, return 400 with a stable invalid-body message (localized EN/FR where locale is available) instead of falling back to generic 500 handlers.
Capture unexpected server/runtime failures in Sentry so production regressions are visible quickly.
For cron endpoints, keep CRON_SECRET auth checks enabled in production and report delivery failures in response payloads for quick debugging.
API update checklist
When adding or changing a route:
Document the route purpose in this file (and related product docs if user-visible).
Confirm auth scope (user token, service role, or external provider token).
Add/adjust webhook emission and delivery logs if the event model changes.
Update tests (smoke/e2e/unit) for behavior and regressions.
Keep docs pages and sitemap entries aligned for multilingual SEO visibility.