temetro is in beta and under active development. Expect things to change.
temetro

AI configuration

Per-user AI provider settings and the migration import endpoint.

AI settings are user-scoped (like /api/settings) — they follow the signed-in user across clinics. Provider API keys are encrypted at rest and never returned by the API.

MethodPathAuthReturns
GET/api/ai/configSigned inNon-secret config + apiKeySet
PUT/api/ai/configSigned inThe saved config
POST/api/ai/testSigned in{ ok, message }
POST/api/ai/importpatient:write{ created, failed }
POST/api/ai/import/validatepatient:write{ valid, invalid, total }
GET/api/ai/policyMember of clinicThe clinic's AI policy
PUT/api/ai/policyOwner/adminThe saved policy

The config object

{
  "config": {
    "mode": "local",
    "provider": "anthropic",
    "ollamaBaseUrl": "http://localhost:11434",
    "ollamaModel": "llama3.1",
    "defaultModel": "claude-sonnet-4-6",
    "defaultEffort": "medium",
    "veilLevel": "full",
    "apiKeySet": { "openai": false, "anthropic": true, "gemini": false }
  }
}
  • mode — how a model is chosen: "api" (a cloud provider key), "local" (Ollama on your own infrastructure), "auto" (use a cloud key when one is set, otherwise fall back to local Ollama), or "off" (the assistant is disabled). New installs default to "auto".
  • provider — the active cloud provider: "openai", "anthropic", or "gemini".
  • veilLevelVeil de-identification strictness: "full", "names", or "off".
  • apiKeySet — which providers have a stored key. The keys themselves are never returned.

Saving config

PUT accepts any subset of the fields above, plus an optional apiKey (plaintext) for the currently selected provider — it is encrypted before storage and used only to call the provider. Send "apiKey": "" to clear it.

curl -b cookies.txt -X PUT http://localhost:4000/api/ai/config \
  -H "Content-Type: application/json" \
  -d '{ "mode": "api", "provider": "anthropic", "apiKey": "sk-ant-…" }'

Testing connectivity

POST /api/ai/test does a lightweight probe before you rely on a setting: for local mode it pings Ollama's /api/tags; for API mode it confirms a key is stored (it does not spend a token).

curl -b cookies.txt -X POST http://localhost:4000/api/ai/test \
  -H "Content-Type: application/json" \
  -d '{ "mode": "local", "ollamaBaseUrl": "http://localhost:11434" }'

Clinic-wide AI policy

Unlike the per-user config above, the AI policy is clinic-scoped (org-wide) and only owners/admins can change it. It is the kill-switch behind the chat: when AI is off for a caller, POST /api/chat returns 403, and the frontend hides the AI page and sidebar entry.

{
  "aiEnabled": true,
  "disabledForEmployees": false
}
  • aiEnabled — master switch. false disables the AI for everyone in the clinic.
  • disabledForEmployees — when true (and aiEnabled is true), the AI is hidden and blocked for staff; owners and admins keep access.

GET is readable by any member (the app needs it to gate the UI); PUT requires owner/admin and returns the saved policy.

curl -b cookies.txt -X PUT http://localhost:4000/api/ai/policy \
  -H "Content-Type: application/json" \
  -d '{ "aiEnabled": true, "disabledForEmployees": true }'

Importing records

POST /api/ai/import commits patient records the clinician approved in the chat import preview (see the chat agent). The body is { "records": [...] } of patient objects in temetro's patient shape. The server re-validates every record and writes via the audited patient service — the AI never writes to the database directly. Imported records are stamped source: "ai" (surfaced as an "Added by AI" badge), gaps are filled with safe placeholders, and a missing fileNumber is auto-assigned — so only rows with no usable identity (e.g. no name) or a duplicate file number land in failed.

{
  "created": ["10293", "10294"],
  "failed": [{ "fileNumber": "10295", "error": "name: Patient name is required." }]
}

POST /api/ai/import/validate is a dry run — it runs the same validation and returns { valid, invalid, total } (each invalid entry includes the original record) without writing anything. The "Review & edit before import" UI calls it to refresh which rows are ready as the clinician edits them.

On this page