πŸ”Œ API Reference

  • transport:
    • same-origin JSON, multipart upload, and server-sent events
    • every /api/* route still requires trusted proxy identity and rejects a missing Remote-User
  • production identity since D-169 (2026-09-26):
    • anonymous requests receive trusted Remote-User: public with empty groups
    • optional Authelia SSO supplies a signed-in user and groups; client-supplied identity headers are overwritten
    • admin routes require admins or pwiz-admins; anonymous and signed-in non-admin requests return 403
    • signed-in library queries use that identity; anonymous saved/history/owned-wizard rows share public
  • operator-local diagnostics:
  • error contract:
    • handlers return { error: string } with a real non-2xx status on failure
    • successful routes do not fabricate success data when the underlying operation fails

Identity

GET /api/me

  • source:
    • production: nginx-injected optional SSO identity, falling back to public for anonymous requests
    • local test mode: loopback-only test header
  • response:
    • { id, username, is_admin }
    • anonymous value: {"id":"public","username":"public","is_admin":false}; a signed-in response reflects that user and its group-derived admin status
  • test-only policy:
    • X-Pwiz-Test-User is accepted only when PWIZ_ALLOW_TEST_IDENTITY=1 and the request originates from loopback
    • production requires a trusted Remote-User; nginx supplies optional SSO identity or the anonymous public fallback

Collections

GET /api/collections/:name?filter=<json>

  • names:
    • wizards
    • blueprints
    • prompt_history
    • saved_prompts
    • likes
    • admin_prompts
    • llm_logs
  • collection policy:
    • collection routes require trusted identity: signed-in SSO user or the shared anonymous public user
    • wizards: seeded globals (user_id IS NULL) are admin-writable; ordinary users may mutate only owned rows
    • admin_prompts: authenticated GET, admin-only write methods
    • llm_logs: fully admin-only for every method
    • likes: authenticated reads only; generic collection writes are forbidden and /api/likes/toggle is the sole mutation route
  • response:
    • newest-first array
  • filter:
    • URL-encoded JSON object
    • equality only
    • allowed indexed keys:
      • is_public
      • user_id
      • blueprint_id
      • key
    • unknown key: 400

POST /api/collections/:name

  • request:
    • JSON row payload
  • server-owned fields:
    • id
    • created_at
    • applicable user_id
  • response:
    • created row
  • collection scope:
    • admin_prompts: admin-only
    • llm_logs: admin-only
    • likes: disabled; use /api/likes/toggle
  • event:
    • broadcasts affected collection

PATCH /api/collections/:name/:id

  • request:
    • partial JSON patch
  • response:
    • updated row or mutation result
  • collection scope:
    • wizards: seeded globals (user_id IS NULL) require admin membership; owned custom wizards require owner identity
    • admin_prompts: admin-only
    • llm_logs: admin-only
    • likes: disabled; use /api/likes/toggle
  • event:
    • broadcasts affected collection

DELETE /api/collections/:name/:id

  • response:
    • 204 no-content
  • collection scope:
    • wizards: seeded globals (user_id IS NULL) require admin membership; owned custom wizards require owner identity
    • admin_prompts: admin-only
    • llm_logs: admin-only
    • likes: disabled; use /api/likes/toggle
  • event:
    • broadcasts affected collection

Likes

POST /api/likes/toggle

  • request:
    • { blueprint_id }
  • transaction:
    • if not liked, insert like row and increment blueprint count
    • if liked, delete like row and decrement blueprint count
  • response:
    • { liked: boolean, likes_count: number }
  • write boundary:
    • this route is the only supported likes mutation path
    • generic POST, PATCH, and DELETE through /api/collections/likes are forbidden
  • events:
    • broadcasts likes
    • broadcasts blueprints

Uploads

POST /api/uploads

  • request:
    • multipart form-data
    • field: file
  • accepted types:
    • image/png
    • image/jpeg
    • image/webp
  • maximum size:
    • 8 MB
  • write target:
    • data/uploads/<uuid>.<ext>
  • response:
    • { url: "/uploads/<uuid>.<ext>" }

AI text

POST /api/ai/text

  • request:
    • { system, messages, json, kind, templateKey, requestId?, thoughtsTab? }
    • only Live generation sends its random per-page thoughtsTab
  • response:
    • { content, timing }; timing is null for successful non-JSON text calls
    • JSON timing fields: requestedModel, resolvedModel, queue/attempt/repair/total; D-479g defaults requestedModel to host default, with no forced model flag unless the operator explicitly overrides it
    • admin timing rows expose requested_model and resolved_model; summaries identify the latest requested route, not a resolved provider for every call
  • behavior:
    • calls ompText
    • successful text AI calls are stored in llm_logs (admin-only)
    • JSON mode first tries local repair, then the existing single rerun on invalid output
    • kind: "ideas" enforces an object {ideas: [...]} containing 5 or 6 title/description entries; parsing and local repair validate the complete batch, not a singleton extracted from an array
  • errors:
    • 400 for invalid request
    • 502 for generation failure or invalid JSON after retry
    • 504 for timeout

See omp-integration for command details.

AI image

POST /api/ai/image

  • request:
    • { subject, aspectRatio, kind, system }
    • system is optional; when omitted, the server uses the built-in safe image system prompt
  • response:
    • { url, provider, model }
  • behavior:
    • calls ompImage
    • copies generated WebP into uploads
    • successful image AI calls are stored in llm_logs (admin-only); this route does not insert a timing row
  • accepted aspect ratios:
    • 1:1
    • 3:4
    • 4:3
    • 9:16
    • 16:9
  • errors:
    • 400 for invalid subject or aspect ratio
    • 502 for tool failure or missing image event
    • 504 for timeout

Client errors

POST /api/errors

  • request:
    • { type, message, stack }
  • server action:
    • log with client prefix
  • response:
    • 204

Logging, retention and public storage

Successful text/image AI calls are stored in llm_logs (admin-only) in the server’s data/pwiz.db. Text rows retain kind/template key, submitted system text, the JSON-encoded message list, final response content/timing and timestamp. Image rows retain supplied context and the result URL/provider/model/aspect ratio; bytes are under data/uploads.

JSON text calls with timing data also insert queue/attempt/repair/outcome data into llm_timings. Timed OMP failures can create failed timing rows without a successful log row; this is not logging of every failed HTTP request. Successful plain-text/diagram calls and image calls do not insert timing rows. Since D-479g, the requested route is stored separately from the final OMP-reported provider/model. host default means no app-level model flag was supplied; unknown means no final model metadata was reported. Historical requested values are preserved and old resolution stays unknown. This is reported provenance, not independent upstream verification.

There is no automatic log expiry/TTL or logging-disable setting in the app. Rows remain until an admin deletes them. Developer tools Off and thought display Off do not disable prompt/result logging. Stored prompt/result content is not blanket-redacted; do not submit credentials.

Live thought relay chunks are bounded, in-memory data, not a persisted raw reasoning transcript. Caught React errors and server/OMP diagnostics go to the service journal. Inherited nginx/journal retention, OMP-core telemetry and upstream provider logging/retention have not been established. No app-owned external analytics sender was found in this code path; this does not establish zero external telemetry.

Anonymous project/history/likes/custom wizard rows share the public identity. Upload URLs have no per-record user access check. Phase metadata is identity-scoped, so public tabs can receive one another’s phases; live thoughts additionally require the matching tab UUID.

Events

GET /api/events

  • protocol:
    • server-sent events
  • headers:
    • Content-Type: text/event-stream
    • Cache-Control: no-cache
    • X-Accel-Buffering: no
  • event payload:
    • data: {"collection":"<name>"}
  • keepalive:
    • comment every 25 seconds

See runtime for the client useQuery invalidation model.