Site

Almost every route runs through a security wrapper (apiSecurity.* / withSecurity()) that enforces a rate limit, method/content-type validation, and a body-size cap. Keys are isolated by HTTP method and canonical route pattern; dynamic IDs, query strings, and unrelated endpoints cannot mint or consume one another's buckets. Tiers: public() 30/min no-auth · authenticated() 30/min · admin() 120/min for reads and 30/min for ordinary mutations + is_admin · sensitive admin actions 5/min · ai() 10/min · webhook() 100/min + signature. State-changing requests (POST/PATCH/PUT/DELETE) additionally require CSRF (Origin/Referer + token). Errors return a typed envelope — { "error": "message", "code": "ERROR_CODE" } — with codes from core/*/error-codes.ts (never raw internals). Feature-gated domains (referrals, affiliates, error logs) return 404 when disabled, not 403.

Exceptions to the wrapper

The job runner /api/jobs/run keeps distinct Bearer and admin authentication paths. The Stripe, Lemon Squeezy and Paddle webhook routes retain their bounded raw-body signature boundaries. These are the wrapper exceptions tracked by the API audit. /api/health uses the standard wrapper with the relaxed public tier.

Authentication

Endpoint Method Description
/api/auth/magic-link POST Send magic-link email (link + 6-digit OTP code)
/api/auth/verify-otp POST Verify the 6-digit code from the magic-link email (alternate path to clicking the link)
/api/auth/me GET Return minimal sanitized auth/profile/workspace context for marketing-shell client hydration. Read-only, relaxed rate limit, Cache-Control: private, no-store.
/api/auth/logout POST Sign out user (logs activity)
/[locale]/callback GET OAuth/Magic link callback

Billing

Endpoint Method Description
/api/billing/checkout POST Create a checkout with the single server-configured payment provider
/api/billing/checkout/status/exchange/[locale] GET Exchange the opaque return bearer for a short HttpOnly cookie, then redirect to a clean localized URL
/api/billing/checkout/status GET Read only the generic durable checkout-attempt status from the HttpOnly hand-off
/api/billing/portal POST Open the portal for the provider persisted on the subscription
/api/billing/license-checkout POST Create license one-shot checkout
/api/billing/subscribe-free POST Create free subscription (owner-only, all modes)
/api/billing/end-trial POST End the provider trial synchronously; payment and subscription proof converge from signed webhooks
/api/billing/current-plan GET Read the active/trialing subscription of the actor's first billing Account (workspace in B2B, personal in B2C), without license fallback. Private, non-cacheable; DB failures return 500 rather than an empty plan.
/api/billing/webhooks/stripe POST Signed Stripe webhook handler
/api/billing/webhooks/lemon-squeezy POST Signed Lemon Squeezy webhook handler, active only when configured
/api/billing/webhooks/paddle POST Signed Paddle webhook handler, active only when configured

AI

/api/ai/stream uses apiSecurity.ai for Origin/Referer and current-session validation, configured session finalization and active identity resolution. Its two canonical-actor AI buckets precede the bounded JSON parser, preserving the existing Origin-only CSRF policy without a double-submit requirement. SSE and errors are private/no-store. Auth unavailability returns 503; database gate failures return 500 AI_GATE_UNAVAILABLE. Account membership and ai:use remain required, with sessions shared within the Account.

Endpoint Method Description
/api/ai/stream POST Streaming AI response (SSE)
/api/ai/config GET AI configuration

Chat

Endpoint Method Description
/api/chat/sessions GET List sessions
/api/chat/sessions POST Create session
/api/chat/sessions/[id] GET Get session with messages
/api/chat/sessions/[id] PATCH Update session
/api/chat/sessions/[id] DELETE Delete session

User

Endpoint Method Description
/api/user/export-data GET GDPR data export (multi-section CSV)
/api/user/sessions GET List recent login/logout activity
/api/user/sessions DELETE Revoke all other sessions
/api/user/onboarding POST Validate and store onboarding profile fields server-side

Invitations

Endpoint Method Description
/api/invitations/accept POST Accept workspace invitation
/api/invitations/resend POST Resend invitation email (owner/admin)
/api/invitations/revoke POST Revoke a pending workspace invitation (owner/admin)

Organization

Endpoint Method Description
/api/org/schedule-deletion POST Schedule workspace deletion (owner, destructive step-up, configured grace)
/api/org/cancel-deletion POST Cancel pending workspace deletion (owner, idempotent)
/api/org/settings PATCH Update workspace name/slug through a validated server boundary
/api/org/security-policy?accountId=uuid GET Read workspace authentication policy and MFA coverage (owner/admin)
/api/org/security-policy PUT Update authentication policy (owner/admin, adminEscalation step-up)

Settings and security-policy handlers require the canonical application actor before parsing or domain effects. Membership denial returns 403; malformed JSON/input returns 400, identity unavailability 503, and database failures 500. Responses are private/no-store. PATCH settings retains its strict rate limit without a new step-up; PUT security-policy retains strict limits and its existing step-up. Policy reads and writes use canonical Account authority through the shared Prisma runtime; mutation audits commit atomically with the change. MFA coverage is an optional, bounded provider diagnostic requested separately from the settings page. Unavailable coverage never relaxes the stored policy. New mandatory policies must include a factor the application can enroll (currently TOTP); passwordless passkeys are separate.

Both organization deletion POSTs accept strictly { accountId }. Remove the former userId field: identity comes from the verified server session, not the request body. They require the configured owner role on a workspace Account; personal Accounts are rejected. Scheduling uses complianceConfig.deletionGraceDays (default 30) and preserves member identities. Existing active requests return 400, including concurrent duplicates. Cancellation removes only pending requests; a 200 no-op does not stop an already processing worker. Both responses are private/no-store, with 400 for invalid input, 403 for denied ownership/type, 503 for unavailable application identity and 500 for database failure.

CMS (Admin)

Endpoint Method Description
/api/admin/cms/pages GET List all CMS pages
/api/admin/cms/pages POST Create page
/api/admin/cms/pages PATCH Update page
/api/admin/cms/pages?id=xxx DELETE Delete page
/api/admin/cms/blocks GET List all blocks
/api/admin/cms/blocks POST Create block
/api/admin/cms/blocks PATCH Update block
/api/admin/cms/blocks?id=xxx DELETE Delete block (query param is id, the block's UUID)
/api/admin/cms/media GET List media files
/api/admin/cms/media POST Upload file
/api/admin/cms/media PATCH Rename file
/api/admin/cms/media?path=xxx DELETE Delete file
/api/admin/cms/categories GET List all blog categories (with usage counts)
/api/admin/cms/categories POST Create category
/api/admin/cms/categories/[id] PATCH Update category
/api/admin/cms/categories/[id] DELETE Delete category
/api/admin/cms/tags GET List all blog tags (with usage counts)
/api/admin/cms/tags POST Create tag
/api/admin/cms/tags/[id] PATCH Update tag
/api/admin/cms/tags/[id] DELETE Delete tag

Jobs

Endpoint Method Description
/api/jobs/run POST Execute a job (cron/manual/api). Dual auth: Authorization: Bearer JOBS_SECRET_KEY (constant-time compared, webhook rate limit) OR an admin user session with Origin/CSRF validation and the 30/min/route admin-write limit.
/api/admin/jobs GET List up to 200 matching jobs through shared Prisma with canonical admin checks. Optional enabled/executor filters apply before the bound; overflow fails explicitly. Returns only id, name, description, executor, cron_expression, is_enabled, last_run_at and last_run_status, ordered by name then id. Configuration and native cron identifiers are excluded.
/api/admin/jobs POST Create a job through shared Prisma with canonical admin authority, CSRF, strict limits and adminEscalation. Input is bounded to one MiB; job, native scheduling and audit commit together. Returns 201 with only job.id, 409 for a name conflict or 503 for unsupported native capabilities. No direct authenticated table-creation path exists.
/api/admin/jobs/[jobId] GET Canonical-admin shared Prisma snapshot of job detail and the latest 20 executions. Returns 15 job fields and nine run fields, with a two-MiB UTF-8 ceiling and stable timestamp/id ordering. Only an absent job returns 404; database and oversized-result failures remain explicit. Native cron and triggering-user identifiers are excluded.
/api/admin/jobs/[jobId] PATCH Update only supplied job fields through shared Prisma, with canonical admin authority, CSRF, strict limits and adminEscalation. Omitted fields keep their stored values; an empty patch is rejected. Configuration/description changes alone do not reprogram cron. Returns 200 with only job.id, 404 for an absent job, 409 for a name conflict or 503 for unsupported native capabilities. Job, scheduler and audit changes roll back together on failure.
/api/admin/jobs/[jobId] DELETE Delete a job, its execution history and matching native schedules atomically with an audit through shared Prisma. Requires canonical admin authority, CSRF and the adminEscalation step-up policy. An absent job still returns success. Unsupported native targets return 503; failures roll back. No direct authenticated table-deletion path exists.
/api/admin/jobs/handlers GET List code handlers and up to 200 database handlers through shared Prisma; overflow fails explicitly. Excludes webhook headers and authentication values; private/no-store, canonical identity required.
/api/admin/jobs/handlers POST Create a handler through shared Prisma with adminEscalation and atomic audit; returns only handler.id (201).
/api/admin/jobs/handlers/[handlerId] GET Get 12 editor fields with credential/header presence flags; no stored secret, headers or timestamps. Private/no-store; missing handler returns 404.
/api/admin/jobs/handlers/[handlerId] PATCH Update supplied fields only, with adminEscalation and atomic audit; returns handler.id. Names are immutable; omitted credentials/headers are preserved, null credential or empty header object clears them.
/api/admin/jobs/handlers/[handlerId] DELETE Delete through shared Prisma with adminEscalation and atomic audit. Referenced handlers return 400 with JOBS_ADMIN_HANDLER_DELETE_IN_USE; absent handlers remain successful. Private/no-store.

Notifications

Endpoint Method Description
/api/notifications GET Fetch latest 50 notifications
/api/notifications PATCH Mark notifications as read

API Keys

All methods authorize the canonical application actor as an Account owner/admin. POST and DELETE retain strict rate limiting, CSRF and credentialChange step-up. Handler responses are private/no-store; missing application identity returns 503 before management effects. See API Keys for metadata, failure and one-time secret contracts.

Endpoint Method Description
/api/account/api-keys GET List API keys
/api/account/api-keys POST Create API key
/api/account/api-keys DELETE Revoke API key

Knowledge Base / Documents (RAG)

Endpoint Method Description
/api/documents GET List documents
/api/documents POST Upload document (multipart). Triggers async processing with credit deduction.
/api/documents DELETE Delete document + chunks + storage
/api/documents/[id] GET Document details + chunks

Changelog (Admin)

Endpoint Method Description
/api/admin/changelog GET List changelog entries
/api/admin/changelog POST Create changelog entry
/api/admin/changelog PATCH Update changelog entry
/api/admin/changelog DELETE Delete changelog entry

Referrals

Account-centric referral program. Every endpoint returns 404 when REFERRAL_ENABLED is off (surface invisibility).

EndpointMethodSecurityDescription
/api/referral/codeGETauthenticated · standardLazy-create the account's active referral code
/api/referral/statsGETauthenticated · standardDashboard counters
/api/referral/listGETauthenticated · standardCursor-paginated referrals
/api/referral/applyPOSTauthenticated + CSRF · strictManually apply a referral code
/api/referral/attributePOSTauthenticated + CSRF · strictCookie-based attribution (called from onboarding)
/[locale]/refer/[code]GETpublic · relaxedTracking redirect — sets the bsk_ref cookie
/api/admin/referralsGETadmin · 120/min/routeFilterable list
/api/admin/referrals/statsGETadmin · 120/min/routeAggregate KPIs
/api/admin/referrals/[id]GETadmin · 120/min/routeDetail + audit trail
/api/admin/referrals/[id]/reversePOSTadmin + CSRF · strictClawback granted credits
/api/admin/referrals/[id]/rejectPOSTadmin + CSRF · strictReject a pending referral
/api/admin/referral-codesGETadmin · 120/min/routeCodes + usage counts
/api/admin/referral-codes/[id]/deactivatePOSTadmin + CSRF · strictDeactivate a code

Affiliates

Cash-commission partner program (distinct from referrals). Every endpoint returns 404 when AFFILIATES_ENABLED is off.

EndpointMethodSecurityDescription
/api/affiliates/applyPOSTauthenticated + CSRF · strictSubmit affiliate application (Zod-validated pitch + URL)
/api/affiliates/attributePOSTauthenticated + CSRF · strictCookie-driven attribution (non-blocking)
/[locale]/affiliate/[code]GETpublic · relaxedTracking redirect — sets bsk_aff cookie + records click
/api/admin/affiliates/applications/[id]/approvePOSTadmin + CSRF · strictApprove at a tier (defaults to config defaultTierSlug)
/api/admin/affiliates/applications/[id]/rejectPOSTadmin + CSRF · strictReject with sanitized reason
/api/admin/affiliates/conversions/[id]/reversePOSTadmin + CSRF · strictManual conversion clawback (fraud / dispute)

Push Notifications

EndpointMethodSecurityDescription
/api/push/subscribePOSTauthenticatedRegister a push subscription (with device name)
/api/push/unsubscribePOSTauthenticatedRemove a push subscription
/api/push/vapid-keyGETpublicFetch the VAPID public key (browser subscription)
/api/push/preferencesGETauthenticatedGet notification preferences
/api/push/preferencesPATCHauthenticatedUpdate preferences / quiet hours
/api/push/trackPOSTauthenticatedTrack interaction (click / dismiss / view)

Error Logs (Admin)

EndpointMethodSecurityDescription
/api/admin/logsGETadmin read · 120/min/routeCursor-paginated error logs (Zod-validated filters). 404 when LOGS_ENABLED is off. Read-only — no write endpoints.

Public & Misc

EndpointMethodSecurityDescription
/api/contactPOSTpublic + Turnstile · contact (3/min)Contact form (queues on provider failure)
/api/newsletter/subscribePOSTpublic + Turnstile · standardNewsletter signup
/api/newsletter/unsubscribePOSTauthenticated + CSRF · strictUnsubscribe the current authenticated user from the newsletter. Public email links need a separate signed-token route.
/api/localePOSTpublicPersist locale preference cookie
/api/healthGETpublic · relaxedHealth check (use for uptime monitoring / load-balancer probes)

Admin & Platform

Super-admin surface — all admin-gated (is_admin) and route-scoped: reads allow 120/min/route, ordinary mutations 30/min/route, and sensitive access, billing, role, cron, reversal, or destructive actions retain 5/min/route. State-changing calls require CSRF; sensitive actions also require recent auth where applicable and write immutable audit rows.

EndpointMethodSecurityDescription
/api/admin/usersPOST PATCH DELETEadminCreate / update (admin toggle, edit) / delete users
/api/admin/organizationsGET POST PATCH DELETEadmin + CSRF + recent-auth for writesWorkspace account CRUD — list, create, update, delete
/api/admin/subscriptionsGET PATCHadmin + CSRF + recent-auth for mutationsSubscription overview + admin updates (status, plan, credit grant)
/api/admin/licensesGET PATCHadmin + CSRFLicense list + admin revoke / extend (delegates to core/licenses/mutations.ts)
/api/admin/roles · /api/admin/roles/[roleId]GET POST PATCH DELETEadmin + CSRFDynamic role CRUD (is_system protected)
/api/admin/settingsGET PATCHadmin + CSRF + recent-auth for sensitive changesPlatform settings (app_settings) and log purges
/api/admin/jobs/stats · /api/admin/jobs/[jobId]/runsGETcanonical adminShared Prisma 24-hour SQL aggregates / paginated run metadata and exact total; page size up to 100 and offset up to 10,000
/api/admin/cron/[jobid]/togglePOSTadmin + CSRFEnable/disable one native cron and commit its audit atomically; missing target returns 404
/api/admin/cron/[jobid]/unschedulePOSTadmin + CSRF + recent authRemove one native cron, clear its application reference and audit atomically; missing target returns ok: false
/api/admin/cron/purge-orphansPOSTadmin + CSRF + recent authAtomically purge matching orphans after checking at most 200 native entries; any failure rolls back the batch and audit

Non-admin routes previously grouped here have moved to their correct sections: /api/billing/current-plan → Billing · /api/locale → Public & Misc · /api/org/cancel-deletion → Organizations.