For AI agents
Configure Engram on the user's behalf
If your user has handed you their Engram API key
(eng_live_…), you can call the four endpoints below to
check their state and configure their BYOK provider for them, no
portal hop required. Bearer auth, JSON in / JSON out, predictable
error shapes.
Not on this page yet: account signup via API. Email verification + a phishing-resistant flow are gating that, so we'll ship a device-code OAuth pattern (the gh-CLI / Vercel pattern) when we're confident it's the right shape. For now, the user signs up via the browser at portal.lumetra.io and hands you the resulting API key.
Authentication
Every endpoint accepts an Engram API key via the standard Authorization: Bearer header. The same endpoints also
accept JWTs (browser cookie or bearer) so the portal UI keeps working
on the exact same handlers.
Authorization: Bearer eng_live_...
Unverified email addresses are rejected with 403 { "code": "email_not_verified" } on every
endpoint except GET /auth/me, which intentionally
allows unverified tenants through so you can detect a half-onboarded
user.
Base URL
All four endpoints live under https://portal.lumetra.io/api.
The portal frontend proxies the same prefix to the engram-admin
backend, so a single base URL works for browsers, agents, and CLI
tools.
Probe state: GET /auth/me
The first call your agent should make. Tells you whether the key is valid, whether the user's email is verified, and whether BYOK is already configured. Allowed pre-verification so you can render a "check your email" hint instead of failing opaquely.
curl https://portal.lumetra.io/api/auth/me \
-H "Authorization: Bearer $ENGRAM_API_KEY"
# 200 OK
{
"tenant_id": "…",
"email": "user@example.com",
"email_verified": true,
"billing_plan": "indie",
"billing_status": "active",
"byok_configured": false,
"is_admin": false
}
If email_verified is false, surface a
message asking the user to click the verification link in their
inbox, then re-probe.
Read current BYOK config: GET /account/byok
Returns the current BYOK state with the key safely redacted
(key_prefix: "sk-•••", never plaintext). Use this to
decide whether to skip configuration or surface a "you already have
a provider configured, switch?" prompt.
curl https://portal.lumetra.io/api/account/byok \
-H "Authorization: Bearer $ENGRAM_API_KEY"
# 200 OK
{
"enabled": true,
"base_url": "https://api.deepseek.com",
"has_key": true,
"key_prefix": "sk-•••",
"last_validated_at": "2026-05-19T17:15:10Z",
"last_error": null,
"components": { "synthesis": { … }, "classifier": { … }, … }
} Configure BYOK: PUT /account/byok
Partial updates: send any subset of base_url, api_key, enabled. The base URL is
SSRF-validated against an allowlist; private IPs and IMDS endpoints
are rejected. The API key is encrypted server-side immediately and
never stored or returned in plaintext. Response shape matches
GET /account/byok.
curl -X PUT https://portal.lumetra.io/api/account/byok \
-H "Authorization: Bearer $ENGRAM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"base_url": "https://api.deepseek.com",
"api_key": "sk-the-users-deepseek-key",
"enabled": true
}'
# 200 OK (same shape as GET /account/byok, with last_error cleared) Be transparent with the user before doing this. Your
agent will need to see the user's provider API key (sk-…)
to POST it here. That's a meaningful boundary, so say so out loud. The
key is encrypted at rest on Lumetra's side and never sent back, but
the agent transcript itself will contain the key briefly before the
POST. If you can route the key through a credential manager rather
than chat input, do.
Validate the configured provider: POST /account/byok/validate
One-shot health check. No body required; the endpoint reads the
stored config, queries the provider's /v1/models, picks
a cheap chat model from what's returned, and sends a minimal ping
via the OpenAI-compatible API. Always responds 200;
success or failure is in the body so clients don't have to special-case
4xx for surfaced errors. Persists the result to byok_validated_at + byok_last_error so any
subsequent GET reflects the latest state.
curl -X POST https://portal.lumetra.io/api/account/byok/validate \
-H "Authorization: Bearer $ENGRAM_API_KEY"
# 200, success
{ "ok": true }
# 200, provider returned an error
{ "ok": false, "error": "Error code: 401, Invalid API key" }
# 400, BYOK not configured yet (call PUT /account/byok first)
{ "ok": false, "error": "BYOK not configured" }
# 503, BYOK globally disabled on the server
{ "ok": false, "error": "BYOK globally disabled by server" } End-to-end happy path
- Ask the user for their Engram API key. They get it from their dashboard at portal.lumetra.io. Store it in your agent's secret store; treat as a bearer token.
-
GET /auth/me, confirm the key works,email_verifiedis true, note whetherbyok_configuredis already true. If it is, ask the user whether to keep their existing provider or switch. -
Pick a provider with the user. We recommend DeepSeek
(
https://api.deepseek.com): fast, cheap, OpenAI-compatible. Ask for the provider key. -
PUT /account/byokwith{ base_url, api_key, enabled: true }. -
POST /account/byok/validate, confirm the round-trip works. Ifok: false, show the user theerrorstring verbatim; the messages are usually actionable ("Invalid API key", "Model not found: …", etc.). -
Tell the user they're set. Their other agents (Claude Code,
Cursor, ChatGPT connector, anything else they've wired up) will
now route their
store_memory/query_memorycalls through this provider automatically.
Caveats & gotchas
- Scopes aren't enforced today. A read-scoped API key
can
PUT /account/byok. We'll tighten this in a future pass; for now, treat any Engram API key as full-access. - Validate before "you're done." The PUT endpoint
doesn't pre-test the credentials; it just stores them. A typo'd
key will only surface on the first real
store_memorycall (with a confusing412from the MCP layer). Always follow PUT with validate. - The agent sees the provider key. Unavoidable for this flow today, since the agent has to POST it. If you build a UX around this, route the key through a credential manager input rather than chat input where possible, and clear the agent transcript afterwards.
- Multi-component overrides are a separate endpoint family.
PUT /account/byok/component/<role>handles per-component model + prompt + (Enterprise) provider overrides. Not documented here yet; ask if you need it.
Error shapes
All four endpoints return JSON with an error string on
failure. Common cases:
401 { "error": "Invalid API key or token" }
401 { "error": "API key required. Use Authorization: Bearer YOUR_API_KEY" }
403 { "code": "email_not_verified" } # every endpoint except /auth/me
400 { "error": "base_url must be http(s)" }
400 { "error": "base_url host 'foo' resolves to a private/loopback address; use a public provider URL" }
500 { "error": "server crypto unavailable: …" } # rare; master key misconfigured on server See also
- All Engram integrations: 34 agents, frameworks, IDEs, and SDKs that talk to the same backend you're configuring.
- Docs: REST + MCP reference for the memory surface itself (
store_memory,query_memory, etc.). - engram-py / engram-js / engram-go: official SDKs for the memory surface.