Mirobody’s hosted API is OpenAI-compatible. Point any OpenAI SDK at the base URL below, pass an mb_live_* key, and call /v1/chat/completions — the agent answers from each end-user’s real, structured health data, with traceable citations. The same engine also runs self-hosted — build the open-source C++ engine with ./build.sh and run it yourself; the hosted API adds managed storage, keys, and billing on top.
You create keys, watch usage, and try an interactive Playground in the developer console. These docs are the API reference.
Base URL
Mirobody is global-first. The live integration environment today is the global test cluster — point your SDK here:
https://test-mirobody-api.thetahealth.ai/v1
Its data is non-production and may be reset at any time, so never put real end-user data there. The global production API (https://mirobody-api.thetahealth.ai/v1) and the China production API (https://mirobody-api.thetahealth.cn/v1) are still provisioning — though the China test environment (consumer app + developer console) is now live. See Regions for the full host matrix and live status.
Authenticate every call with an mb_live_* key — see Authentication.
Authentication
| Surface | Scheme | How to get it |
|---|
/v1/* (the API) | Authorization: Bearer mb_live_* | Create a key in the console → API Keys. The secret is shown once. |
/api/console/* (console) | Session JWT (login) | Email-code or WeChat login in the console. |
/v1 keys are not JWTs — they’re long-lived API keys scoped to your account. Never ship them in client-side code.
OpenAI compatibility
Standard OpenAI fields work as-is; Mirobody extensions ride along in extra_body (Python SDK) or as plain top-level JSON (curl/fetch).
| Fields |
|---|
| Standard | model, messages, stream, user |
| Mirobody extensions | retention, session_id, health_context, mcp_servers, include_tool_steps |
| Response | choices[0].message.content (+ reasoning_content, tool_steps), usage, plus top-level health_records / citations |
Sampling parameters are accepted for compatibility, but the agent runs its own generation. max_tokens is accepted but not enforced — the agent controls output length. temperature, top_p, stop, and seed are accepted but ignored. n other than 1 is also not enforced — the response still returns a single choice.
Multi-tenancy: the user field
Every request carries a user string — the tenant-isolation key. The backend maps (your account, user) to an internal Subject; each user you pass is fully isolated from the others. Pass each end-user’s stable id as user and their data never crosses over. Omit it and the call falls back to your account’s default Subject. Subjects are not consumer accounts — they’re invisible to the Mirobody app and to other developers.
Data retention
Anything you supply (structured records, uploaded files, inline health_context) carries a retention that decides how long it’s kept:
retention | Meaning | Lifetime |
|---|
permanent | Kept until explicitly deleted (default for inline health_context) | Permanent |
session | Bound to session_id, dropped when the session ends | ≤ 24h, or DELETE /v1/sessions/{id} |
1d | Auto-deleted within a day | ≤ 24h |
6h | Auto-deleted 6 hours after write | ≤ 6h |
2h | Auto-deleted 2 hours after write | ≤ 2h |
1h | Auto-deleted 1 hour after write | ≤ 1h |
none | Used for this request only, never persisted | Deleted in finally |
Accepted values are permanent (a.k.a. persistent) | 1h | 2h | 6h | 1d | session | none. Any time-bounded grain is hard-capped at ≤ 24h.
On POST /v1/data the retention field is required — there is no default; omitting it returns 400 (code: invalid_retention). For inline health_context on POST /v1/chat/completions, it defaults to permanent.
none is meant for inline health_context (use-and-forget). On POST /v1/data it is rejected with HTTP 400 (writing to the store but asking not to store is contradictory) — to use-and-forget structured data, attach it as health_context with retention=none on POST /v1/chat/completions instead. The agent only ever reads the Subject’s currently-unexpired data.
Citations
Answers are explainable and checkable — not “sounds plausible,” but “this conclusion came from that record of yours.” Today the traceability is carried by health_records (a top-level array of {tool, data} pairs — the actual tool outputs the answer relied on). The citations array is reserved for record:N-style refs and is currently returned empty, so read health_records.
Errors use the OpenAI-style envelope:
{
"error": {
"message": "`retention` is required (days int or 'persistent').",
"type": "invalid_request_error",
"code": "invalid_retention",
"param": "retention"
}
}
type is consistently invalid_request_error; code and param identify the specific problem. Observed (status, code) pairs:
| HTTP | code | When |
|---|
400 | invalid_retention (field-specific) | Malformed request / missing required field — param names the field |
401 | null | Missing or malformed Authorization header |
401 | invalid_api_key | Bad or revoked mb_live_* key |
404 | — | Unknown resource (e.g. file_key / session_id) |
429 | — | Rate limit — see Rate Limits |
500 | internal_error | Unexpected server-side failure |
502 | — | Upstream agent error — transient; retry with backoff |
In streaming, an upstream failure arrives as an SSE error frame before the stream ends.
Endpoints at a glance
| Method | Path | Purpose |
|---|
GET | /v1/models | List capability tiers (mirobody-flash / mirobody-expert) |
POST | /v1/chat/completions | OpenAI-compatible health chat (stream, health_context, mcp_servers) |
POST · GET | /v1/data | Write / read structured records |
POST · GET | /v1/files | Upload & parse files (OCR / Excel), list, fetch parsed text |
DELETE | /v1/sessions/{id} | End a session (see caveats — session-record purge is not yet effective) |
GET·POST·DELETE | /api/console/keys | Manage API keys (JWT) |
GET | /api/console/usage | Usage: requests / tokens / cost (JWT) |