Skip to main content
The Portal acts as a secure Backend for Frontend (BFF) proxy. The browser never communicates directly with the database (AllCodex Core) or the AI brain (AllKnower). It sends requests to /api/* on the Portal, which parses authentication cookies server-side and forwards requests to the private backends.

Credential Flow

All backend calls resolve credentials from HTTP-only cookies via getEtapiCreds() and getAkCreds() (defined in lib/get-creds.ts). If cookies are not set, the route falls back to environment variables in .env.local. The browser never sees tokens.
Browser  →  POST /api/auth/login or /api/config/allknower-login  (AllKnower: email/password → Bearer token)
         →  HTTP-only cookies set (allknower_url, allknower_token)
         →  POST /api/integrations/allcodex/connect  (Core: password → ETAPI token,
              stored as the user's encrypted UserIntegration inside AllKnower)
         →  All subsequent /api/* calls read the AllKnower token from cookies; Core creds
              are resolved server-side per-user by AllKnower
Zero-Login Auto-Provisioning: In dev/zero-login deployments, the AllKnower token cookie is set automatically by the auto-provisioning middleware, and Core credentials are bootstrapped into the default user’s UserIntegration — so neither step above is required for the first user.

Lore CRUD

These routes proxy requests directly to the AllCodex Core ETAPI.
MethodPathDescriptionBackend
GET/api/loreSearch lore notes. Default: ?q=#lore (all lore). Supports full ETAPI search syntax.AllCodex ETAPI
POST/api/loreCreate a new lore note.AllCodex ETAPI
GET/api/lore/[id]Get note metadata + attributes.AllCodex ETAPI
PATCH/api/lore/[id]Update note properties (title, type, mime).AllCodex ETAPI
DELETE/api/lore/[id]Delete a note. Returns 204.AllCodex ETAPI

POST /api/lore — Create Note Example

{
  "title": "Lord Ashvane",
  "type": "text",
  "mime": "text/html",
  "loreType": "character",
  "templateId": "_template_character",
  "parentNoteId": "root",
  "attributes": { "fullName": "Lord Ashvane of Solara", "race": "Human" }
}

Note Attributes & Relationships

MethodPathDescriptionBackend
POST/api/lore/[id]/attributesCreate a label or relation attribute.AllCodex ETAPI
DELETE/api/lore/[id]/attributes?attrId=<ID>Delete an attribute by ID. Returns 204.AllCodex ETAPI
POST/api/lore/[id]/relationshipsGet existing relations + AI-suggested connections for a note.AllKnower + AllCodex ETAPI

POST /api/lore/[id]/attributes — Create Label

{ "type": "label", "name": "fullName", "value": "Lord Ashvane" }

POST /api/lore/[id]/attributes — Create Relation

{ "type": "relation", "name": "~belongsTo", "value": "<targetNoteId>" }

Note Content, Revisions & Media

MethodPathDescriptionBackend
GET/api/lore/[id]/contentGet the HTML content of a note. Returns text/html.AllCodex ETAPI
PUT/api/lore/[id]/contentUpdate the HTML content of a note. Body: raw HTML string. Returns 204.AllCodex ETAPI
GET/api/lore/[id]/preview?mode=gm|playerGet sanitized HTML content. gm mode uses sanitizeLoreHtml(), player mode uses sanitizePlayerView() (strips gmOnly content).AllCodex ETAPI
GET/api/lore/[id]/imageProxy image binary from AllCodex. Cached 1 day.AllCodex ETAPI
GET/api/images/[id]/[filename]Redirect compatibility route for editor image URLs. Forwards to /api/lore/[id]/image.None (local redirect)
GET/api/lore/[id]/backlinksGet notes that link to this note (inbound relations + content links).AllCodex ETAPI
GET/api/lore/[id]/breadcrumbsGet the ancestor branch path for breadcrumb navigation.AllCodex ETAPI
GET/api/lore/[id]/revisionsList a note’s revision history.AllCodex ETAPI
GET/api/lore/[id]/revisions/[revId]/contentGet the content of a specific revision.AllCodex ETAPI
GET/api/lore/[id]/graphGet the relationship graph (existing relations + traversal) for a note.AllKnower
GET/api/lore/[id]/relationship-historyGet the applied-relationship history for a note.AllKnower
GET/api/lore/[id]/mapBuild map data for a note: map image URL, dimensions, and geolocation pins derived from child notes.AllCodex ETAPI
POST/api/lore/[id]/map/uploadUpload a map image for a note.AllCodex ETAPI

Lore Tree Operations

MethodPathDescriptionBackend
POST/api/lore/moveMove a note to a different parent branch.AllCodex ETAPI
POST/api/lore/upload-imageUpload an image file and create an AllCodex image note. Returns { url, noteId }.AllCodex ETAPI

POST /api/lore/move

{ "noteId": "<noteId>", "newParentId": "<parentId>", "index": 0 }

POST /api/lore/upload-image

Send binary image body. Set x-vercel-filename header for the file name.

Search & Discovery

MethodPathDescriptionBackend
GET/api/lore/mention-search?q=<text>Autocomplete for @-mentions. Min 2 chars. Returns up to 8 results.AllCodex ETAPI + AllKnower
GET/api/lore/note-search?q=<text>&type=<type>Title/type note search for picker-style UI. Min 2 chars. Returns up to 12 results.AllCodex ETAPI
POST/api/lore/autolinkScan a text block for lore title matches. Cached 60s.AllCodex ETAPI
GET/api/search?q=<query>&mode=etapi|ragDual-mode search. etapi = ETAPI full-text/attribute, rag = AllKnower semantic search.AllCodex ETAPI or AllKnower

POST /api/lore/autolink

{ "text": "Lord Ashvane marched to Solara with the Blackthorn Company." }
Returns:
{ "matches": [
  { "term": "Lord Ashvane", "noteId": "abc123", "title": "Lord Ashvane" },
  { "term": "Solara", "noteId": "def456", "title": "Solara" }
] }

Brain Dump

MethodPathDescriptionBackend
POST/api/brain-dumpProcess raw text through the AI extraction pipeline.AllKnower
POST/api/brain-dump/streamSSE-streamed auto brain dump (progressive entity cards, tokens, done).AllKnower
POST/api/brain-dump/commitCommit reviewed entities from review mode.AllKnower
GET/api/brain-dump/historyList the 20 most recent brain dumps.AllKnower
GET/api/brain-dump/history/[id]Get full detail for a single brain dump entry.AllKnower
GET/api/brain-dump/[id]/diffsPer-note content diffs (revision history) produced by a brain dump.AllKnower
POST/api/brain-dump/batchSubmit a batch (bulk) of brain dumps for background processing.AllKnower
GET/api/brain-dump/batch/[batchId]Batch processing status.AllKnower
DELETE/api/brain-dump/batch/[batchId]Cancel queued jobs in a batch.AllKnower

POST /api/brain-dump

{
  "rawText": "Lord Ashvane is a human noble who rules Solara...",
  "mode": "auto"
}
Modes:
  • auto — extract and write entities immediately
  • review — extract entities and return as proposals for user approval
  • inbox — queue the raw text for later processing

POST /api/brain-dump/commit

{
  "rawText": "Lord Ashvane is a human noble...",
  "approvedEntities": [
    { "title": "Lord Ashvane", "type": "character", "action": "create", "content": "<p>...</p>" }
  ]
}

AI Tools & Copilot

MethodPathDescriptionBackend
POST/api/ai/consistencyRun a RAG-augmented consistency scan.AllKnower
GET|POST/api/ai/gapsDetect underdeveloped lore areas. POST preferred (avoids caching).AllKnower
POST/api/ai/relationshipsGet AI relationship suggestions for text/note.AllKnower
PUT/api/ai/relationshipsApply suggested relationships (persists as AllCodex relation attributes).AllKnower
POST/api/lore/[id]/copilot/chatArticle copilot turn — sends conversation transcript + current note context, returns assistant message + optional proposal.AllKnower
POST/api/lore/[id]/copilot/streamSSE-streamed article copilot turn (status, token, reasoning, result, done).AllKnower
POST/api/lore/[id]/copilot/applyApply an approved copilot proposal (create/update notes, labels, relations). Returns { updatedNoteIds, createdNoteIds, skipped, failed }.AllCodex ETAPI

POST /api/ai/consistency

{ "noteIds": ["abc123", "def456"] }

PUT /api/ai/relationships — Apply

{
  "sourceNoteId": "abc123",
  "relations": [
    { "targetNoteId": "def456", "type": "rulerOf", "description": "Lord Ashvane rules Solara" }
  ],
  "bidirectional": true
}

RAG

MethodPathDescriptionBackend
GET/api/rag?text=<query>&topK=10Semantic similarity search against the lore vector index.AllKnower
POST/api/ragSame, via POST body.AllKnower

Content Collections

These routes query AllCodex ETAPI for notes with specific labels.
MethodPathDescriptionFilter
GET/api/questsList all quest notes.#quest label
GET/api/statblocksList all statblock notes.#statblock label
GET/api/timelineList events and timelines.#loreType=event OR #loreType=timeline

Share Settings

MethodPathDescriptionBackend
GET/api/shareGet the current share root configuration.AllCodex ETAPI
PUT/api/shareSet a note as the share root (public entry point).AllCodex ETAPI
GET/api/share/treeList all lore notes with share visibility flags.AllCodex ETAPI

GET /api/share/tree — Response Fields

Each note includes: noteId, title, isDraft, isGmOnly, shareAlias, isProtected, isShared.

Configuration & Auth

MethodPathDescriptionBackend
POST/api/auth/loginAllKnower email/password login → sets Bearer token cookie.AllKnower (better-auth)
POST/api/auth/registerRegister AllKnower account → sets token cookie.AllKnower (better-auth)
POST/api/auth/logoutSign out, clear AllKnower session cookie.AllKnower (better-auth)
GET/api/auth/sessionVerify AllKnower session, return user.AllKnower (better-auth)
POST/api/config/allknower-loginAllKnower email/password login → sets Bearer token cookie.AllKnower (better-auth)
POST/api/config/allknower-registerRegister AllKnower account → sets token cookie.AllKnower (better-auth)
POST/api/integrations/allcodex/connectCore password (or pre-acquired token) → obtains ETAPI token → stores it as the user’s encrypted UserIntegration in AllKnower.AllCodex ETAPI + AllKnower
GET/api/integrations/allcodex/statusWhether a Core integration is connected for the user.AllKnower
DELETE/api/integrations/allcodexRemove the user’s stored Core integration.AllKnower
DELETE/api/config/disconnect?service=allcodex|allknower|allClear stored credentials.None (local)
GET/api/config/modelsList configured/available LLM models per task.AllKnower
POST/api/config/wipeDangerous: wipe the user’s lore + RAG state (LanceDB table + Prisma tracking).AllKnower
GET/api/config/portalGet portal config (lore root note ID).None (local)
PUT/api/config/portalSet portal config.None (local)
GET/api/config/statusCheck connectivity to AllCodex and AllKnower.Both

Import

MethodPathDescriptionBackend
POST/api/import/system-packImport a system pack (SRD, monster manual, etc.).AllKnower
POST/api/import/azgaarImport Azgaar Fantasy Map JSON. Supports ?action=preview for simulation.AllKnower

Notifications

Web-push notifications (e.g. brain dump completion status).
MethodPathDescriptionBackend
GET/api/notifications/vapid-public-keyGet the VAPID public key for push subscription.AllKnower
POST/api/notifications/subscribeRegister a browser push subscription.AllKnower
DELETE/api/notifications/unsubscribeRemove a push subscription.AllKnower

Usage & Budgets

MethodPathDescriptionBackend
GET/api/usage/summaryToken/cost usage summary (per day, task, model).AllKnower
GET/api/usage/budgetsGet the user’s token-budget thresholds.AllKnower
PUT/api/usage/budgetsSet the user’s token-budget thresholds.AllKnower
GET/api/usage/alert-statusWhether the user is over budget / nearing the alert threshold.AllKnower

Key Developer Patterns

  1. Proxy Architecture: Every /api/* route is a thin proxy. No domain logic lives in the Portal.
  2. Error Handling: Routes call notConfigured() when credentials are missing and handleRouteError() for exceptions.
  3. HTML Sanitization: Portal performs DOMPurify sanitization. Never assume content from ETAPI is safe for direct HTML insertion.
  4. Content-Type Constraint: PUT requests to /api/lore/[id]/content must use Content-Type: text/plain due to Express raw-parser behavior in Core.
  5. Cookie-only Secrets: All tokens are stored in HTTP-only cookies. The browser JS never has access to credentials.