Maison Doclar

Intelligence — Operator Manual

Slice 5 operator console · Last updated May 2026

No sections match your search.

1. Introduction ↑ TOC

Intelligence is the Maison Doclar OS service that runs structured AI conversations with hosts, synthesises outcomes into JSON, and translates free-text feedback into design directives. Calling apps (Nucleus, Relay, Orion, Badges, and others) never talk to Anthropic directly — they call Intelligence with scoped API keys.

Two operators only manage the system: configure conversation types, maintain the app registry, rotate API keys, monitor usage and sessions, test prompts in the sandbox, and verify gateway webhooks. Hosts interact only through magic-link host sessions owned by Intelligence.

All persistent data lives in PostgreSQL schema app_intelligence on the shared Neon cluster. Deployments run on Railway with migrate → seed → server startup.

Admin console

After login at /, operators reach /admin (redirects to Conversation Types). The sidebar links to Conversation Types, API Keys, App Registry, Usage, Sessions, Prompt Sandbox, and this manual at /manual. Every admin route requires a valid md-session cookie.

Who uses this manual

  • Operators configuring prompts, keys, and registry entries before go-live.
  • Engineers verifying inter-app flows, host sessions, webhooks, and health after deploy.
  • Support staff tracing a session lifecycle or usage spike via the admin console.
2. Login ↑ TOC

Open the service root /. You see a single password field — no email or username. This is intentional: only two operator accounts exist, distinguished by which password you enter.

AccountEmail (internal)Env variable
Operator 1command@maisondoclar.comSUPERADMIN_1_PASSWORD
Operator 2control@maisondoclar.comSUPERADMIN_2_PASSWORD

Passwords are set in Railway (or local .env), hashed with bcrypt cost 12 at seed time, and never stored in plaintext in the database.

UX details

  • Eye icon — toggles password visibility without submitting the form.
  • Enter — submits the same as “Sign In”.
  • Errors — appear inline below the field; the page does not reload.
  • ? (top-right) — opens this manual at /manual.

On success, the browser receives an md-session HTTP-only cookie (8-hour JWT). Failed attempts return 401 with Invalid password. Sign out from any admin page clears the session and returns you to /.

Health checks

  • GET /health — public liveness; returns {"status":"ok"}. Railway uses this to restart unhealthy containers.
  • GET /api/health/ready — operator-only readiness; runs SELECT 1 against Postgres. Returns 401 without a session.
3. App Registry ↑ TOC

The app_registry table lists every OS app that may call Intelligence. Each row has a unique slug (e.g. nucleus, badges), display metadata, and capabilityContext — prose injected into system prompts at runtime so the model knows what that app can do and what data it already holds.

Why capabilityContext matters: Without accurate capability context, the consultant may hallucinate integrations, ask hosts questions the calling app already answered, or omit features the app supports. Update capabilityContext whenever an app ships new capabilities. The model reads this text on every turn — treat it as the ground truth for what the calling app owns.

Edit registry rows at /admin/app-registry. Each entry includes:

FieldPurpose
slugStable identifier; matches callingApp in usage logs and API key ownership.
displayName / descriptionHuman-readable labels in admin UI.
capabilityContextInjected into system prompts — the most important field for conversation quality.
baseUrlOptional app base URL for operator reference.
webhookUrlWhere Intelligence may POST synthesis results when enabled.
synthesisWebhookEnabledToggle outbound synthesis webhook delivery.
synthesisWebhookSecretShared secret for signing outbound synthesis payloads.
isActiveInactive apps cannot receive new API keys.

Conversation types belong to an app via appId. When Badges calls Intelligence, the Badges registry row’s capabilityContext tells the model what Badges already knows about the host’s brand identity.

4. Conversation Types ↑ TOC

Conversation types are the unit of configuration. No prompts live in application code — all system, synthesis, and translation prompts live in conversation_types. Manage them at /admin/conversation-types.

FieldPurpose
slugStable identifier; API keys scope to slugs; referenced in inter-app API calls.
displayNameLabel shown in admin UI and sandbox dropdown.
appIdOwning app from the registry; determines callingApp and capabilityContext source.
systemPromptMain consultant prompt; may include {variable} placeholders.
systemPromptVariablesExplicit list of allowed variable names callers must supply per session.
synthesisPromptPrompt used when closing a session into structured JSON.
translationPromptOptional prompt for stateless revision-note translation via POST /translate.
synthesisSchemaJSON Schema validating synthesis model output.
translationSchemaJSON Schema validating translation model output (optional).
closingSignalToken the model emits when ready to synthesise (default [READY_TO_SYNTHESISE]).
maxTurnsHard cap on host/AI exchanges; reaching the limit forces session close.
temperamentGuidanceJSON map of host temperament key → extra instructions; DEFAULT key is fallback.
modelDefault Anthropic model for this type (e.g. claude-sonnet-4-5).
maxTokensPerTurnMax output tokens per conversation turn.
isActiveInactive types reject new sessions with 404.
createdByOperator email or seed for audit trail.

At session start, callers supply variables which are merged into the system prompt. The runtime also injects {temperament_guidance} from temperamentGuidance based on hostTemperament. Missing required variables are rejected at the API layer with 400 and a missing array.

Use the admin preview to verify placeholder interpolation before saving. Test closing signals and tone in the Prompt Sandbox before promoting changes to production types.

5. API Keys ↑ TOC

Calling apps authenticate with keys formatted il_ + 32 hex characters. Only a bcrypt hash and 8-character prefix are stored. The full key is shown once at creation in /admin/api-keys — copy it immediately; it cannot be retrieved later.

scopes is a string array of conversation type slugs this key may access. A key cannot start sessions for types outside its scopes (403).

Badges placeholder key

Seed data creates a placeholder key for Badges pre-go-live:

  • Label: Maison Doclar Badges
  • Key: il_badges_placeholder_rotate_before_golive
  • Prefix: il_badges
  • Scopes: identity-consultation

Rotate this key before Badges goes live. The placeholder is documented in seed output and must not remain in production environments.

Rotation procedure

  1. In /admin/api-keys, create a new key for the same app with identical scopes.
  2. Copy the full key from the one-time reveal modal.
  3. Deploy the new secret to the calling app’s environment (Railway env var or secrets manager).
  4. Verify traffic on the new key: check last_used_at on the key row and filter Usage by calling app.
  5. Set is_active = false on the old key (Deactivate in admin).
  6. After a safe window (e.g. 7 days with zero usage on the old key), delete the old key.

Never revoke the only active key for a live app without deploying the replacement first. For compromised keys, create and deploy the replacement immediately, then deactivate the compromised key.

6. Prompt Sandbox ↑ TOC

/admin/sandbox runs test conversations against Anthropic without creating real host sessions or conversation sessions. Select a conversation type, fill variable inputs, optionally override the model, enter a host message, and click Run Test.

Model override

The optional Model override field accepts any Anthropic model ID (e.g. claude-sonnet-4-5). When empty, the sandbox uses the conversation type’s configured model field. Overrides apply only to the current test run — they do not persist to the conversation type record.

How runs work

  • API: POST /api/admin/sandbox/run streams tokens via SSE.
  • Each completed run is stored in prompt_test_runs and logged to usage_log with operation TEST.
  • Token counts (input/output) display after each run completes.
  • If the AI response contains the type’s closingSignal, the UI shows “Closing signal detected”.
  • Recent runs appear in the history panel; click a run to view its full turn transcript.

Leave the host message empty to simulate the opening turn (the sandbox sends a standard opening user message). Use multi-turn testing by continuing the conversation — turn history accumulates until you reset.

Always sandbox-test prompt changes before saving production conversation types. Verify variable interpolation, temperament guidance, closing signals, and max-turn behaviour.

7. Host Surface ↑ TOC

Intelligence owns the host conversation UI at /host/<token>. Calling apps create a link via POST /api/intelligence/host-session after starting a conversation; the host never sees Badges, API keys, or admin surfaces.

Creating a host session

Request body:

  • sessionId — required; must belong to the caller’s API key.
  • brandingConfig — optional object (see below).
  • expiryHours — optional; defaults to 72.
  • returnUrl — optional URL shown after consultation closes.

Returns hostUrl, token, and expiresAt. If an active non-expired host session already exists for the conversation session, the existing link is returned.

brandingConfig

  • title — heading on the host page.
  • subtitle — smaller line under the title.
  • closingMessage — shown after the consultation closes.
  • accentColor — gold accent (default #B79F85).

Host experience

Hosts load GET /api/host/:token/session to restore turns. Messages stream via SSE on POST /api/host/:token/message. When the AI emits the closing signal, the stream sends event: close, the session becomes COMPLETED, and synthesis runs automatically in the background.

Expired or invalid links render a graceful expired page — no raw error codes exposed to hosts.

Host surface troubleshooting

SymptomLikely causeAction
“This link has expired”tokenExpiresAt passed or host session status EXPIREDCalling app must create a new host session via POST /host-session; default expiry is 72 hours.
Blank or stuck loadingInvalid token or session deletedVerify token in host_sessions; confirm conversation session still ACTIVE or COMPLETED.
Messages not sendingSession not ACTIVECheck session status in Sessions admin; closed sessions reject new host turns.
Stream stops mid-responseNetwork drop or Anthropic errorHost UI shows “Connection lost”; refresh page — turns persist in DB.
Closing screen but no synthesisBackground synthesis failedCheck server logs for host_auto_synthesis_failed; manually trigger via inter-app /synthesise if session is COMPLETED.
Wrong brandingbrandingConfig not passed or cached old sessionPass branding on host-session create; existing active sessions reuse prior branding.
8. Inter-app API ↑ TOC

Calling apps (e.g. Badges) use the inter-app API at /api/intelligence/*. Authenticate with Authorization: Bearer il_… — not operator cookies.

Endpoints

MethodPathDescription
POST/conversation/startCreate session + opening AI message. Body: conversationTypeSlug, variables, optional externalRef, hostTemperament. Returns 201 with sessionId and openingMessage.
POST/conversation/:sessionId/turnHost message → AI reply. Body: hostMessage. Returns shouldClose when closing signal seen or max turns reached.
GET/conversation/:sessionIdSession metadata + full transcript ordered by turnIndex.
POST/conversation/:sessionId/synthesiseRun synthesis into structured JSON. Session must be COMPLETED (or already synthesised).
POST/translateStateless revision-note translation. Body: conversationTypeSlug, inputText, optional context.

Additionally, POST /complete records a final outcome (DELIVERED, ABANDONED, or EXPIRED) and maps it to session status. Host link creation is documented in Host Surface (POST /host-session).

Errors

StatusWhen
400Missing required body fields; missing prompt variables on start; translation not configured for type; invalid outcome on /complete.
401Missing or invalid API key (Authorization header absent or wrong).
403Key not scoped for conversation type slug; session belongs to a different key.
404Unknown session, conversation type, or inactive type.
409Wrong session state — e.g. turn on non-ACTIVE session; synthesise before COMPLETED.
502Anthropic returned non-JSON for synthesis or translation; parse failure.

Rotate compromised keys from Admin → API Keys: create new key, deploy to caller, verify usage, deactivate old key.

9. Usage ↑ TOC

/admin/usage provides full Anthropic call analytics. Every model call writes a row to usage_log. Rows are never deleted — use for billing reconciliation and abuse detection.

Operations

OperationSource
CONVERSATIONInter-app turns and host-surface message streams.
SYNTHESISStructured JSON extraction when a session closes.
TRANSLATIONStateless POST /translate calls.
TESTPrompt Sandbox runs (/api/admin/sandbox/run).

Each row stores: operation, model, inputTokens, outputTokens, estimatedCostUsd, callingApp, conversationTypeSlug, optional sessionId, and durationMs. Cost is estimated at write time from model pricing tables in application logic.

Using the Usage page

  • Summary metrics — total cost, call count, input/output tokens for the filtered date range (default last 30 days).
  • Filters — date range, calling app, operation, conversation type slug.
  • Charts — cost breakdown by operation and by app.
  • Table — paginated log entries with model, tokens, cost, and duration per call.

API: GET /api/admin/usage accepts query params from, to, callingApp, operation, conversationTypeSlug, page, limit. Returns entries, total, and aggregated summary.

After API key rotation, filter by calling app and confirm last_used_at on the new key aligns with expected traffic before deactivating the old key.

10. Sessions ↑ TOC

/admin/sessions lists every host journey stored in conversation_sessions. Click a row to open the detail panel with full transcript, synthesis result, host session metadata, and per-session usage summary.

Session lifecycle

StatusMeaning
ACTIVEIn progress; accepts host turns via inter-app or host surface.
COMPLETEDConsultation closed (closing signal or max turns); synthesis may be pending or in progress.
SYNTHESISEDStructured JSON stored in synthesis_results; webhook may have been delivered.
ABANDONEDCalling app reported abandonment via POST /complete with outcome ABANDONED.
EXPIREDCalling app reported expiry via POST /complete with outcome EXPIRED, or host link expired without completion.

Related tables

  • conversation_turns — roles AI or HOST with monotonic turn_index. Always read transcripts ordered by turn_index.
  • synthesis_results — JSON result, confidence scores, gaps, and webhook delivery status.
  • host_sessions — magic-link tokens (il_host_…), branding, expiry, and access timestamps.

Using the Sessions page

  • Status pills — quick filter by lifecycle status.
  • Filters — status, calling app, conversation type, date range.
  • Detail panel — transcript, variables, host temperament, synthesis JSON, host session status, token/cost summary.

API: GET /api/admin/sessions (list with filters) and GET /api/admin/sessions/:id (full detail). Support workflows: locate session by externalRef (caller-supplied ID) or by session UUID from logs.

11. Gateway Webhook ↑ TOC

POST /api/webhooks/gateway receives events from Maison Doclar Gateway. Requests must include header x-gateway-signature — HMAC-SHA256 hex digest of the raw request body using GATEWAY_WEBHOOK_SECRET.

Every payload is logged to gateway_webhook_logs and acknowledged with 200 immediately; processing runs asynchronously. Invalid signatures return 401.

Handled event types

Intelligence does not own guest data — receipts are logged and processed_at is set on matching log rows. Unknown types are logged as warnings but still return 200.

Event typeDescription
guest.createNew guest record created in Nucleus.
guest.updatedGuest profile or metadata changed.
guest.deleteGuest removed from an event.
rsvp.updatedRSVP status changed for a guest.
attendance.reconciledAttendance counts reconciled post-event.
checkin.createdGuest checked in at venue.

Payloads include nucleusGuestId and nucleusEventId when applicable. Query gateway_webhook_logs for audit and replay investigation.

12. Troubleshooting ↑ TOC
SymptomLikely causeAction
Container exits on bootMissing env varCheck logs for [Config] FATAL; fill required vars from .env.example.
/health OK but login failsSeed not run or wrong passwordRun pnpm seed:superadmin; confirm operators in admins table.
ready returns 500DB unreachableVerify DATABASE_URL, Neon IP allowlist, migrations applied.
Inter-app 401Wrong or missing API keyConfirm Authorization: Bearer il_…; rotate if compromised; check key is active.
Inter-app 403Scope mismatchKey scopes must include the conversation type slug; session must belong to same key.
Inter-app 409 on turnSession already closedCheck status in Sessions admin; start a new session if consultation ended.
Synthesis 502Model returned invalid JSONReview synthesis prompt and schema; test in sandbox; check Anthropic response in logs.
Placeholder key still in useBadges not rotated pre-go-liveReplace il_badges_placeholder_rotate_before_golive with a production key.
Webhook always 401Secret mismatchAlign GATEWAY_WEBHOOK_SECRET with Gateway; signature must be raw-body HMAC hex.
Prisma “schema does not exist”Migration not deployedpnpm migrate on Railway; confirm app_intelligence schema exists.
Model hallucinates app featuresStale capabilityContextUpdate registry row in App Registry; verify in sandbox with that app’s type.
Usage totals look wrongDate filter or TEST noiseReset filters; exclude TEST operation for production cost views.
Host link expired72h default passedSee Host Surface troubleshooting; create new host-session.

Structured logs go to stdout and optionally Axiom when AXIOM_API_KEY is set. Missing Axiom config never crashes the server. Each admin page links to the relevant manual section via the ? help icon.