# Modelvia — Complete API & Agent Reference (llms-full.txt) > Single-file technical reference for human engineers and AI coding agents integrating with Modelvia (`https://api.modelvia.dev`). Covers Project-Key Inference (`POST /v1/chat/completions`, `POST /v1/messages`, `POST /v1/embeddings`), Agentic Endpoints (`POST /v1/decisions`, `POST /v1/web-search`), Immutable Receipts (`GET /v1/requests/{id}`), Server-to-Server Client Integration (`https://api.modelvia.dev/v1/client/*`), and the Model Context Protocol (MCP) Server (`POST https://api.modelvia.dev/mcp` and local `stdio`). --- ## 1. Base URLs, Credentials & Price Contract ### 1.1 Base URLs - **OpenAI SDK Base URL**: `https://api.modelvia.dev/v1` - **Anthropic SDK Base URL**: `https://api.modelvia.dev` - **HTTP & Client API Origin**: `https://api.modelvia.dev` - **Remote MCP Endpoint**: `https://api.modelvia.dev/mcp` ### 1.2 Credential Boundaries Never mix project keys and client integration keys. Every credential is bound on the server to its exact tenant scope: | Credential Type | Prefix | Header | Scope & Authority | | :--- | :--- | :--- | :--- | | **Project Serving Key** | `rbk_<16-hex>_` | `Authorization: Bearer ` or `x-api-key: ` | Scoped to **one project** and **one environment** (`production`, `staging`, `development`, `test`). Used for model inference, embeddings, decisions, web search, model catalog, request receipts, and project-mode MCP tools. | | **Client Integration Key** | `mgt_<16-hex>_` | `Authorization: Bearer ` or `x-api-key: ` | Scoped to **one client business** (e.g., `exampleco`). Used for server-to-server customer/project provisioning, key lifecycle, customer usage/billing/invoices, client margin/analytics reporting, and client-mode MCP tools. | - Keep all keys on your server or in a trusted local agent host environment. Never embed `rbk_*` or `mgt_*` keys in browser JavaScript or mobile bundles. - Key secrets are returned **only once** at mint or rotation time; only their SHA-256 hash is stored. ### 1.3 Monetary Units & Price Contract 2 - **Nano-AUD (`nanoAud`)**: All monetary caps, reservations, and ledger charges are exact integer strings in **nano-AUD**, where: - `1 AUD = 1,000,000,000 nano-AUD` (`"1000000000"`) - `A$0.01 = "10000000"` nano-AUD - `A$50.00 = "50000000000"` nano-AUD - **Price Contract 2 (`priceContractVersion: 2`)**: - `providerCostNanoAud`: Upstream provider token cost in nano-AUD. - `platformFeeNanoAud`: Modelvia platform fee (`ceil(providerCost * platformFeeBasisPoints / 10000)`). - `wholesaleNanoAud`: `providerCostNanoAud + platformFeeNanoAud` (charged by Modelvia to the client business under wholesale billing). - `clientMarkupNanoAud`: Client business markup (`ceil(wholesaleNanoAud * clientMarkupBasisPoints / 10000)`). - `customerGrossNanoAud`: `wholesaleNanoAud + clientMarkupNanoAud` (retail charge to the downstream customer). --- ## 2. Project-Key Serving & Agent Endpoints All routes in this section require a **Project Serving Key** (`rbk_*`). ### 2.1 `GET /v1/models` — Allowed Model Catalog Returns only the models and configurations that the calling project key's account hierarchy (`client -> customer -> project`) permits, together with context limits and active rate-card prices. ```bash curl --fail-with-body https://api.modelvia.dev/v1/models \ -H "Authorization: Bearer $MODELVIA_KEY" ``` Response (`200 OK`): ```json { "object": "list", "priceContractVersion": 2, "currency": "AUD", "unit": "per_1m_tokens", "modes": [ { "id": "auto", "description": "Classifies the prompt and picks the cheapest allowed model that fits." }, { "id": "default", "description": "Balanced everyday route." }, { "id": "flash", "description": "Fastest low-cost route." }, { "id": "max", "description": "Highest-capability reasoning route." } ], "data": [ { "id": "wonder-full", "object": "model", "owned_by": "modelvia", "modality": "text", "context_length": 256000, "max_output_tokens": 65536, "pricing": { "currency": "AUD", "unit": "per_1m_tokens", "inputNanoAudPerMillion": "3200000000", "outputNanoAudPerMillion": "16000000000" }, "configurations": [ { "id": "wonder-full-high", "model": "wonder-full", "reasoningEffort": "high" }, { "id": "wonder-full-xhigh", "model": "wonder-full", "reasoningEffort": "xhigh" } ] } ] } ``` ### 2.2 `POST /v1/chat/completions` — OpenAI-Compatible Chat Completions Supports synchronous JSON and Server-Sent Events (`"stream": true`). Works with official OpenAI Node/Python SDKs by setting `baseURL: "https://api.modelvia.dev/v1"`. #### Model Selection & Configuration Pinning (`modelvia_configuration`) 1. **Mode Aliases** (`"auto"`, `"default"`, `"flash"`, `"max"`): Let Modelvia classify the prompt and select the optimal permitted model and configuration. 2. **Concrete Model IDs** (e.g., `"wonder-full"`, `"opus-5.5"`, `"deepseek-v4.1-flash"`, `"glm-5.1"`, `"qwen-3.6-plus"`): Route directly to that model's default configuration. 3. **Explicit Configuration Pinning (`modelvia_configuration`)**: - You may pass `"modelvia_configuration"` (for example `"wonder-full-high"`, `"wonder-full-xhigh"`, `"opus-5.5-medium"`, `"opus-5.5-high"`, `"flash-high"`) **only when `"model"` is a concrete model ID** that owns that configuration. - Passing `"modelvia_configuration"` with a mode alias (`"auto"`, `"default"`, `"flash"`, `"max"`) is refused with `400 modelvia_configuration_requires_concrete_model`. - Passing a configuration that belongs to a different model is refused with `400 invalid_modelvia_configuration`. ```bash curl --fail-with-body https://api.modelvia.dev/v1/chat/completions \ -H "Authorization: Bearer $MODELVIA_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "wonder-full", "modelvia_configuration": "wonder-full-high", "max_tokens": 1024, "messages": [ {"role": "system", "content": "Answer concisely."}, {"role": "user", "content": "Give three failure modes of webhook retries."} ] }' ``` #### Response Headers (Every Inference Call) - `X-Request-Id`: UUID of the ledger record. Query `GET /v1/requests/{X-Request-Id}` for the full accounting receipt. - `X-Modelvia-Requested-Model`: The `"model"` string sent by the caller. - `X-Modelvia-Model`: The concrete model ID that served the request. - `X-Modelvia-Configuration`: The resolved configuration ID (e.g., `wonder-full-high`). - `X-Modelvia-Routing-Reason`: Why the router chose this configuration. #### Request Parameters (`POST /v1/chat/completions`) - Required: `model` (string), `messages` (non-empty array of `{role, content}`). - Supported optional parameters: `max_tokens` / `max_completion_tokens`, `temperature`, `top_p`, `stop`, `stream`, `stream_options`, `tools`, `tool_choice`, `parallel_tool_calls`, `response_format`, `reasoning_effort` (`"low" | "medium" | "high" | "xhigh"`), `modelvia_configuration`, `metadata`, `user`. - Unsupported OpenAI legacy parameters (`n > 1`, `logprobs`, `top_logprobs`, `logit_bias`, `presence_penalty`, `frequency_penalty`, `seed`, `service_tier`, `store`, `prediction`, `audio`, `modalities`, `functions`, `function_call`) are refused with `400 invalid_request` before admission. ### 2.3 `POST /v1/messages` — Anthropic Messages API Native endpoint for Anthropic SDKs and Claude Code (`ANTHROPIC_BASE_URL=https://api.modelvia.dev`). Accepts `x-api-key: ` or `Authorization: Bearer `. ```bash curl --fail-with-body https://api.modelvia.dev/v1/messages \ -H "x-api-key: $MODELVIA_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "auto", "max_tokens": 1024, "messages": [ {"role": "user", "content": "Explain idempotent ledger admission in two sentences."} ] }' ``` ### 2.4 `POST /v1/embeddings` — Embeddings API OpenAI-compatible vector generation for concrete embedding routes (such as `qwen3-embedding-8b`). Mode aliases (`"auto"`) are not valid on `/v1/embeddings` (`400 model_modality_mismatch`). ```bash curl --fail-with-body https://api.modelvia.dev/v1/embeddings \ -H "Authorization: Bearer $MODELVIA_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "qwen3-embedding-8b", "input": ["Invoice reconciliation rule", "Idempotent key rotation"] }' ``` ### 2.5 `POST /v1/decisions` — Structured Multi-Option Decision Engine Evaluates `2..20` candidate options against weighted criteria or a free-form rubric and returns a validated JSON ranking with scores, winner, rationale, and receipt metadata. Charges the project key and requires `"confirm": true`. ```bash curl --fail-with-body https://api.modelvia.dev/v1/decisions \ -H "Authorization: Bearer $MODELVIA_KEY" \ -H "Content-Type: application/json" \ -d '{ "confirm": true, "question": "Which queue strategy fits a single-writer financial ledger?", "options": [ {"id": "serial-lease", "label": "Single writer with DB lease fence"}, {"id": "multi-master", "label": "Optimistic multi-writer without fencing"} ], "criteria": [ {"name": "correctness", "weight": 5, "description": "Prevents double-spend and split-brain"}, {"name": "simplicity", "weight": 3, "description": "Operational clarity during incident recovery"} ], "model": "auto" }' ``` ### 2.6 `POST /v1/web-search` — Grounded Web Search & Synthesis Executes a grounded web search and synthesizes an answer with structured source citations. Charges the project key and requires `"confirm": true`. ```bash curl --fail-with-body https://api.modelvia.dev/v1/web-search \ -H "Authorization: Bearer $MODELVIA_KEY" \ -H "Content-Type: application/json" \ -d '{ "confirm": true, "query": "Australian Payton / PayID business invoice settlement standards", "maxResults": 5, "model": "auto" }' ``` ### 2.7 `GET /v1/requests/{id}` — Request Accounting Receipt Returns the immutable accounting and routing record for a single request. Never returns the prompt or completion text. Scoped strictly to the project key's own project (`404 request_not_found` if foreign or missing). ```bash curl --fail-with-body "https://api.modelvia.dev/v1/requests/$REQUEST_ID" \ -H "Authorization: Bearer $MODELVIA_KEY" ``` Response (`200 OK`): ```json { "id": "6f9d2a10-4b3a-4d1c-9e2a-112233445566", "project": "exampleco-acme-prod", "keyId": "0123456789abcdef", "requestedModel": "wonder-full", "model": "wonder-full", "configuration": "wonder-full-high", "routingReason": "pinned_configuration", "rateCardVersion": 4, "priceContractVersion": 2, "state": "settled", "ReservedNanoAud": "12800000", "SettledNanoAud": "4160000", "Usage": { "input_tokens": 120, "output_tokens": 236, "cache_read_tokens": 0, "cache_write_tokens": 0 }, "commercialBreakdown": { "priceContractVersion": 2, "providerCostNanoAud": "4160000", "platformFeeNanoAud": "208000", "wholesaleNanoAud": "4368000", "clientMarkupNanoAud": "873600", "customerGrossNanoAud": "5241600" } } ``` ### 2.8 Idempotency & Retry Rules for Inference - **With `Idempotency-Key: `**: - Persist a unique key (`1..128` ASCII printable characters) before sending the request, and reuse that exact key if you must retry after a lost connection. - If the original request is still running: returns `409 request_in_flight`. - If the original request already settled with the **same** payload: returns `409 request_already_processed` (or replays the receipt header `X-Request-Id` so you can inspect `GET /v1/requests/{id}`). - If you reuse the same `Idempotency-Key` with a **different** payload, route, or model: returns `409 idempotency_conflict`. - **Without `Idempotency-Key` (Header-less rule)**: - While an identical request is in flight or its response was never delivered because the connection dropped, resending the identical body attaches to that record instead of double-charging. Once a response has been delivered over the socket, a subsequent identical call without `Idempotency-Key` is admitted as a fresh request. ### 2.9 Inference & Admission Refusal Codes | HTTP Status | Error Code | Meaning & Caller Action | | :--- | :--- | :--- | | `400` | `invalid_request`, `invalid_json` | Malformed JSON or unsupported parameter. Fix the request body before retrying. | | `400` | `model_modality_mismatch` | Calling `/v1/chat/completions` with an embeddings model or `/v1/embeddings` with a text model or `"auto"`. | | `400` | `modelvia_configuration_requires_concrete_model` | `"modelvia_configuration"` was sent with `"model": "auto" | "default" | "flash" | "max"`. Specify the concrete `"model"` ID. | | `400` | `invalid_modelvia_configuration` | The requested configuration does not exist or does not belong to the specified model. | | `400` | `confirmation_required` | `/v1/decisions`, `/v1/web-search`, or a paid/mutating MCP tool was called without `"confirm": true`. | | `401` | `missing_api_key`, `invalid_api_key`, `revoked_api_key` | Missing, unknown, or revoked credential. Do not retry with the same key. | | `403` | `account_inactive`, `environment_not_allowed`, `model_not_allowed` | The client, customer, or project is inactive, or the key's environment/model is outside parent policy. | | `409` | `customer_terms_required`, `billing_company_inactive` | Commercial terms or billing company are not yet active for this account. | | `409` | `request_in_flight`, `request_already_processed`, `idempotency_conflict` | Idempotency state machine refusal. Check `GET /v1/requests/{X-Request-Id}` rather than re-billing. | | `413` | `request_too_large`, `context_length_exceeded` | Payload exceeds byte limit or token context window. | | `422` | `reasoning_context_expired` | A Claude tool-loop continuation whose earlier turn's signed reasoning cannot be sent back; refused before anything is reserved. The error object adds `reason` (`not_found`: not held, e.g. expired or after a restart; `changed`: the turn sent back differs; `other_scope`: held for another project or environment; `host_changed`: the route no longer lists the turn's host) and `turn` (zero-based tool-call turn of the active loop). Resend turns exactly as received with the same key, or restart the loop. | | `422` | `request_cap_exceeded`, `company_cap_exceeded`, `account_cap_exceeded` | Estimated reservation exceeds the per-request or monthly nano-AUD cap. | | `429` | `company_concurrency_exceeded`, `account_concurrency_exceeded`, `rate_limited` | In-flight concurrency limit reached. Honour `Retry-After` and back off. | | `502` / `504` | `provider_error`, `provider_timeout`, ` outcome_unknown` | Upstream provider failed or timed out. If outcome is unknown, the reservation stays held until reconciled—do not blindly replay without your saved `Idempotency-Key`. | --- ## 3. Client Integration API (`/v1/client/*`) All `/v1/client/*` endpoints authenticate with your server-side **Client Integration Key** (`Authorization: Bearer $MODELVIA_CLIENT_KEY`, prefix `mgt_`). Every call is strictly scoped to the client business bound to that key. Machine-readable OpenAPI 3.1 contract: `https://modelvia.dev/modelvia-client.openapi.json`. ### 3.1 Endpoint Summary | Method & Path | Purpose | Retry Semantics | | :--- | :--- | :--- | | `GET /v1/client/payment-settings` | Read client binding (`clientId`, `version`, `customerPayment`) | Safe read; use as startup connection check | | `POST /v1/client/payment-settings` | Update `customerPayment` (`square`, `direct`, `both`, `disabled`) | Optimistic lock via `version` + `clientId` | | `GET /v1/client/customers` | List all customers, projects, and public key records under this client | Safe read | | `POST /v1/client/customers` | Create a customer under this client | Idempotent if body is identical (`200`); `409 account_version_conflict` if modified | | `POST /v1/client/customers/{customer}/projects` | Create a project under `{customer}` | Idempotent if body is identical (`200`); `409 account_version_conflict` if modified | | `GET /v1/client/customers/{customer}/projects/{project}/keys` | List public key records for `{project}` (never returns secrets) | Safe read | | `POST /v1/client/customers/{customer}/projects/{project}/keys` | Mint a new project serving key (`rbk_*`), returned **once** | **Non-idempotent**: repeating mints a second valid key | | `POST /v1/client/customers/{customer}/projects/{project}/keys/{key}/rotate` | Revoke `{key}` and mint a replacement in one transaction | **Non-idempotent**: repeating original URL returns `409 key_revoked` | | `POST /v1/client/customers/{customer}/projects/{project}/keys/{key}/revoke` | Permanently revoke `{key}` | Idempotent; preserves original `revokedAt` | | `GET /v1/client/customers/{customer}/usage-summary?period=YYYY-MM` | Customer-safe monthly totals (`requests`, `tokens`, `money.customerNetNanoAud`, `remainingNanoAud`) | Safe read | | `GET /v1/client/customers/{customer}/analytics?period=YYYY-MM` | Customer-safe breakdown (`byDay`, `byModel`, `byProject`, paged `recentRequests`) | Safe read; paginate requests with `requestsLimit` & `requestsCursor` | | `GET /v1/client/customers/{customer}/billing?period=YYYY-MM` | Customer-safe billing view, invoices, and surface-aware payment eligibility | Safe read | | `GET /v1/client/customers/{customer}/invoices/{invoice}` | Single customer-safe invoice JSON | Safe read | | `GET /v1/client/customers/{customer}/invoices/{invoice}/document` | Printable HTML invoice document | Safe read (`text/html`) | | `POST /v1/client/customers/{customer}/invoices/{invoice}/checkout` | Start a Square checkout for an issued invoice (empty `{}` body) | Idempotent while checkout is active; hold state on unknown failure | | `POST /v1/client/customers/{customer}/invoices/{invoice}/external-payment` | Record an verified full external payment (`reference`, `amountCents`, `currency`, `receivedAt`) | Bookkeeping only; never moves money | | `GET /v1/client/margins?period=YYYY-MM` | **Client-internal** wholesale vs retail margin report across all customers and models | Safe read (never expose to downstream customers) | | `GET /v1/client/analytics?period=YYYY-MM` | **Client-internal** full usage and commercial analytics across the client business | Safe read (never expose to downstream customers) | ### 3.2 Hierarchy & Cap Inheritance Rules - Account IDs (`customer.id`, `project.id`) share a global namespace across the platform (`^[a-z0-9][a-z0-9._-]{0,62}[a-z0-9]$`). Always prefix IDs with your client slug (e.g. `exampleco-acme`, `exampleco-acme-prod`); colliding with another tenant returns `409 account_id_unavailable`. - **Child accounts narrow their parent and can never widen it**: - `monthlyCapNanoAud` and `requestCapNanoAud` must be `<= parent.monthlyCapNanoAud` (`400 invalid_caps`). - `maxConcurrent` must be `<= parent.maxConcurrent` (`400 invalid_concurrency`). - `allowedModels` must be a non-empty subset of `parent.allowedModels` (`400 invalid_model_policy`). - Never send `version` on create requests (`400 invalid_fields`), and never send `billingCompanyId` (`403 billing_binding_operator_only`). --- ## 4. Model Context Protocol (MCP) Server Modelvia hosts a stateless Streamable HTTP MCP server at `POST https://api.modelvia.dev/mcp` (JSON-RPC 2.0) and also ships a local `stdio` runner (`npm --prefix managed-gateway run mcp`). ### 4.1 Connecting an Agent Host #### Claude Code ```bash claude mcp add --transport http modelvia https://api.modelvia.dev/mcp \ --header "Authorization: Bearer $MODELVIA_KEY" ``` #### Cursor (`.cursor/mcp.json`) ```json { "mcpServers": { "modelvia": { "url": "https://api.modelvia.dev/mcp", "headers": { "Authorization": "Bearer ${MODELVIA_KEY}" } } } } ``` #### OpenClaw ```json { "mcpServers": { "modelvia": { "type": "http", "url": "https://api.modelvia.dev/mcp", "headers": { "Authorization": "Bearer ${MODELVIA_KEY}" } } } } ``` #### Hermes (`~/.hermes/config.yaml`) ```yaml mcp_servers: modelvia: url: https://api.modelvia.dev/mcp headers: Authorization: Bearer ${MODELVIA_KEY} enabled: true ``` ### 4.2 Project-Key MCP Tools (`rbk_*` key — 6 tools) When authenticated with a project serving key (`rbk_*` over HTTP, or `MODELVIA_API_KEY` in `stdio` mode), the MCP server exposes 6 tools: | Tool Name | Cost / Safety | Underlying Gateway Operation | | :--- | :--- | :--- | | `models_list` | Read-only, free | `GET /v1/models` — lists allowed models, context limits, configurations, and AUD rate card. | | `request_receipt` | Read-only, free | `GET /v1/requests/{id}` — reads the immutable accounting receipt for one request ID. | | `chat_completion` | Paid (`confirm: true`) | `POST /v1/chat/completions` — runs one non-streaming chat completion (supports `model`, `messages`, `maxTokens`, `temperature`, `reasoningEffort`, `idempotencyKey`, `confirm: true`). | | `embeddings` | Paid (`confirm: true`) | `POST /v1/embeddings` — generates embeddings for one or more strings (`model`, `input`, `encodingFormat`, `dimensions`, `idempotencyKey`, `confirm: true`). | | `decisions` | Paid (`confirm: true`) | `POST /v1/decisions` — structured multi-option decision analysis (`question`, `options`, `criteria`, `rubric`, `context`, `model`, `idempotencyKey`, `confirm: true`). | | `web_search` | Paid (`confirm: true`) | `POST /v1/web-search` — grounded web search with cited sources (`query`, `context`, `domains`, `recency`, `maxResults`, `model`, `idempotencyKey`, `confirm: true`). | ### 4.3 Client-Key MCP Tools (`mgt_*` key — 12 tools) When authenticated with a client integration key (`mgt_*` over HTTP, or `PLATFORM_CLIENT_KEY` in `stdio` mode), the MCP server exposes 12 client-scoped tools: | Tool Name | Safety | Underlying Gateway Operation | | :--- | :--- | :--- | | `client_customers_list` | Read-only | `GET /v1/client/customers` — lists customers, projects, and public key records. | | `customers_create` | Mutating (`confirm: true`) | `POST /v1/client/customers` — provisions a downstream customer record. | | `projects_create` | Mutating (`confirm: true`) | `POST /v1/client/customers/{customerId}/projects` — provisions a project under a customer. | | `keys_list` | Read-only | `GET /v1/client/customers/{customerId}/projects/{projectId}/keys` — lists public key metadata. | | `keys_mint` | Mutating (`confirm: true`) | `POST /v1/client/customers/{customerId}/projects/{projectId}/keys` — mints a new project key (secret returned once). | | `keys_rotate` | Mutating (`confirm: true`) | `POST .../keys/{keyId}/rotate` — rotates a project key and returns the replacement secret once. | | `keys_revoke` | Mutating (`confirm: true`) | `POST .../keys/{keyId}/revoke` — revokes a project key. | | `customer_analytics` | Read-only | `GET /v1/client/customers/{customerId}/analytics` — customer-safe usage and recent requests. | | `customer_billing` | Read-only | `GET /v1/client/customers/{customerId}/billing` — customer-safe billing snapshot and invoices. | | `customer_usage_summary` | Read-only | Customer-safe period summary (`summary` and `coverage` projection). | | `client_margins` | Read-only | `GET /v1/client/margins` — client-internal wholesale cost, retail revenue, and net margin report across all customers and models. | | `client_analytics` | Read-only | `GET /v1/client/analytics` — client-internal full usage and commercial breakdown across the client business. |