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

# Developer API

> What the muveya public API covers, its base URL, the conventions every response follows and how this reference is organized.

The muveya public API is a REST surface under `/v1` for server-side integrations: an ERP or purchasing system, a BI tool, a spreadsheet job, an internal dashboard. It authenticates with an **API key** that belongs to exactly one dental clinic, and it reads the same data your team works with in the console.

The API is **read-only**, with one exception: `POST /v1/analytics/exports`, which asks muveya to build a CSV file of the management briefing. Nothing you call on `/v1` creates, changes or deletes an order, a box, a stock movement, a member or a catalog item.

## Base URL

```text theme={null}
https://api.muveya.com
```

Every public path starts with `/v1`, so your first call is `https://api.muveya.com/v1/me`.

## Authentication in one line

```bash theme={null}
curl https://api.muveya.com/v1/me \
  -H "Authorization: Bearer $MUVEYA_API_KEY"
```

The key is sent as a bearer token on every request. It is bound to one dental clinic on the server: no path, query parameter or header ever names a dental clinic, so a key can only read its own. See [Authentication](/docs/en/api-reference/authentication) for how to get a key and keep it safe.

## Console words and API words

The console and the API describe the same things with different words. Keep both in mind when you map API data to what people see on screen.

| In the console | In the API |
| - | - |
| **Dental clinic** (your workspace) | The workspace the key belongs to. `GET /v1/me` returns it as `workspaceId`. It never appears in a path. |
| **Locations** | Clinics: `GET /v1/clinics`, `clinicId` |
| **Warehouses** | Warehouses: `GET /v1/warehouses`, `warehouseId`. A `central` warehouse serves the dental clinic; an `express` warehouse belongs to one location. |
| **Catalog** | Catalog items and categories: `itemId`, `catalogItemId`, `categoryId` |
| **Inventory** | Stock boxes, their balances and their ledger movements: `boxId`, `movementId` |
| **Orders** and **Order approvals** | Orders, their lines and their approval plan: `orderId`, `lineId` |
| **Deliveries** | Fulfillments and custody history, keyed by `orderId` |
| **Reports** | Analytics: metrics, exceptions, consumption, briefing |

## What `/v1` covers

| Operation | Scope | What you get |
| - | - | - |
| `GET /v1/me` | Any valid key | The dental clinic (`workspaceId`), the key `environment` and its `scopes` |
| `GET /v1/clinics` | `clinics:read` | Every location with its name and status |
| `GET /v1/warehouses` | `clinics:read` | Every warehouse with its kind (`central` or `express`) and status |
| `GET /v1/catalog/items` | `catalog:read` | Every catalog item, whatever its status |
| `GET /v1/catalog/items/{itemId}` | `catalog:read` | One catalog item |
| `GET /v1/catalog/categories` | `catalog:read` | Every category |
| `GET /v1/inventory/balances` | `inventory:read` | On hand, reserved and available quantities per box, paginated, filterable by `catalogItemId` and `warehouseId` |
| `GET /v1/inventory/boxes/{boxId}` | `inventory:read` | One box: code, item, warehouse, status, lot, serials, expiry, origin |
| `GET /v1/inventory/boxes/{boxId}/balance` | `inventory:read` | The quantities of one box |
| `GET /v1/inventory/boxes/{boxId}/movements` | `inventory:read` | The ledger movements of one box, oldest first |
| `GET /v1/orders` | `orders:read` | Orders, paginated, filterable by `status` |
| `GET /v1/orders/{orderId}` | `orders:read` | One order with its lines and its approval plan |
| `GET /v1/fulfillments` | `fulfillment:read` | Delivery records, paginated, filterable by `status` |
| `GET /v1/fulfillments/{orderId}` | `fulfillment:read` | The custody history of one order: movements, delivery, receipt and closure |
| `GET /v1/analytics/metrics/{metric}` | `analytics:read` | One operational metric with its evidence |
| `GET /v1/analytics/exceptions` | `analytics:read` | Operational exceptions, highest priority first |
| `GET /v1/analytics/clinics/comparison` | `analytics:read` | Locations compared by order volume |
| `GET /v1/analytics/consumption-trend` | `analytics:read` | Consumption per supply in its own unit over time |
| `GET /v1/analytics/approval-backlog` | `analytics:read` | Pending approvals grouped by age |
| `GET /v1/analytics/cycle-time` | `analytics:read` | Average duration of each order stage |
| `GET /v1/analytics/briefing` | `analytics:read` | The daily or weekly management briefing |
| `POST /v1/analytics/exports` | `analytics:read` | Requests a CSV export of the briefing (the only write) |
| `GET /v1/analytics/exports/{exportId}` | `analytics:read` | The state of an export and, when ready, its download link |

Every scope is explained on [Scopes](/docs/en/api-reference/scopes). The request and response of each operation are documented in the **Endpoints** group of this tab.

## What `/v1` does not do

* It does not create or change anything in your operation: orders, approvals, picking, dispatch, delivery, receipt, stock receipts, use, moves, corrections, counts, catalog edits and team changes are done in the [console](/docs/en/introduction), and some stock tasks from [WhatsApp](/docs/en/whatsapp/overview).
* It does not create or revoke API keys. See [Authentication](/docs/en/api-reference/authentication).
* It does not send events to your systems. There are no webhooks yet; see [Webhooks](/docs/en/api-reference/webhooks) for how to poll instead.
* It never returns order totals or patient references (see [Data that is left out](#data-that-is-left-out)).

## Conventions

**JSON everywhere.** Successful responses are `application/json`. Errors are `application/problem+json` documents with a stable `code` (see [Errors](/docs/en/api-reference/errors)). Field names are English camelCase and enum values are English lowercase words such as `pending_approval`; metric keys are uppercase, such as `LOW_STOCK`. None of them changes with the language.

**Opaque ids.** Every id is a string. Compare ids as strings and never parse them or build them yourself. An id that belongs to another dental clinic behaves exactly like an id that does not exist: the answer is a `404`.

**Absent, not null.** An optional field that has no value, or that your key is not allowed to see, is left out of the response. It is not sent as `null` or as `0`. The exceptions are three analytics fields: `value` (of a metric or a briefing figure) and `averageHours` (of a cycle-time stage) are `null` when there was no sample to measure, and `maxAgeHours` is `null` for the open-ended approval-backlog band.

**Timestamps in UTC.** Dates and times are ISO 8601 strings in UTC with a `Z` suffix, for example `2026-09-15T13:45:00.000Z`. Analytics reports state `"timezone": "UTC"`. Time windows you send (`from`, `to`) are ISO 8601 too.

**Money in minor units.** An amount is an integer in the minor unit of its currency (cents for USD) and always travels with `currency`, an ISO 4217 code: `"cost": 1250, "currency": "USD"` means 12.50 USD. Money appears only in catalog cost fields and in order line `unitCost`, and only for a key with `catalog.cost:read`.

**Quantities in the item unit.** Quantities such as `onHand`, `requestedQty` or `quantityDelta` are integers counted in the item's `unitOfMeasure` (`unit`, `box`, `milliliter` and so on). Quantities of different items or units are never added together by the API.

**Language.** Send `Accept-Language` with `en`, `es` or `pt` (English is the fallback). It changes only text meant for people: the `title` and `detail` of an error, the `label` of each briefing figure, and the labels inside a CSV export. Keys, codes, enum values, numbers and ids stay the same in every language.

**Request ids.** Every response carries an `x-request-id` header. If you send your own `X-Request-Id` with a UUID, muveya reuses it; otherwise it generates one. Error bodies repeat it as `requestId`. Log it on your side and include it when you write to [team@muveya.com](mailto:team@muveya.com).

**Lists.** Every list answers `{ "data": [...], "hasMore": false }`, plus `nextCursor` when there is another page. See [Pagination](/docs/en/api-reference/pagination).

**Rate limits.** Every key has its own request budget and every response reports it in headers. See [Rate limits](/docs/en/api-reference/rate-limits).

**Stability.** Inside `/v1` changes are additive. See [Versioning](/docs/en/api-reference/versioning) and the [changelog](/docs/en/changelog).

## Data that is left out

The server removes data your key may not see before the response is built. Fields are absent, never masked.

| Data | When `/v1` returns it |
| - | - |
| Catalog `cost`, `currency`, `costStatus`, `costMeasurement` | Only with `catalog.cost:read` |
| Order line `unitCost` and `currency` | Only with `catalog.cost:read` |
| Order `value`, `currency` and `approvalPlan.evaluatedValue` | Never. No API scope grants order values. |
| `patientRef` of a clinical order | Never. No API scope grants patient references. |
| Analytics and briefing exports | Counts, quantities and durations only. Never costs, values or patient references. |

An API key is not a team member and it is not limited by location access: it reads **every location and warehouse** of its dental clinic within its scopes. Grant a key only the scopes the integration needs.

## How this reference is organized

* **Overview** (these pages): authentication, a quickstart, scopes, pagination, rate limits, errors, versioning, exports and webhooks.
* **Endpoints**: one page per operation, generated from the muveya OpenAPI contract, in your language. Each page shows the parameters, the response schema, the possible errors and request examples in cURL, JavaScript and Python. The request builder on those pages only prepares a request for you to copy: it never sends it and never stores your key.

<Note>
  The MCP server at `https://api.muveya.com/mcp` uses OAuth sign-in and authorization for AI assistants. See [MCP](/docs/en/mcp/introduction).
</Note>

<CardGroup cols={2}>
  <Card title="Authentication" icon="key" href="/docs/en/api-reference/authentication">
    Get a key, send it and keep it on your server.
  </Card>

  <Card title="Quickstart" icon="rocket" href="/docs/en/api-reference/quickstart">
    Your first calls with cURL, JavaScript and Python.
  </Card>

  <Card title="Scopes" icon="shield-halved" href="/docs/en/api-reference/scopes">
    What each scope lets a key read.
  </Card>

  <Card title="Errors" icon="triangle-exclamation" href="/docs/en/api-reference/errors">
    The problem document and every error code.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.