> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mirobody.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# API Overview

> OpenAI-compatible health-data API — base URL, auth, multi-tenancy, retention, errors.

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](/en/index) 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](https://test-platform.mirobody.ai/). 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:

```text theme={null}
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](/en/api-reference/regions/overview) for the full host matrix and live status.

Authenticate every call with an `mb_live_*` key — see [Authentication](#authentication).

## Authentication

| Surface                    | Scheme                            | How to get it                                                                                                                 |
| -------------------------- | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `/v1/*` (the API)          | `Authorization: Bearer mb_live_*` | Create a key in the [console](https://test-platform.mirobody.ai/developer/keys) → **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` |

<Note>
  **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.
</Note>

## 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`.

## Error format

Errors use the OpenAI-style envelope:

```json theme={null}
{
  "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](/en/api-reference/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`](/en/api-reference/models)                | List capability tiers (`mirobody-flash` / `mirobody-expert`)            |
| `POST`                | [`/v1/chat/completions`](/en/api-reference/chat)        | OpenAI-compatible health chat (stream, `health_context`, `mcp_servers`) |
| `POST` · `GET`        | [`/v1/data`](/en/api-reference/data)                    | Write / read structured records                                         |
| `POST` · `GET`        | [`/v1/files`](/en/api-reference/files)                  | Upload & parse files (OCR / Excel), list, fetch parsed text             |
| `DELETE`              | [`/v1/sessions/{id}`](/en/api-reference/sessions)       | End a session (see caveats — session-record purge is not yet effective) |
| `GET`·`POST`·`DELETE` | [`/api/console/keys`](/en/api-reference/console/keys)   | Manage API keys (JWT)                                                   |
| `GET`                 | [`/api/console/usage`](/en/api-reference/console/usage) | Usage: requests / tokens / cost (JWT)                                   |
