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)
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)
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).
Endpoint
Method
Security
Description
/api/referral/code
GET
authenticated · standard
Lazy-create the account's active referral code
/api/referral/stats
GET
authenticated · standard
Dashboard counters
/api/referral/list
GET
authenticated · standard
Cursor-paginated referrals
/api/referral/apply
POST
authenticated + CSRF · strict
Manually apply a referral code
/api/referral/attribute
POST
authenticated + CSRF · strict
Cookie-based attribution (called from onboarding)
/[locale]/refer/[code]
GET
public · relaxed
Tracking redirect — sets the bsk_ref cookie
/api/admin/referrals
GET
admin · 120/min/route
Filterable list
/api/admin/referrals/stats
GET
admin · 120/min/route
Aggregate KPIs
/api/admin/referrals/[id]
GET
admin · 120/min/route
Detail + audit trail
/api/admin/referrals/[id]/reverse
POST
admin + CSRF · strict
Clawback granted credits
/api/admin/referrals/[id]/reject
POST
admin + CSRF · strict
Reject a pending referral
/api/admin/referral-codes
GET
admin · 120/min/route
Codes + usage counts
/api/admin/referral-codes/[id]/deactivate
POST
admin + CSRF · strict
Deactivate a code
Affiliates
Cash-commission partner program (distinct from referrals). Every endpoint returns 404 when AFFILIATES_ENABLED is off.
Shared 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]/toggle
POST
admin + CSRF
Enable/disable one native cron and commit its audit atomically; missing target returns 404
/api/admin/cron/[jobid]/unschedule
POST
admin + CSRF + recent auth
Remove one native cron, clear its application reference and audit atomically; missing target returns ok: false
/api/admin/cron/purge-orphans
POST
admin + CSRF + recent auth
Atomically 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.