openapi: 3.0.0 info: description: >- 0G Router is an API gateway between users and the decentralized 0G Compute Provider network. It provides unified access, fee collection, and intelligent routing for AI inference services. ## Balance model: Router ledger vs Payment Layer vault User funds live in the shared 0G Payment Layer (PL) vault — a multi-app funding pool that other 0G products also draw from. Router does not drain a user's full vault balance up front; it pulls small amounts on demand into its own ledger as inference is consumed. As a result, the `/v1/account/balance` endpoint returns the **Router ledger only** (`deposit_balance + credit_balance`). It deliberately excludes the PL vault, because vault balance is shared across consumer apps and is not yet committed to Router. ### Picking an endpoint: `/balance` vs `/funds` If you just want one **display** balance that already combines both sides, call `/v1/account/funds`. Router does the aggregation server-side and returns a net `total` = `(deposit + credit − pending_charge) + vault_balance`, plus the breakdown. Unlike `/balance`'s `total_balance` (Router-ledger-only, always `≥ 0`), `/funds.total` includes the full vault and **may be negative** when the user owes Router (`pending_charge` exceeds available funds) — render that as "owed to Router". Use `/v1/account/balance` (not `/funds`) for settlement / SDK / mgmt-key integrations that depend on the Router-ledger-only, `≥ 0` figure. And note `/funds.total` is a wallet-**display** number: it is **not** the admission figure — the "can I submit a request" calculation below applies `vault_ratio` and is computed separately. ### Admission rule An inference request is admitted whenever either side has enough funds: - Router ledger covers `min_cost`, **or** - `vault_balance × vault_ratio − pending_charge >= min_cost` (deferred path; the shortfall is recorded as `pending_charge` and cleared from the next PL pull). Otherwise the request returns `402 insufficient_balance`. ### Auto-pull from the vault (mainnet values) After a user's **first successful inference charge**, the account becomes pull-eligible. A background worker then keeps the Router ledger topped up from the PL vault: | Parameter | Mainnet value | Meaning | |---|---|---| | Scan interval | 3 s | How often Router checks each pull-eligible account | | Low watermark | 0.1 0G | Pull triggers when effective Router balance drops below this | | High watermark | 0.5 0G | Target balance after a pull (≈ amount pulled per cycle) | | Vault ratio | 0.5 | Only 50% of the PL vault counts toward admission (safety margin for the shared pool) | | min_cost | 0.01 0G | Minimum cost a single request must be able to cover | Accounts that have only deposited to the vault but never sent an inference request will not trigger a pull. ### Predicting "can submit" on the client To gate a submit button before the first request, combine both sides: ``` available = router.total_balance + max(0, vault_balance × vault_ratio − pending_charge) ``` Use `vault_ratio` from the table above (`0.5` on mainnet). Allow submission when `available > 0`. There is no need to mirror the `min_cost` floor on the client — Router enforces it at admission time and returns `402` if the request cannot be covered. ## Changelog `### Unreleased` lists API changes merged but not yet live on mainnet; on release each is cut into a dated `### vX.Y — YYYY-MM` section. Endpoint and field stability is marked inline with `[beta]`. ### Unreleased - **`POST /v1/messages` — a model that does not speak the Anthropic wire format now returns `400`, not `503`** — asking `/v1/messages` for a model whose endpoints only serve the OpenAI format used to fail with `503 "No available providers for this request"`, byte-for-byte the error a real outage returns. Since SDKs treat `5xx` as retryable and `4xx` as final, a Messages-API-only client spent its whole retry budget on a request no retry can satisfy — the mismatch is permanent — and then surfaced it as an outage rather than as "use the other endpoint". It is now a `400 invalid_request_error` naming the model and the formats it does serve: `model "some-model" is not available on the anthropic API format (supported: openai); use POST /v1/chat/completions instead`. **If you branch on status codes, add a `400` arm here** — this condition previously arrived as `503`. Check a model's formats up front via `supported_formats` on `GET /v1/models`. Unchanged: a genuine supply outage still returns `503`, an unknown model still `404`, and a model reachable on the Anthropic format routes exactly as before. - **`POST /v1/chat/completions`, `/v1/messages`, `/v1/images/*`, `/v1/audio/transcriptions` — the `ZG-Res-Key` response header now carries the PROVIDER's response id, passed through verbatim, instead of a router-generated value** — it lets a client independently verify the provider's TEE signature for that specific response against the provider's signature endpoint. Two behavioural notes: it is now **present only when the provider returns one** (the router previously always emitted a value), so treat its absence as "no provider response id available" rather than an error; and its value is the provider's own — opaque and provider-scoped, not a router-owned token. The router's own stable, always-present per-response identifier is unchanged: read **`X-Request-ID`** (echoed on every response, and the value recorded in your usage history) when you need a handle the router will recognise. No request contract change. - **`POST /v1/videos`, `GET /v1/models` — documented: `size` and `seconds` do not mean what the OpenAI Video API means by them** — no behaviour change; the behaviour was undocumented and reads as a bug when you meet it. The request shape is OpenAI's, but the models behind it are not, and where a model's own limits differ they win. **`size` names a resolution TIER, not output dimensions**, because a video model advertises the tiers it is PRICED at rather than arbitrary sizes. Two spellings are accepted and they are not equivalent: **pixel dimensions** (`1280x720` — OpenAI's spelling, and what an SDK sends) select the **aspect ratio only, and only for text-to-video**, so asking a 2K-only model for `1280x720` returns a **2560x1440** clip billed at the 2K rate rather than a 720p one; for **image-to-video they have no effect at all**, since the aspect ratio follows your reference image (a tier name still selects the tier there). **A tier name** (`2K`) addresses the tier directly — send only names that appear in that list, since it is the set we can price and it grows as a model adds tiers. Pixel dimensions are the safe thing to send blind; a tier name is what to send when you must have a specific tier. Read the tiers from `pricing.variants[].dimensions.resolution` on `GET /v1/models`. **`seconds` is clamped to the model's supported range in BOTH directions, silently**: below the minimum you get and pay for the minimum, above the maximum you get the maximum, and neither errors — so a request outside the range costs something other than what you asked for. Omitting either field is the recommended default. For both, the **submit** response echoes what you sent while the **poll** response reports what was actually rendered, so reconcile against the poll. Per-model limits (supported range, tiers, prompt length, accepted reference-image formats and dimensions) are stated in each model's `description`; a request that violates one is rejected by the model provider, so that error text and any numeric code in it are theirs. Billing is unaffected throughout — you are billed for the clip actually produced, at the per-second price published for its tier. **[beta]** - **`GET /v1/videos/{id}`, `GET /v1/async/jobs/{id}` — fixed: `x_0g_trace.billing` on a re-poll now reports the amount you were CHARGED, not a fresh quote** — an async job's trace is returned on every poll, not only the one that settles the charge. Polls after settlement were re-deriving the fee at the CURRENT price instead of reading the booked figure, so the reported cost could drift away from the charge over time. It drifts whenever the provider prices in USD, because its native-token rate is derived from a moving FX pair: one clip's trace read `6794472400000000000` when it settled and `6696290550000000000` five hours later, a 1.45% gap against a charge that had not changed. **No money was ever wrong** — the ledger, `GET /v1/account/usage/history` and your balance always agreed, and every one of them still does — but the trace is what many clients reconcile spend against, so it now carries the booked amount. The wire shape is unchanged (`input_cost` / `output_cost` / `total_cost`, `currency` on USD traces), and the settling poll is unaffected: it already reported the charge it had just written. Reconciling off `GET /v1/account/usage/history` was, and remains, the authoritative route. If the booked figure cannot be read the response falls back to the previous behaviour rather than failing your poll. - **`POST /v1/videos`, `GET /v1/videos/{id}`, `GET /v1/videos/{id}/content` — async video generation** — three new endpoints add video as a modality, in the OpenAI Video API shape. `POST /v1/videos` accepts `{model, prompt, seconds, size}` as JSON or as `multipart/form-data` (so an uploaded first-frame image can drive image-to-video) and returns `{id, status, provider_address}`; `GET /v1/videos/{id}` reports status and is what settles the charge once the job completes; `GET /v1/videos/{id}/content` streams the finished file. On both `GET`s **`provider_address` is optional** — it is resolved from the job id — so an OpenAI-native client can poll and download with no query parameters. Because generation takes minutes, billing happens at completion rather than at submit, and on the **duration actually delivered** — your requested `seconds` becomes the billing basis only when the provider's own figure is unusable — it completed the job but reported no duration at all (so that a delivered clip is never served free), or it reported one implausibly larger than you asked for, which is capped. Both cases are logged as degraded. The cap bounds the DURATION at a small multiple of what you requested, not the fee: on a `video_clip` table the price is a step function of duration, so a capped duration can still land on a dearer row than the one you asked for. The same precedence applies to `size`: the resolution the price is keyed on is the one the provider reports, falling back to the `size` you asked for when the provider does not echo it, which is the normal case for some upstreams — so a resolution-priced model is billed at its own tier rather than at the table's most expensive one. Two consequences worth coding against: the content endpoint **charges before it streams** if you never polled, so bytes are never delivered unbilled; and each account may hold only a small number of unfinished video jobs at once, so a submit can return **`429`** with code `video_jobs_in_flight_limit` while earlier clips are still rendering. **`seconds` is REQUIRED** — the one place this endpoint deviates from the upstream OpenAI Video API, where it is optional. Billing is per delivered second and the delivered figure comes from the provider, so your declared duration is the only reference the router can sanity-check it against; without one, a provider misreporting its units could charge orders of magnitude more. Omitting it returns `400` `invalid_request` with "seconds is required" — note this applies to the OpenAI SDK path too, including multipart image-to-video. A `seconds` value the model has no price for is likewise rejected up front with `400` and the list of durations that are available, rather than failing after the clip was generated. Other job states: **`404`** `async_job_not_found` on either `GET` means this router has no such job id — it never existed, or you pinned a `provider_address` other than the one that owns it — so re-check the id (and drop the pin, since it is resolved for you) rather than retrying; **`409`** `video_not_ready` from the content endpoint means keep polling; **`424`** `video_job_failed` means the job died at the provider and has no output. A USD-settling account can use video only when the model publishes a USD video price (`pricing_usd.video` or `pricing_usd.variants`); otherwise submit returns `501` `usd_not_supported` rather than a mis-priced charge. **[beta]** - **`GET /v1/account/usage/history` — `request_id` VALUE format differs for VIDEO usage rows** — the `request_id` on a usage row for a video job is `async-<16 hex>-`, while every other async modality uses `async-<8 chars of the provider address>-`. The field, its type and the `async-` prefix are unchanged, so a `LIKE 'async-%'` filter or an exact-match lookup still behaves identically. Only one thing breaks: if you PARSE the middle segment expecting the provider's address suffix, it will not match on a video row — read `provider_address` from the row itself instead, which is the stable way to get it for every modality. Nothing else about the field changed, and no other endpoint's `request_id` changed. **[beta]** - **`GET /v1/models`, `GET /v1/providers` — `pricing.video` and `pricing.variants`; `GET /v1/service-types` — `video-generation`** — the pricing object gains **`video`**, the flat price per generated second for a single-rate video model, and **`variants`**, a list of priced request shapes for a model whose price depends on the shape of the request rather than a single rate. Each variant carries `dimensions` (the axes it is keyed on, e.g. `{"resolution":"2K","duration_seconds":"5"}`), `unit` — **`video_second`** (multiply `unit_price` by the generated seconds) or **`video_clip`** (`unit_price` is the whole-clip total) — and `unit_price`, which is always the final per-unit price and never a multiplier to apply yourself. When `variants` is present, use it, and note that a table has one of two shapes, with different fallback rules for a request it does not name. The shape is told by the `dimensions` keys, not by `unit`. A **resolution-only** table (rows keyed on `{"resolution": ...}`) keeps `video` published: it is the rate for any resolution the table does NOT list, so match your resolution against `variants`, else use `video` × seconds. A **bucketed** table (rows keyed on `{"resolution", "duration_seconds"}`) omits `video`, because it is never the basis there, and a request the table does not name exactly is billed off the table itself — the row for **your resolution with the smallest `duration_seconds` that is still ≥ your clip's duration**, and if no row at your resolution covers it (or your resolution has no rows at all) the **highest-priced row in the whole table**. That last case can therefore be much dearer than the shape you asked for; it means the operator has not tabulated what you requested, and the router counts it so they can. A model with no `variants` at all bills `video` × generated seconds. Resolution matching is case- and whitespace-insensitive on our side. Duration here is the length actually delivered, which for a request carrying a reference image or video is the vendor's billed length (input + output) and so can exceed the `seconds` you asked for. `GET /v1/service-types` lists `video-generation` / "Video Generation" once a video provider is on the network. **Purely additive — no existing field changes.** A video-generation model still reports `prompt` / `completion`, unchanged. Note these are only per-TOKEN prices for chat: on every other modality `completion` is an echo of the on-chain output price whose meaning follows the modality — per image for `text-to-image` / `image-editing` (which is why `image` exists), and per generated second for `video-generation` (which is why `video` / `variants` exist). So compute video cost from `video` / `variants`, exactly as image cost is computed from `image`; do not multiply `completion` by a token count. One caveat specific to `variants`, because it is a whole price LIST rather than a single rate: `GET /v1/models` aggregates a model across every endpoint serving it and shows one endpoint's block, so when two endpoints of the same model publish different tables the quote you read may not be the table the request is billed against. Endpoints are per-provider, so read `GET /v1/providers` for the exact list, or pin `provider.address` if you need the quote and the charge to be the same table. This is a general property of the aggregated view rather than something new — the shown block always comes from one endpoint, and which endpoint the request routes to depends on the routing preferences you send (`sort`, a pinned `address`) and on health — but it is worth stating for `variants` specifically, because a table is a whole price list rather than a single rate, so two endpoints can differ in the SHAPE of what they charge and not just the amount. **[beta]** - **All inference endpoints — the `min_cost` admission floor rises from `0.00001 0G` to `0.01 0G`** — a request is admitted only when the account can cover `min_cost`, either from the Router ledger or through the vault deferred path (see the **Admission rule** table above, which now reads `0.01 0G`). The practical effect is that an account whose spendable balance has fallen below `0.01 0G` now receives `402` rather than being served one last request. **No balance is lost** — the remainder stays on the account and becomes spendable again on the next top-up — and accounts with vault funds are unaffected, because the vault side of the admission rule is unchanged. The old floor was low enough to be indistinguishable from zero, which let a nearly-empty account be served a request it could not pay for, leaving a debt (`pending_charge`) that the account could then only clear by funding its vault. - **`POST /v1/chat/completions`, `/v1/messages`, `/v1/images/*`, `/v1/audio/transcriptions`, `/v1/async/images/*`, `/v1/routing/preview` — pinning a provider that doesn't serve the requested model now returns `400`, not `500`** — when a request pins a specific provider (`provider.address` in the body or the `X-0G-Provider-Address` header) whose address exists but does not serve the requested `model` (or whose service type / API format doesn't match the endpoint), the router now returns **`400`** with error code **`provider_model_mismatch`** instead of a generic `500` / `502`. This is a deterministic bad pin — two constraints ("use this exact provider" and "serve this model") that don't intersect — so retrying won't help and the response now says so, letting the caller correct the pin. An unpinned request is unaffected (it routes normally to a provider that serves the model), as is a pin that resolves to no provider at all (still `400`/`provider not found`). - **`GET /v1/account`, `POST /v1/account/onboarded` (JWT; `GET` also accepts a management key with `account:read`) — account onboarding state** — a new endpoint pair exposes whether the authenticated wallet has handled the new-user onboarding flow. `GET /v1/account` returns account metadata `{address, created_at, onboarded_at}`, where **`onboarded_at`** is an RFC3339 timestamp once the account has completed or dismissed onboarding and **`null`** until then, so a client can decide whether to show the flow. Because the value is stored per wallet on the server (not in the browser), it persists across a cleared cache, another browser, and another device. `POST /v1/account/onboarded` records that the flow was handled, setting `onboarded_at` to the server time; it is **idempotent** — the first call stamps the timestamp and later calls (a client retry, or a "dismiss" after a "complete") leave it unchanged — so both client paths can safely call and retry it. **[beta]** - **`POST /v1/chat/completions`, `/v1/messages`, `/v1/images/*`, `/v1/audio/transcriptions`, `/v1/async/images/*` — transient vault-check failure now returns retryable `503` instead of `402`** — a routing-mode (Payment Layer vault) user's request is admitted by checking their on-chain vault balance. When that chain read fails transiently (RPC blip), the pre-request balance gate previously returned `402 "Insufficient balance"` — indistinguishable from a genuinely empty account, so a funded user saw "insufficient balance" and assumed their deposit was lost. It now returns **`503`** with error code **`vault_unavailable`** (OpenAI-style) / `api_error` (Anthropic-style) and is safe to retry; the next request self-heals once the RPC recovers. Behavior is still fail-closed (the request is never admitted while vault state is unknown — no unbounded deferred debt) and a genuine balance shortfall (vault read succeeds, funds insufficient) is unchanged at `402`. No request contract change. - **All endpoints — a client-supplied `X-Request-ID` is now validated before it is echoed and recorded** — the header is still propagated as your correlation id, but only when it is at most 64 characters, built from letters, digits and `- _ . :`, and does not begin with `async-`. A value failing any of those is replaced by a server-generated id, exactly as an over-long one already was; you always get the id actually in effect back in the `X-Request-ID` response header, so read it there rather than assuming your value was kept. UUIDs, ULIDs, hex trace ids and W3C `traceparent` values are unaffected. The `async-` prefix is reserved because the router derives its own asynchronous-billing identifiers in that namespace. title: 0G Router API termsOfService: https://0g.ai/terms contact: name: 0G Labs url: https://0g.ai email: contact@0g.ai version: "1.0" paths: /account: get: security: - ManagementKeyAuth: [] description: "Returns non-financial metadata for the authenticated wallet: `created_at` and `onboarded_at`. `onboarded_at` is an RFC3339 timestamp once the account has completed or dismissed the new-user onboarding flow, and `null` until then — a client reads it to decide whether to show onboarding. Set it via `POST /account/onboarded`. The value is stored per wallet on the server, so it persists across a cleared cache, another browser, and another device." tags: - Account summary: Get account responses: "200": description: Account metadata content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.AccountRes\ ponse" "401": description: Unauthorized content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "500": description: Server error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" /account/funds: get: security: - ManagementKeyAuth: [] description: >- Returns a single aggregated view of the caller's funds so clients don't have to combine `/account/balance` (Router ledger) with the on-chain `PaymentVault.balanceOf` themselves. `total` = `router.subtotal` + `payment_layer.balance`, where `router.subtotal` = `deposit` + `credit` − `pending_charge` (the net Router-ledger position). `total` is the user's net spendable funds and **may be negative** when outstanding debt exceeds available funds — render the shortfall as "owed to Router". This differs from `/account/balance`'s `total_balance`, which keeps a ≥0 contract for settlement / SDK callers and does NOT subtract pending_charge. `payment_layer.balance` is the shared Payment Layer vault balance (`shared_across_products: true` — the vault is a pool across 0G apps, not owned exclusively by Router). `queried_at` is when that balance was actually read from chain; it is cached (~10s), so on a cache hit it can be up to that TTL in the past — treat it as the value's as-of time, not the request time. For the Router-ledger-only view that settlement / SDK / mgmt-key integrations depend on, use `/account/balance` instead. tags: - Account summary: Get unified account funds responses: "200": description: Unified funds view content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.FundsRespo\ nse" "401": description: Unauthorized content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "500": description: Server error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" /account/usage/daily: get: security: - ManagementKeyAuth: [] description: Get usage statistics broken down by day for current user, with optional filters tags: - Account summary: Get daily usage statistics parameters: - description: Filter by API key ID name: api_key_id in: query schema: type: string - description: "Filter by source: 'wallet' (JWT/browser), 'api_key' (any API key), or omit for all" name: source in: query schema: type: string - description: Start date (YYYY-MM-DD, inclusive) name: start_date in: query schema: type: string - description: End date (YYYY-MM-DD, inclusive) name: end_date in: query schema: type: string - description: "Filter by trust tier: 'standard' | 'verified' | 'private'. Omit for all tiers." name: trust_mode in: query schema: type: string - description: "Extra breakdown dimensions on top of (date, model). Only 'trust_mode' is supported: pass `dimensions=trust_mode` to split each day/model row per trust tier and emit the `trust_mode` field. Omit (default) for the legacy one-row-per (date, model) shape." name: dimensions in: query schema: type: string responses: "200": description: Daily usage statistics content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.DailyUsage\ Response" "401": description: Unauthorized content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "500": description: Server error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" /account/usage/history: get: security: - ManagementKeyAuth: [] description: Get usage history for current user, with optional filters tags: - Account summary: Get usage history parameters: - description: Limit number of records name: limit in: query schema: type: integer default: 20 - description: Offset for pagination (legacy). Ignored when `cursor` is set. name: offset in: query schema: type: integer default: 0 - description: "[beta] Opaque keyset cursor from a prior page's `next_cursor`. When set, pages via seek (constant per-page cost, independent of depth) and `offset` is ignored; also forces `include_total=false`." name: cursor in: query schema: type: string - description: "[beta] Include the full-set `total` count (default true). Set false to skip the expensive COUNT(*); recommended with `cursor`." name: include_total in: query schema: type: boolean default: true - description: Filter by model ID name: model_id in: query schema: type: string - description: Filter by API key ID name: api_key_id in: query schema: type: string - description: "Filter by source: 'wallet' (JWT/browser), 'api_key' (any API key), or omit for all" name: source in: query schema: type: string - description: Start date (YYYY-MM-DD, inclusive) name: start_date in: query schema: type: string - description: End date (YYYY-MM-DD, inclusive) name: end_date in: query schema: type: string - description: "Filter by trust tier: 'standard' | 'verified' | 'private'. Omit for all tiers." name: trust_mode in: query schema: type: string responses: "200": description: Usage history list content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.UsageHisto\ ryResponse" "401": description: Unauthorized content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "500": description: Server error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" /account/usage/stats: get: security: - ManagementKeyAuth: [] description: Get usage statistics for current user, optionally filtered by API key and date range tags: - Account summary: Get usage statistics parameters: - description: Filter by API key ID name: api_key_id in: query schema: type: string - description: "Filter by source: 'wallet' (JWT/browser), 'api_key' (any API key), or omit for all" name: source in: query schema: type: string - description: Start date (YYYY-MM-DD, inclusive) name: start_date in: query schema: type: string - description: End date (YYYY-MM-DD, inclusive) name: end_date in: query schema: type: string - description: "Filter by trust tier: 'standard' | 'verified' | 'private'. Omit for all tiers." name: trust_mode in: query schema: type: string responses: "200": description: Usage statistics content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.UsageStats\ Response" "401": description: Unauthorized content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "500": description: Server error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" /api-keys: get: security: - ManagementKeyAuth: [] description: Get all API keys for current user tags: - API Key summary: Get API key list parameters: - description: "Filter: active / revoked / expired / all (default all)" name: status in: query schema: type: string responses: "200": description: API key list content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.APIKeyList\ Response" "401": description: Unauthorized content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "500": description: Server error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" post: security: - ManagementKeyAuth: [] description: Create a new API key for LLM service access tags: - API Key summary: Create API key requestBody: content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_request.CreateAPIKe\ yRequest" description: API key configuration responses: "200": description: API key created content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.CreateAPIK\ eyResponse" "400": description: Request error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "401": description: Unauthorized content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "500": description: Server error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "/api-keys/{keyId}": delete: security: - ManagementKeyAuth: [] description: Revoke a specific API key tags: - API Key summary: Revoke API key parameters: - description: API Key ID name: keyId in: path required: true schema: type: string responses: "200": description: Revoked successfully content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.RevokeAPIK\ eyResponse" "401": description: Unauthorized content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "404": description: API key not found content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "500": description: Server error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" patch: security: - ManagementKeyAuth: [] description: Update name / credit_limit / reset_period / expiration of an API key tags: - API Key summary: Update API key parameters: - description: API Key ID name: keyId in: path required: true schema: type: string requestBody: content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_request.UpdateAPIKe\ yRequest" description: Patch payload required: true responses: "200": description: Updated key content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.APIKeyItem" "400": description: Invalid request content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "401": description: Unauthorized content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "404": description: API key not found content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "409": description: Duplicate name content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "410": description: Key revoked content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "500": description: Server error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" /async/images/edits: post: security: - ApiKeyAuth: [] description: >- Submit an image editing job to be processed asynchronously. The response includes `jobId` and `provider_address` — clients then poll `/v1/async/jobs/{jobId}` with `?provider_address=...` to retrieve the result. Supports provider routing preferences via `X-0G-Provider-*` request headers (`Address`, `Sort`, `Trust-Mode`, `Allow-Fallbacks`). Multipart endpoints have no body-side routing surface — headers only. tags: - Inference summary: Submit async image edit job parameters: - description: "Routing: pin to a specific provider by on-chain address (0x-prefixed)." name: X-0G-Provider-Address in: header schema: type: string - description: "Routing: provider sort strategy. `latency` | `price`." name: X-0G-Provider-Sort in: header schema: type: string - description: "Routing: trust tier filter (`verified` | `private`). `verified` is a floor — `private` providers also satisfy it." name: X-0G-Provider-Trust-Mode in: header schema: type: string - description: "Routing: whether to retry on other providers after a failure. `true` | `false`. Defaults to false when an address is pinned, true otherwise." name: X-0G-Provider-Allow-Fallbacks in: header schema: type: string responses: "202": description: Job submitted; response carries jobId, status, provider_address content: application/json: schema: type: object additionalProperties: true "400": description: Request error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "401": description: Unauthorized content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "402": description: Insufficient balance content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "423": description: Refund pending content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "503": description: No available provider content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" /async/images/generations: post: security: - ApiKeyAuth: [] description: >- Submit an image generation job to be processed asynchronously. The response includes `jobId` and `provider_address` — clients then poll `/v1/async/jobs/{jobId}` with `?provider_address=...` to retrieve the result. **Provider Routing**: Use `"provider": {...}` body field OR `X-0G-Provider-*` request headers. Header > body precedence resolves same-field conflicts. tags: - Inference summary: Submit async image generation job parameters: - description: "Routing: pin to a specific provider by on-chain address (0x-prefixed)." name: X-0G-Provider-Address in: header schema: type: string - description: "Routing: provider sort strategy. `latency` | `price`." name: X-0G-Provider-Sort in: header schema: type: string - description: "Routing: trust tier filter (`verified` | `private`). `verified` is a floor — `private` providers also satisfy it." name: X-0G-Provider-Trust-Mode in: header schema: type: string - description: "Routing: whether to retry on other providers after a failure. `true` | `false`. Defaults to false when an address is pinned, true otherwise." name: X-0G-Provider-Allow-Fallbacks in: header schema: type: string requestBody: $ref: "#/components/requestBodies/github_com_0glabs_0g-router_pkg_inference.Ima\ geGenerationRequest" responses: "202": description: Job submitted; response carries jobId, status, provider_address content: application/json: schema: type: object additionalProperties: true "400": description: Request error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "401": description: Unauthorized content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "402": description: Insufficient balance content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "423": description: Refund pending content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "503": description: No available provider content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" /audio/transcriptions: post: security: - ApiKeyAuth: [] description: >- Transcribe audio file to text. Accepts **either** content type: - `application/json` (default, schema below): `AudioTranscriptionRequest` body with base64-encoded audio in `file_base64`. - `multipart/form-data` (OpenAI-style): `file` part with audio bytes + `model` form field (plus optional `language`, `response_format`). Schema not auto-documented here — matches the OpenAI `/v1/audio/transcriptions` shape. Supports provider routing preferences via `X-0G-Provider-*` request headers (`Address`, `Sort`, `Trust-Mode`, `Allow-Fallbacks`). Multipart requests have no body-side routing surface — headers only. JSON requests may also use the body `provider: {...}` field. tags: - Inference summary: Audio transcription parameters: - description: "Routing: pin to a specific provider by on-chain address (0x-prefixed)." name: X-0G-Provider-Address in: header schema: type: string - description: "Routing: provider sort strategy. `latency` | `price`." name: X-0G-Provider-Sort in: header schema: type: string - description: "Routing: trust tier filter (`verified` | `private`). `verified` is a floor — `private` providers also satisfy it." name: X-0G-Provider-Trust-Mode in: header schema: type: string - description: "Routing: whether to retry on other providers after a failure. `true` | `false`. Defaults to false when an address is pinned, true otherwise." name: X-0G-Provider-Allow-Fallbacks in: header schema: type: string requestBody: content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.AudioTran\ scriptionRequest" multipart/form-data: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.AudioTran\ scriptionRequest" description: JSON-body shape (base64-encoded audio in `file_base64`). For multipart/form-data requests, send `file` + `model` form parts instead — see description. required: true responses: "200": description: Transcription result content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.AudioTran\ scriptionResponse" "400": description: Request error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "401": description: Unauthorized content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "402": description: Insufficient balance content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "404": description: Model not found (unknown model id) content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "423": description: Refund pending content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "429": description: Rate limit exceeded content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "503": description: No available provider content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" /chat/completions: post: security: - ApiKeyAuth: [] description: >- Send chat request to AI provider (OpenAI compatible API). **Web Search**: Add `"plugins": [{"id": "web"}]` to enable real-time web search. Search results are injected into the prompt context and returned as `url_citation` annotations in the response. Works with any model. Multi-turn conversations automatically rewrite the query for better search relevance. The injected search context tokens are billed as normal input tokens. **File Attachments**: Upload via `POST /v1/files`, then reference with `"attachments": [{"file_id": "file-..."}]`. Extracted text is injected into the system prompt and billed as normal input tokens. **Provider Routing**: Use `"provider": {"sort": "latency"|"price", "address": "0x...", "allow_fallbacks": true}` to influence provider selection. `address` overrides `sort`. The `X-0G-Provider-*` request headers (documented below) are the equivalent canonical surface. **Price Ceiling** (header-only): set `X-0G-Provider-Max-Price-Usd-Prompt` / `-Completion` (USD per 1M tokens) or `-Image` (USD per generated image). Providers above the cap are excluded before sort/fallback runs. [beta] **E2EE (sealed requests)**: a 0G Private Computer sidecar may seal `messages`/`tools` into a top-level `_e2ee` object and omit them from the cleartext body. The router detects `_e2ee`, skips the `messages`-required check, and carries the `_e2ee` field through to the provider (which it never reads or decrypts); `model` and response `usage` stay cleartext for routing/billing. Presence of `_e2ee` is the only signal (no header). [beta] tags: - Inference summary: Chat completion parameters: - description: "Routing: pin to a specific provider by on-chain address (0x-prefixed)." name: X-0G-Provider-Address in: header schema: type: string - description: "Routing: provider sort strategy. `latency` | `price`." name: X-0G-Provider-Sort in: header schema: type: string - description: "Routing: trust tier filter (`verified` | `private`). `verified` is a floor — `private` providers also satisfy it." name: X-0G-Provider-Trust-Mode in: header schema: type: string - description: "Routing: whether to retry on other providers after a failure. `true` | `false`. Defaults to false when an address is pinned, true otherwise." name: X-0G-Provider-Allow-Fallbacks in: header schema: type: string requestBody: content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.ChatCompl\ etionRequest" description: Chat request required: true responses: "200": description: Chat response content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.ChatCompl\ etionResponseWithTrace" "400": description: Request error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "401": description: Unauthorized content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "402": description: Insufficient balance content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "404": description: Model not found (unknown model id) content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "423": description: Refund pending content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "429": description: Rate limit exceeded content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "502": description: Provider error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "503": description: No available provider (model exists but unavailable) content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" /images/edits: post: security: - ApiKeyAuth: [] description: >- Edit an image using AI (inpainting, variations). Supports provider routing preferences via `X-0G-Provider-*` request headers (`Address`, `Sort`, `Trust-Mode`, `Allow-Fallbacks`). Multipart endpoints have no body-side routing surface — headers only. tags: - Inference summary: Image editing parameters: - description: "Routing: pin to a specific provider by on-chain address (0x-prefixed)." name: X-0G-Provider-Address in: header schema: type: string - description: "Routing: provider sort strategy. `latency` | `price`." name: X-0G-Provider-Sort in: header schema: type: string - description: "Routing: trust tier filter (`verified` | `private`). `verified` is a floor — `private` providers also satisfy it." name: X-0G-Provider-Trust-Mode in: header schema: type: string - description: "Routing: whether to retry on other providers after a failure. `true` | `false`. Defaults to false when an address is pinned, true otherwise." name: X-0G-Provider-Allow-Fallbacks in: header schema: type: string requestBody: content: multipart/form-data: schema: type: object properties: image: description: Source image file type: string format: binary prompt: description: Edit instruction type: string model: description: Model ID type: string required: - prompt - model responses: "200": description: Edited image content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.ImageGene\ rationResponse" "400": description: Request error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "401": description: Unauthorized content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "402": description: Insufficient balance content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "404": description: Model not found (unknown model id) content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "423": description: Refund pending content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "429": description: Rate limit exceeded content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "503": description: No available provider content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" /images/generations: post: security: - ApiKeyAuth: [] description: Generate images using AI. Supports `provider` routing preferences (see /chat/completions). tags: - Inference summary: Image generation parameters: - description: "Routing: pin to a specific provider by on-chain address (0x-prefixed)." name: X-0G-Provider-Address in: header schema: type: string - description: "Routing: provider sort strategy. `latency` | `price`." name: X-0G-Provider-Sort in: header schema: type: string - description: "Routing: trust tier filter (`verified` | `private`). `verified` is a floor — `private` providers also satisfy it." name: X-0G-Provider-Trust-Mode in: header schema: type: string - description: "Routing: whether to retry on other providers after a failure. `true` | `false`. Defaults to false when an address is pinned, true otherwise." name: X-0G-Provider-Allow-Fallbacks in: header schema: type: string requestBody: $ref: "#/components/requestBodies/github_com_0glabs_0g-router_pkg_inference.Ima\ geGenerationRequest" responses: "200": description: Generated image content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.ImageGene\ rationResponse" "400": description: Request error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "401": description: Unauthorized content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "402": description: Insufficient balance content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "404": description: Model not found (unknown model id) content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "423": description: Refund pending content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "429": description: Rate limit exceeded content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "503": description: No available provider content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" /messages: post: security: - ApiKeyAuth: [] description: >- Send chat request using Anthropic Messages API format. Routes to providers that support the Anthropic format. Supports web search plugins, file attachments, and provider routing preferences (same as `/chat/completions`). tags: - Inference summary: Anthropic Messages API parameters: - description: "Routing: pin to a specific provider by on-chain address (0x-prefixed)." name: X-0G-Provider-Address in: header schema: type: string - description: "Routing: provider sort strategy. `latency` | `price`." name: X-0G-Provider-Sort in: header schema: type: string - description: "Routing: trust tier filter (`verified` | `private`). `verified` is a floor — `private` providers also satisfy it." name: X-0G-Provider-Trust-Mode in: header schema: type: string - description: "Routing: whether to retry on other providers after a failure. `true` | `false`. Defaults to false when an address is pinned, true otherwise." name: X-0G-Provider-Allow-Fallbacks in: header schema: type: string requestBody: content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.Anthropic\ Request" description: Anthropic Messages request required: true responses: "200": description: Anthropic Messages response content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.Anthropic\ Response" "400": description: Request error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "401": description: Unauthorized content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "402": description: Insufficient balance content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "404": description: Model not found (unknown model id) content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "423": description: Refund pending content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "429": description: Rate limit exceeded content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "502": description: Provider error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "503": description: No available provider (model exists but unavailable) content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" /models: get: description: >- Get all available AI models. By default the response is the **canonical view**: one row per `canonical_id`, with curated registry metadata and endpoints aggregated (max context, union of params/formats, cheapest pricing); `id` is the canonical model id. A model stays listed even when all its endpoints are temporarily unhealthy (health is not a visibility filter; routing falls back to unhealthy endpoints so a listed model is always routable). To enumerate the endpoints behind a canonical id, call `GET /v1/providers?canonical_id=`. provider_address uses OR (match any), input_modality and supported_parameter use AND (must match all). `?legacy=true` returns the historical view keyed by the raw provider `model_id` (one row per on-chain model id, including ids not mapped to a registered canonical), for clients that have not migrated to canonical ids. This is a **listing-only** compatibility surface — routing remains canonical-only, so a raw id absent from the model registry is still `404 model_not_found` on the inference path. **[beta]** tags: - Models summary: Get model list parameters: - description: "[beta] true: historical raw-model_id-keyed view (listing-only; an unregistered id it lists is still 404 on the inference path). Default false: canonical view (id = canonical_id)." name: legacy in: query schema: type: boolean - description: "[beta] 'usd': emit the OpenRouter-standard shape with per-token USD decimal strings under the top-level `pricing` field (for external pricing aggregators, e.g. TKX); models without a USD price are omitted. Default: native prices under `pricing`, USD under `pricing_usd`." name: pricing in: query schema: type: string enum: - usd - description: "Filter by provider address (OR: match any)" name: provider_address in: query explode: true schema: type: array items: type: string - description: "Filter by input modality, e.g. text, image (AND: must support all)" name: input_modality in: query explode: true schema: type: array items: type: string - description: "Filter by supported parameter, e.g. temperature (AND: must support all)" name: supported_parameter in: query explode: true schema: type: array items: type: string responses: "200": description: Model list content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ModelListR\ esponse" "500": description: Server error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" /providers: get: description: 'Get all TEE-acknowledged providers, optionally narrowed by service type, on-chain model id, and/or canonical id. `model` and `canonical_id` are independent filters and compose (ANDed): `canonical_id` alone lists every endpoint of a canonical, while `model` + `canonical_id` narrows to a specific endpoint within it. An empty value means "not filtered on" (so no filters = list all). Includes providers with unknown health status (is_healthy=null).' tags: - Provider summary: Get provider list parameters: - description: Filter by service type (chatbot, text-to-image, speech-to-text) name: service_type in: query schema: type: string - description: Filter by on-chain model ID (e.g. zai-org/GLM-5.1-FP8); exact model_id match. Empty = not filtered. name: model in: query schema: type: string - description: "[beta] Filter by canonical model ID (e.g. glm-5.1); lists every endpoint serving that canonical. Composes (AND) with model. Empty = not filtered." name: canonical_id in: query schema: type: string responses: "200": description: Provider list content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ProviderLi\ stResponse" "500": description: Server error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" /routing/preview: post: security: - ApiKeyAuth: [] description: >- Return, in fallback order, the provider(s) the router WOULD select for a request — WITHOUT executing, billing, or mutating routing state. Built for end-to-end-encrypted (sealed) requests: a sealed request must be encrypted to a specific provider enclave before it can be sent, so a gateway calls this first (with the sensitive `messages` / prompt stripped out), seals to the head candidate, and pins the real request to it via `X-0G-Provider-Address` — falling back to the next candidate (re-sealing) on failure. The body is the stripped inference request plus the control field `service_type` — the internal service type being previewed (`chatbot` | `text-to-image` | `image-editing` | `speech-to-text`). This is the SAME vocabulary returned by `GET /v1/service-types` (`.type`) and accepted by `GET /v1/providers` (`?service_type=`), so a caller discovering providers via the catalog feeds the same string straight in (it is NOT the model-modality `type` on `/v1/models`). Anthropic (`/v1/messages`) sealed requests are not supported yet, so `anthropic-chat` is not an accepted value. `model` is OPTIONAL: given, candidates are that model's providers; omitted, candidates are ANY provider of the service type (a heterogeneous list — each candidate carries its own `canonical_id` / `model_id`). Routing preferences (`provider` / `X-0G-Provider-*`) and capability signals (`tools`, `response_format`, `max_tokens`, `reasoning_effort`) are read from the body/headers exactly as on the inference endpoints, so the previewed order matches what the request would select. The number of candidates is fixed at the router's provider-retry budget (the length of the fallback chain the router itself would try); a smaller healthy pool returns fewer. Each candidate returns the provider `address` (to pin), its broker `endpoint` (to fetch the enclave key from), and the model it serves (`canonical_id` to put in the sealed request, `model_id` informational). A pinned `provider.address` returns just that provider. For the multipart inference endpoints (`image-editing`, `speech-to-text`) there is no JSON body to strip: synthesize this JSON body carrying `service_type` + `model` and forward routing prefs via `X-0G-Provider-*` headers — the uploaded blob (`file` / `mask`) and `prompt` need not (and should not) be included, as they are both sensitive and irrelevant to provider selection. This endpoint is in limited availability and may be unavailable (`404`) in some environments. [beta] tags: - Inference summary: Preview provider selection (E2EE) requestBody: content: application/json: schema: $ref: "#/components/schemas/internal_handler.RoutePreviewRequest" description: Preview request (stripped inference body + service_type/model) required: true responses: "200": description: OK content: application/json: schema: $ref: "#/components/schemas/internal_handler.RoutePreviewResponse" "400": description: Request error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "401": description: Unauthorized content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "404": description: Model not found content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "503": description: No available provider content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" /service-types: get: description: Get service types that have at least one TEE-acknowledged provider (health is not a filter — a service type stays listed even when its providers are temporarily unhealthy) tags: - Service Types summary: Get available service types responses: "200": description: Service type list content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ServiceTyp\ eListResponse" "500": description: Server error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" /videos: post: security: - ApiKeyAuth: [] description: >- Submit a video generation job (OpenAI Video API shape). The response includes `id` and `provider_address` — clients poll `GET /videos/{id}?provider_address=...` and download the result from `GET /videos/{id}/content?provider_address=...`. Accepts `application/json` OR `multipart/form-data`. Use multipart to drive image-to-video: send the same fields as form values plus the reference image as a file part. Supports provider routing preferences via `X-0G-Provider-*` request headers (`Address`, `Sort`, `Trust-Mode`, `Allow-Fallbacks`), identical to the other inference endpoints. The request shape is the OpenAI Video API's, but the models behind it are not OpenAI's, and where a model's own limits differ from OpenAI's they win. Those limits are per-model — read them from the model's `description` and `pricing.variants` on `GET /v1/models` rather than assuming the OpenAI defaults — and a request that violates one is rejected by the model provider, so the error text and any numeric code in it are theirs, not ours. The two that differ most often are `size` and `seconds`. `size` names a resolution TIER, not output dimensions, because a video model advertises the tiers it is PRICED at rather than arbitrary sizes. Two spellings are accepted and they are not equivalent. **Pixel dimensions** (`1280x720`, OpenAI's spelling and what an SDK sends) select the ASPECT RATIO only, and only for text-to-video — the clip renders at whatever tier the model serves, so on a 2K-only model `1280x720` returns a 2560x1440 clip billed at the 2K rate, not a 720p one. For **image-to-video** the aspect ratio follows your reference image, so pixel dimensions have no effect there — a tier name still selects the tier. **A tier name** (`2K`) addresses the tier directly. Send only names that appear in that list — it is the set this endpoint can price, and it grows as a model adds tiers, so read it rather than hard-coding what a model serves today. Read the tiers from `pricing.variants[].dimensions.resolution`. Omitting `size` is the recommended default — the model's own default tier and aspect ratio apply. `seconds` is clamped to the model's supported range rather than rejected, in BOTH directions and without an error: ask for less than the minimum and you get (and pay for) the minimum; ask for more than the maximum and you get the maximum. The clip you are billed for is the one actually produced, so a request outside the range costs something other than what you asked for. The range is per-model and stated in the model's description. For both fields the submit response echoes what you SENT, while the poll response reports what was actually rendered. Reconcile against the poll. tags: - Inference summary: Submit async video generation job parameters: - description: "Routing: pin to a specific provider by on-chain address (0x-prefixed)." name: X-0G-Provider-Address in: header schema: type: string - description: "Routing: provider sort strategy. `latency` | `price`." name: X-0G-Provider-Sort in: header schema: type: string - description: "Routing: trust tier filter (`verified` | `private`). `verified` is a floor — `private` providers also satisfy it." name: X-0G-Provider-Trust-Mode in: header schema: type: string - description: "Routing: whether to retry on other providers after a failure. `true` | `false`. Defaults to false when an address is pinned, true otherwise." name: X-0G-Provider-Allow-Fallbacks in: header schema: type: string requestBody: content: application/json: schema: type: object multipart/form-data: schema: type: object description: Video generation request. `model`, `prompt` and `seconds` are REQUIRED (`seconds` is optional in the upstream OpenAI Video API but mandatory here — see the changelog); `size` is optional. required: true responses: "200": description: Job submitted; response carries id, status, provider_address content: application/json: schema: type: object additionalProperties: true "400": description: Request error — includes `seconds is required` and a `seconds` value the model has no price for (the available durations are listed) content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "402": description: Insufficient balance content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "404": description: model_not_found — no provider on the network serves this model content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "423": description: refund_pending — a refund is in progress for this account content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "429": description: video_jobs_in_flight_limit — too many unfinished video jobs for this account; poll or wait for the earlier ones content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "501": description: Not available in USD mode content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "503": description: no_available_provider content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "/videos/{id}": get: security: - ApiKeyAuth: [] description: Return a video job's current status. When the job has completed this is also what settles the charge. `provider_address` is optional — it is resolved from the job id when omitted, so an OpenAI-native client can call `GET /videos/{id}` with no query parameters. tags: - Inference summary: Poll async video generation job parameters: - description: Video job id returned by POST /videos name: id in: path required: true schema: type: string - description: Pin the provider that owns this job; resolved from the job id when omitted name: provider_address in: query schema: type: string - description: Verify the provider's TEE signature and include the trace name: verify_tee in: query schema: type: boolean responses: "200": description: Job status; on completion the result payload content: application/json: schema: type: object additionalProperties: true "400": description: Request error — includes a job id tracked against more than one provider, where `provider_address` must be supplied to disambiguate content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "403": description: Caller is not the submitter of this job content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "404": description: async_job_not_found — this router has no such job id (it never existed, or it belongs to a different provider than the one pinned) content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "502": description: Upstream provider error content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "/videos/{id}/content": get: security: - ApiKeyAuth: [] description: Stream the finished video file. If the job has not been charged yet (the client never polled `GET /videos/{id}`) it is charged before any bytes are sent. A job that is still running returns 409; a job that failed at the provider returns 424. `provider_address` is optional — it is resolved from the job id when omitted. tags: - Inference summary: Download a completed video parameters: - description: Video job id returned by POST /videos name: id in: path required: true schema: type: string - description: Pin the provider that owns this job; resolved from the job id when omitted name: provider_address in: query schema: type: string - description: Verify the provider's TEE signature before billing name: verify_tee in: query schema: type: boolean responses: "200": description: The video file content: application/octet-stream: schema: type: string format: binary "400": description: Request error — includes a job id tracked against more than one provider, where `provider_address` must be supplied to disambiguate content: application/octet-stream: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "403": description: Caller is not the submitter of this job content: application/octet-stream: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "404": description: async_job_not_found — this router has no such job id (it never existed, or it belongs to a different provider than the one pinned) content: application/octet-stream: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "409": description: Video not ready yet; keep polling GET /videos/{id} content: application/octet-stream: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "424": description: The video job failed at the provider and has no output content: application/octet-stream: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "429": description: Too many concurrent downloads for this account content: application/octet-stream: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" "502": description: Upstream provider error content: application/octet-stream: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorRespo\ nse" tags: [] servers: - url: /v1 components: requestBodies: github_com_0glabs_0g-router_pkg_inference.ImageGenerationRequest: content: application/json: schema: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.ImageGene\ rationRequest" description: Image generation request required: true securitySchemes: ApiKeyAuth: description: 'API key for inference, file upload, and async image endpoints. Format: "Bearer sk-..."' type: apiKey name: Authorization in: header ManagementKeyAuth: description: 'Management key for account read and API key management. Capabilities are scope-limited per key (account:read, keys:read, keys:create, keys:manage). Format: "Bearer mk-..."' type: apiKey name: Authorization in: header schemas: github_com_0glabs_0g-router_internal_service.AdminRoleView: type: object properties: created_at: type: string granted_by: type: string scopes: type: array items: type: string updated_at: type: string wallet_address: type: string github_com_0glabs_0g-router_internal_service.CreateProjectGrantRequest: type: object required: - owner_address - total_quota properties: currency: description: >- Currency selects the grant's unit and target ledger: "0g" (the default when omitted) or "usd". A "usd" grant requires features.usd_billing. type: string expires_at: description: RFC3339, "" = never type: string name: type: string owner_address: type: string total_quota: description: |- TotalQuota is a raw neuron integer for a 0g grant, or a USD decimal ("5", "1.50") for a usd grant (stored as micro-USD). type: string github_com_0glabs_0g-router_internal_service.DistributeProjectCreditRequest: type: object required: - amount - grant_id - idempotency_key - recipient_address properties: amount: type: string grant_id: type: string idempotency_key: type: string reason: type: string recipient_address: type: string github_com_0glabs_0g-router_internal_service.GrantCreditRequest: type: object required: - amount - type - user_address properties: amount: description: neuron (1 0G = 10^18 neuron) type: string reason: type: string type: description: '"welcome", "promotion", "compensation", "manual"' type: string user_address: type: string github_com_0glabs_0g-router_internal_service.GrantUsdCreditRequest: type: object required: - amount - type - user_address properties: amount: description: USD decimal, e.g. "5.00" type: string reason: type: string type: description: '"welcome", "promotion", "compensation", "manual"' type: string user_address: type: string github_com_0glabs_0g-router_internal_service.UpdateAutoDepositRequest: type: object properties: enabled: type: boolean low_watermark: type: string max_balance: type: string github_com_0glabs_0g-router_pkg_inference.AnthropicCacheCreation: type: object properties: ephemeral_1h_input_tokens: type: integer ephemeral_5m_input_tokens: type: integer github_com_0glabs_0g-router_pkg_inference.AnthropicContentBlock: type: object properties: id: description: tool_use fields type: string input: {} name: type: string text: type: string type: description: '"text", "tool_use", etc.' type: string github_com_0glabs_0g-router_pkg_inference.AnthropicRequest: type: object properties: attachments: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.Attachmen\ t" max_tokens: type: integer messages: description: AnthropicMessage objects type: array items: {} metadata: {} model: type: string plugins: description: 0G Router extensions (stripped before forwarding) type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.Plugin" provider: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.ProviderP\ references" stop_sequences: type: array items: type: string stream: type: boolean system: description: string or []AnthropicContentBlock temperature: type: number tool_choice: {} tools: type: array items: {} top_k: type: integer top_p: type: number github_com_0glabs_0g-router_pkg_inference.AnthropicResponse: type: object properties: content: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.Anthropic\ ContentBlock" id: type: string model: type: string role: description: '"assistant"' type: string stop_reason: type: string type: description: '"message"' type: string usage: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.Anthropic\ Usage" github_com_0glabs_0g-router_pkg_inference.AnthropicUsage: type: object properties: cache_creation: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.Anthropic\ CacheCreation" cache_creation_input_tokens: type: integer cache_read_input_tokens: type: integer input_tokens: type: integer output_tokens: type: integer github_com_0glabs_0g-router_pkg_inference.Attachment: type: object properties: file_id: description: File ID from upload endpoint type: string example: file-abc123 github_com_0glabs_0g-router_pkg_inference.AudioTranscriptionRequest: type: object properties: file_base64: description: Base64 encoded audio file type: string example: SGVsbG8gd29ybGQ= filename: description: Filename type: string example: audio.mp3 language: description: Audio language type: string example: en model: description: Model ID type: string example: whisper-1 response_format: description: Response format type: string example: json github_com_0glabs_0g-router_pkg_inference.AudioTranscriptionResponse: type: object properties: duration: description: Audio duration (seconds) type: number example: 5.5 language: description: Detected language type: string example: en text: description: Transcribed text type: string example: Hello, world! github_com_0glabs_0g-router_pkg_inference.BillingInfo: type: object properties: currency: description: >- Currency is the unit of the cost fields: "0g" (wei) or "usd" (micro-USD), matching the account's settlement mode. Omitted on the wire for 0G traces (empty → the historical wei default), so the field is additive. type: string example: usd input_cost: description: Input cost type: string example: "0.00001" output_cost: description: Output cost type: string example: "0.00002" total_cost: description: Total cost type: string example: "0.00003" github_com_0glabs_0g-router_pkg_inference.ChatCompletionRequest: type: object properties: attachments: description: File attachments (uploaded via /v1/files) type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.Attachmen\ t" max_tokens: description: Maximum output tokens type: integer example: 1024 messages: description: Message list (content may be string or multimodal array) type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.RequestMe\ ssage" model: description: Model ID type: string example: qwen/qwen-2.5-7b-instruct plugins: description: Plugins to enable (e.g. [{"id":"web"}] for web search) type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.Plugin" provider: description: Routing preferences (0G Router extension) allOf: - $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.ProviderP\ references" response_format: description: "Output format: json_object or json_schema for structured output" allOf: - $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.ResponseF\ ormat" stream: description: Whether to stream output type: boolean example: false temperature: description: Temperature parameter (0-2) type: number example: 0.7 tool_choice: description: 'Which tool to use: "none", "auto", "required", or {"type":"function","function":{"name":"..."}}' tools: description: Tools (functions) the model may call type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.Tool" top_p: description: Top-p sampling parameter type: number example: 0.9 github_com_0glabs_0g-router_pkg_inference.ChatCompletionResponseWithTrace: type: object properties: choices: description: Choice list type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.Choice" created: description: Creation timestamp type: integer example: 1677652288 id: description: Response ID type: string example: chatcmpl-123 model: description: Model used type: string example: qwen/qwen-2.5-7b-instruct object: description: Object type type: string example: chat.completion usage: description: Token usage allOf: - $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.Usage" x_0g_trace: description: 0G trace info allOf: - $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.TraceInfo" github_com_0glabs_0g-router_pkg_inference.ChatMessage: type: object properties: content: description: Message content type: string example: Hello! role: description: "Role: system, user, assistant" type: string example: user github_com_0glabs_0g-router_pkg_inference.Choice: type: object properties: delta: description: Delta content (streaming) allOf: - $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.DeltaMess\ age" finish_reason: description: Finish reason type: string example: stop index: description: Choice index type: integer example: 0 message: description: Message content (non-streaming) allOf: - $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.ChatMessa\ ge" github_com_0glabs_0g-router_pkg_inference.DeltaMessage: type: object properties: content: type: string reasoning: description: >- Reasoning carries the raw `delta.reasoning` field some providers emit before NormalizeReasoningSSELine mirrors it into ReasoningContent. Captured here so the upstream parser can count reasoning tokens for the streaming-disconnect fallback (issue #190). type: string reasoning_content: type: string role: type: string github_com_0glabs_0g-router_pkg_inference.ImageData: type: object properties: b64_json: description: Base64 encoded image data type: string revised_prompt: description: Revised prompt type: string example: A sunset.. url: description: Image URL type: string example: https://... github_com_0glabs_0g-router_pkg_inference.ImageGenerationRequest: type: object properties: model: description: Model ID type: string example: dall-e-3 n: description: Number of images to generate type: integer example: 1 prompt: description: Image description type: string example: A beautiful sunset response_format: description: "Response format: url or b64_json" type: string example: url size: description: Image size type: string example: 1024x1024 github_com_0glabs_0g-router_pkg_inference.ImageGenerationResponse: type: object properties: created: description: Creation timestamp type: integer example: 1677652288 data: description: Image data list type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.ImageData" github_com_0glabs_0g-router_pkg_inference.Plugin: type: object properties: id: description: Plugin ID ("web" for web search) type: string example: web github_com_0glabs_0g-router_pkg_inference.PromptTokensDetails: type: object properties: cache_write_1h_tokens: description: 1-hour-TTL portion of the cache-write tokens type: integer cache_write_tokens: description: Default/5-minute subset of PromptTokens written to prompt cache (creation) type: integer cached_tokens: description: Subset of PromptTokens served from prompt cache (read) type: integer github_com_0glabs_0g-router_pkg_inference.ProviderPreferences: type: object properties: address: description: Route to a specific provider address type: string example: 0x1234... allow_fallbacks: description: "Allow fallback to other providers on failure (default: true, false when address is set)" type: boolean example: true sort: description: 'Sort strategy: "latency" or "price" (default: price — cheapest healthy provider)' type: string example: latency trust_mode: description: 'Trust tier filter: "verified" | "private" (empty = no preference). A "verified" filter also accepts "private" providers, since private (native TeeML) provides verifiability plus privacy.' type: string example: verified github_com_0glabs_0g-router_pkg_inference.RequestMessage: type: object properties: content: type: string example: Hello! role: type: string example: user github_com_0glabs_0g-router_pkg_inference.ResponseFormat: type: object properties: json_schema: description: 'Required when type is "json_schema": {name, schema, strict?}' type: description: '"text", "json_object", or "json_schema"' type: string example: json_schema github_com_0glabs_0g-router_pkg_inference.Tool: type: object properties: function: description: Function definition allOf: - $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.ToolFunct\ ion" type: description: Tool type (currently only "function") type: string example: function github_com_0glabs_0g-router_pkg_inference.ToolFunction: type: object properties: description: description: Function description type: string example: Get weather name: description: Function name type: string example: get_weather parameters: description: JSON Schema for parameters github_com_0glabs_0g-router_pkg_inference.TraceInfo: type: object properties: billing: description: >- Billing is a pointer + omitempty so it is genuinely OMITTED (not emitted as {"input_cost":"","output_cost":"","total_cost":""}) when the cost couldn't be computed — a mode-read/pricing error or missing billing service. The success path always sets it, so the normal wire shape is unchanged. allOf: - $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.BillingIn\ fo" provider: description: Provider address type: string example: 0x1234... request_id: description: Request ID type: string example: req_abc123 tee_verified: description: "TEE verification: nil=not requested, true=verified, false=failed" type: boolean github_com_0glabs_0g-router_pkg_inference.Usage: type: object properties: completion_tokens: description: Output tokens type: integer example: 20 prompt_tokens: description: Input tokens type: integer example: 10 prompt_tokens_details: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_inference.PromptTok\ ensDetails" total_tokens: description: Total tokens type: integer example: 30 github_com_0glabs_0g-router_pkg_request.CreateAPIKeyRequest: type: object properties: allowed_models: description: |- AllowedModels is an optional allowlist of model IDs this key may invoke. Nil = no restriction. Empty slice = explicit empty list, rejected at validation (a key that can call no models is useless). type: array items: type: string allowed_providers: description: |- AllowedProviders is an optional allowlist of provider addresses (hex, 0x-prefixed) this key may target. Same nil-vs-empty convention as AllowedModels. type: array items: type: string credit_limit: description: 0G units; nil = unlimited type: string expiration: description: RFC3339, "no_expiration", or "" type: string name: type: string reset_period: description: never/daily/weekly/monthly; "" → never type: string trust_mode: description: >- TrustMode optionally pins this key to a single trust tier. Accepted values: "standard" | "verified" | "private" | "" (no pin). The wire shape is a value type rather than a pointer because the create path has no "leave unchanged" semantics — absent / empty / explicit "" all mean "no pin". type: string github_com_0glabs_0g-router_pkg_request.CreateMgmtKeyRequest: type: object properties: name: type: string scopes: type: array items: type: string github_com_0glabs_0g-router_pkg_request.PrivySigninRequest: type: object required: - token properties: cf_turnstile_token: description: Router-issued Cloudflare Turnstile response token (defence-in-depth) type: string token: description: Privy access token type: string github_com_0glabs_0g-router_pkg_request.SigninRequest: type: object required: - message - signature properties: message: description: SIWE message type: string signature: description: Signature type: string github_com_0glabs_0g-router_pkg_request.UpdateAPIKeyRequest: type: object properties: allowed_models: description: |- AllowedModels / AllowedProviders sparse-PATCH sentinels: nil → unchanged non-nil empty [] → clear the allowlist (no restriction) non-nil non-empty → replace the allowlist Pointer to slice (rather than slice) so we can distinguish "leave alone" from "clear", matching how Scopes is handled on mgmt keys. type: array items: type: string allowed_providers: type: array items: type: string credit_limit: type: string expiration: type: string name: type: string reset_period: type: string trust_mode: description: |- TrustMode sparse-PATCH sentinels: nil → unchanged; "" → clear the pin (key falls back to no trust-mode restriction); any of standard|verified|private → set the pin. type: string github_com_0glabs_0g-router_pkg_request.UpdateMgmtKeyRequest: type: object properties: name: type: string scopes: type: array items: type: string github_com_0glabs_0g-router_pkg_response.APIKeyItem: type: object properties: allowed_models: description: |- AllowedModels / AllowedProviders are ALWAYS present in the response (never omitted) — `[]` means "no restriction", a non-empty slice means "only these". Stable shape so clients can index without `field !== undefined` guards, and so a missing field cleanly signals "response truncated by middleware / bug" rather than "no allowlist". csvToSlice in the service layer materialises an empty slice (not nil) for the no-allowlist case to keep the contract. type: array items: type: string example: - '["glm-5"]' allowed_providers: type: array items: type: string example: - '["0xabc..."]' created_at: type: string example: 2024-01-15T10:30:00Z credit_limit: type: string example: "1.5" currency: description: >- Currency is the unit of CreditLimit / Used: "0g" (0G decimals) or "usd" (USD decimals), following the account's settlement mode. Without it a client can't tell whether "1.5" means 1.5 0G or $1.50. type: string example: 0g expires_at: type: string example: 2025-02-15T10:30:00Z key_id: type: string example: abc12345 key_preview: type: string example: sk-abcde… name: type: string example: my-api-key reset_period: type: string example: monthly revoked: type: boolean example: false status: description: active / revoked / expired type: string example: active trust_mode: description: |- TrustMode is the trust-tier pin for this key, or nil when the key has no pin (matches the API-key constraint table verbatim). Emitted as a pointer so callers can distinguish "no pin" from "pinned to the empty string"; the latter is rejected at create time, so a nil value is the only way the field is absent in practice. type: string example: verified used: type: string example: "0.42" github_com_0glabs_0g-router_pkg_response.APIKeyListResponse: type: object properties: data: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.APIKeyItem" object: type: string example: list github_com_0glabs_0g-router_pkg_response.AccountResponse: type: object properties: address: type: string example: "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65" created_at: type: string example: 2026-01-15T10:30:00Z onboarded_at: type: string example: 2026-01-15T10:35:00Z github_com_0glabs_0g-router_pkg_response.AdminUsageDailyEntry: type: object properties: canonical_id: description: canonical model id (the GROUP BY key); '' = unmapped type: string example: glm-5 cost: description: Cost in neuron (1 0G = 10^18 neuron). type: string example: "150000000000000" date: type: string example: 2024-01-15 input_tokens: type: integer example: 8000 model_id: description: dual-emits the same canonical value for back-compat type: string example: glm-5 output_tokens: type: integer example: 4345 request_count: type: integer example: 42 source_id: description: source filter only type: string example: 0g-app user_address: description: wallet filter only type: string example: "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65" github_com_0glabs_0g-router_pkg_response.AdminUsageDailyResponse: type: object properties: data: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.AdminUsage\ DailyEntry" object: type: string example: list github_com_0glabs_0g-router_pkg_response.AdminUsageHistoryResponse: type: object properties: data: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.AdminUsage\ LogEntry" limit: type: integer example: 20 next_cursor: description: NextCursor is the opaque token for the next page via `?cursor=`; absent on the last page. [beta] type: string x-stability: beta object: type: string example: list offset: type: integer example: 0 total: description: Total is the full-set count; omitted when include_total=false / cursor paging. type: integer example: 12345 github_com_0glabs_0g-router_pkg_response.AdminUsageLogEntry: type: object properties: api_key_id: type: string example: abc12345 cache_write_1h_tokens: description: 1-hour-TTL portion of cache_write_tokens (disjoint); only the Anthropic path reports a TTL breakdown, else 0 type: integer example: 0 cache_write_tokens: description: cache-WRITE (creation) subset of input_tokens; reflects volume, not a premium (disjoint from cached_tokens; input_tokens includes both) type: integer example: 0 cached_tokens: description: cache-READ subset of input_tokens type: integer example: 40 canonical_id: description: registry-resolved canonical id ('' if unmapped); mirrors /v1/source/usage/history type: string example: glm-5 cost: description: neuron (1 0G = 10^18) type: string example: "150000000000000" created_at: type: string credit_used: description: neuron type: string example: "50000000000000" deferred_used: description: neuron; portion charged but not yet deposit-backed type: string example: "0" deposit_used: description: neuron type: string example: "100000000000000" id: type: integer example: 1 input_tokens: type: integer example: 100 model_id: description: raw on-chain model id (per-provider attribution) type: string example: zai-org/GLM-5-FP8 output_tokens: type: integer example: 150 provider_address: type: string example: 0x1234... request_id: type: string example: req_abc123 user_address: type: string example: "0xabc1230000000000000000000000000000000001" github_com_0glabs_0g-router_pkg_response.AdminUsageStatsEntry: type: object properties: completion_tokens: type: integer example: 120000 prompt_tokens: type: integer example: 80000 source_id: type: string example: 0g-app total_cost: description: Cost in neuron (1 0G = 10^18 neuron). Decimal string. type: string example: "1234500000000000000" total_requests: type: integer example: 12345 total_tokens: type: integer example: 200000 user_address: type: string example: "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65" github_com_0glabs_0g-router_pkg_response.AdminUsageStatsResponse: type: object properties: data: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.AdminUsage\ StatsEntry" limit: description: breakdown only type: integer example: 50 object: type: string example: list offset: description: breakdown only type: integer example: 0 total: description: "breakdown only: distinct key count" type: integer example: 137 github_com_0glabs_0g-router_pkg_response.AutoDepositConfigResponse: type: object properties: default_low_watermark: type: string example: "1000000000000000000" default_max_balance: type: string example: "10000000000000000000" enabled: type: boolean example: true is_custom: type: boolean example: false low_watermark: type: string example: "1000000000000000000" max_balance: type: string example: "10000000000000000000" github_com_0glabs_0g-router_pkg_response.BatchCreditGrantResponse: type: object properties: failed: type: integer example: 1 results: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.BatchCredi\ tGrantResultItem" succeeded: type: integer example: 2 total: type: integer example: 3 github_com_0glabs_0g-router_pkg_response.BatchCreditGrantResultItem: type: object properties: amount: type: string example: "10000000000000000000" error: type: string example: "" grant_id: type: integer example: 1 user_address: type: string example: "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65" github_com_0glabs_0g-router_pkg_response.CreateAPIKeyResponse: type: object properties: allowed_models: description: |- AllowedModels / AllowedProviders are ALWAYS present in the response (never omitted) — `[]` means "no restriction", a non-empty slice means "only these". Stable shape so clients can index without `field !== undefined` guards, and so a missing field cleanly signals "response truncated by middleware / bug" rather than "no allowlist". csvToSlice in the service layer materialises an empty slice (not nil) for the no-allowlist case to keep the contract. type: array items: type: string example: - '["glm-5"]' allowed_providers: type: array items: type: string example: - '["0xabc..."]' created_at: type: string example: 2024-01-15T10:30:00Z credit_limit: type: string example: "1.5" currency: description: >- Currency is the unit of CreditLimit / Used: "0g" (0G decimals) or "usd" (USD decimals), following the account's settlement mode. Without it a client can't tell whether "1.5" means 1.5 0G or $1.50. type: string example: 0g expires_at: type: string example: 2025-02-15T10:30:00Z key: description: Only shown once! type: string key_id: type: string example: abc12345 key_preview: type: string example: sk-abcde… name: type: string example: my-api-key reset_period: type: string example: monthly revoked: type: boolean example: false status: description: active / revoked / expired type: string example: active trust_mode: description: |- TrustMode is the trust-tier pin for this key, or nil when the key has no pin (matches the API-key constraint table verbatim). Emitted as a pointer so callers can distinguish "no pin" from "pinned to the empty string"; the latter is rejected at create time, so a nil value is the only way the field is absent in practice. type: string example: verified used: type: string example: "0.42" github_com_0glabs_0g-router_pkg_response.CreateMgmtKeyResponse: type: object properties: created_at: type: string example: 2026-04-22T10:30:00Z key: description: Only shown once! type: string key_id: type: string example: abc12345 key_preview: type: string example: mk-abcde… key_type: type: string example: mgmt last_source_ip: description: >- LastSourceIP is the client IP recorded at LastUsedAt — emitted as the full address; UI is responsible for any masking before display. type: string example: 52.13.45.6 last_used_at: description: >- LastUsedAt is the most recent successful authentication via this key. nil until the key has been used at least once. type: string example: 2026-04-29T14:02:00Z name: type: string example: ci-deploy revoked: description: |- NOTE: expires_at is deliberately not emitted. The row in api_keys still has the column and the service layer still tracks it for internal callers, but mgmt keys today are non-expiring at the HTTP surface. Adding back is a single line here — no schema or service changes — once the product surface for expiration is finalised. type: boolean example: false scopes: type: array items: type: string example: - '["keys:read"' - '"keys:create"]' status: type: string example: active github_com_0glabs_0g-router_pkg_response.CreatePartnerResponse: type: object properties: api_key: type: string example: pk-foopartner-7a3f9c2e8b... api_keys: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.PartnerAPI\ KeyMeta" contact_email: type: string example: tech@foopartner.com created_at: type: string id: type: string example: foopartner key_id: type: string example: a1b2c3d4e5f60718 link: type: string example: https://foopartner.com logo_url: type: string example: https://cdn.example/logo.png name: type: string example: FooPartner revoked_at: type: string status: type: string example: active updated_at: type: string warning: type: string example: This api_key will not be shown again. Store it securely. github_com_0glabs_0g-router_pkg_response.CreditGrantListResponse: type: object properties: data: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.CreditGran\ tResponse" limit: type: integer example: 20 object: type: string example: list offset: type: integer example: 0 total: type: integer example: 100 github_com_0glabs_0g-router_pkg_response.CreditGrantResponse: type: object properties: amount: type: string example: "10000000000000000000" created_at: type: string example: 2024-01-15T10:30:00Z grant_id: type: integer example: 1 granted_by: type: string example: system reason: type: string example: Welcome bonus type: type: string example: welcome user_address: type: string example: "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65" github_com_0glabs_0g-router_pkg_response.DailyUsageResponse: type: object properties: currency: description: >- Currency is the unit of each row's cost: "0g" (wei) or "usd" (micro-USD). The series is filtered to a single currency (the account's current settlement mode for /account; "0g" for the partner /source endpoint). type: string example: 0g data: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.DailyUsage\ SummaryEntry" object: type: string example: list github_com_0glabs_0g-router_pkg_response.DailyUsageSummaryEntry: type: object properties: cache_write_1h_tokens: description: >- CacheWrite1hTokens is the 1-hour-TTL portion of cache_write_tokens summed over the day (disjoint from it). Permanent; reflects volume, not billing. [beta] type: integer x-stability: beta example: 0 cache_write_tokens: type: integer x-stability: beta example: 0 cached_tokens: description: >- CachedTokens / CacheWriteTokens are the provider-reported cache-READ and cache-WRITE (creation) subsets of input_tokens, summed over the day's rows. Permanent (survive usage_logs retention), so a monthly bill can break out cache volume and reconcile `cost`. Both reflect volume, not billing: cache_write_tokens is non-zero whenever a provider reported cache-creation tokens and does NOT by itself imply a premium was charged (the premium, when active, only changes how those tokens price into `cost`). Disjoint subsets — input_tokens already includes both (input_tokens = plain + cached_tokens + cache_write_tokens). [beta] type: integer x-stability: beta example: 3200 canonical_id: description: >- CanonicalID is the registry-resolved model id this row aggregates. Empty only for rows whose underlying on-chain model_id was never in the registry (same '' = "unmapped" sentinel as providers.canonical_id). [beta] type: string x-stability: beta example: glm-5 cost: type: string example: "150000000000000" date: type: string example: 2024-01-15 input_tokens: type: integer example: 8000 model_id: description: >- ModelID is preserved for one dual-emit milestone and carries the SAME value as CanonicalID for /daily responses. Clients should migrate to `canonical_id`; the field is dropped after migration. type: string example: glm-5 output_tokens: type: integer example: 4345 request_count: type: integer example: 42 trust_mode: description: >- TrustMode is the trust tier this row aggregates. Emitted only when the caller opts in with ?dimensions=trust_mode (and the capability is enabled); omitted otherwise, so the default wire shape is one row per (date, model) — byte-identical to pre-trust-mode prod regardless of the server flag. When emitted, an empty string represents pre-trust-mode legacy traffic (logs that predate the dimension); clients render it as "Unknown" or filter the series out. type: string example: verified github_com_0glabs_0g-router_pkg_response.ErrorDetail: type: object properties: code: type: string example: bad_request message: type: string example: Invalid request type: type: string example: invalid_request github_com_0glabs_0g-router_pkg_response.ErrorResponse: type: object properties: error: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ErrorDetai\ l" request_id: type: string github_com_0glabs_0g-router_pkg_response.FundsResponse: type: object properties: address: type: string example: "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65" currency: description: >- Currency is the unit of all amount fields: "0g" (wei, default) or "usd" (micro-USD). In USD mode there is no vault, so payment_layer.balance is "0" and total = router.subtotal = balance + credit. [beta] type: string example: 0g payment_layer: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.PaymentLay\ erFunds" router: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.RouterLedg\ erFunds" total: type: string example: "7390000000000000000" github_com_0glabs_0g-router_pkg_response.GetNonceResponse: type: object properties: expires_at: type: string nonce: type: string github_com_0glabs_0g-router_pkg_response.HealthCheckResponse: type: object properties: instance_id: type: string example: backend-abc123 status: type: string example: ok time: type: integer example: 1677652288 github_com_0glabs_0g-router_pkg_response.MeResponse: type: object properties: auth_type: type: string example: jwt privy_user_id: description: >- PrivyUserID is the session owner's Privy DID, present only for Privy logins (omitempty). SIWE / API-key sessions omit it; the frontend then falls back to wallet-address matching. [beta] type: string example: did:privy:abc123 wallet_address: type: string example: "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65" github_com_0glabs_0g-router_pkg_response.MgmtKeyItem: type: object properties: created_at: type: string example: 2026-04-22T10:30:00Z key_id: type: string example: abc12345 key_preview: type: string example: mk-abcde… key_type: type: string example: mgmt last_source_ip: description: >- LastSourceIP is the client IP recorded at LastUsedAt — emitted as the full address; UI is responsible for any masking before display. type: string example: 52.13.45.6 last_used_at: description: >- LastUsedAt is the most recent successful authentication via this key. nil until the key has been used at least once. type: string example: 2026-04-29T14:02:00Z name: type: string example: ci-deploy revoked: description: |- NOTE: expires_at is deliberately not emitted. The row in api_keys still has the column and the service layer still tracks it for internal callers, but mgmt keys today are non-expiring at the HTTP surface. Adding back is a single line here — no schema or service changes — once the product surface for expiration is finalised. type: boolean example: false scopes: type: array items: type: string example: - '["keys:read"' - '"keys:create"]' status: type: string example: active github_com_0glabs_0g-router_pkg_response.MgmtKeyListResponse: type: object properties: data: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.MgmtKeyIte\ m" object: type: string example: list github_com_0glabs_0g-router_pkg_response.ModelArchitecture: type: object properties: input_modalities: type: array items: type: string instruct_type: type: string modality: type: string output_modalities: type: array items: type: string tokenizer: type: string github_com_0glabs_0g-router_pkg_response.ModelEntry: type: object properties: architecture: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ModelArchi\ tecture" context_length: type: integer created: description: Unix timestamp type: integer example: 1737936000 default_parameters: type: object additionalProperties: {} description: type: string expiration_date: type: string id: description: The canonical model id by default; the raw provider model_id when listed with ?legacy=true type: string example: qwen-2.5-7b-instruct max_completion_tokens: type: integer name: type: string object: type: string example: model owned_by: type: string example: 0G Foundation pricing: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ModelPrici\ ng" pricing_usd: description: Per-token USD prices; only set when upstream metadata provides them allOf: - $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ModelPrici\ ng" provider_count: description: >- ProviderCount is the number of endpoints serving this canonical, counted irrespective of health — it is NOT a count of currently-healthy capacity. For per-endpoint health, call GET /v1/providers?canonical_id= (each ProviderEntry carries is_healthy). type: integer supported_formats: type: array items: type: string supported_parameters: type: array items: type: string tee_attested: type: boolean tee_type: type: string tee_verifier: type: string type: type: string verifiability: type: string github_com_0glabs_0g-router_pkg_response.ModelListResponse: type: object properties: data: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ModelEntry" object: type: string example: list github_com_0glabs_0g-router_pkg_response.ModelPriceVariant: type: object properties: dimensions: type: object additionalProperties: type: string unit: type: string unit_price: type: string github_com_0glabs_0g-router_pkg_response.ModelPricing: type: object properties: cache_write: description: >- CacheWrite is the per-token price for writing a prompt token to the cache (cache creation) at the default/5-minute TTL, i.e. prompt * write_multiplier. [beta] Present only when the provider advertises a cache-write premium (write_multiplier > 1) and features.cache_write_premium is enabled; omitted otherwise. type: string cache_write_1h: description: >- CacheWrite1h is the per-token price for writing a prompt token to the cache at the 1-hour TTL, i.e. prompt * write_1h_multiplier. [beta] Present only when the provider advertises a distinct, valid 1-hour cache-write premium and features.cache_write_premium is enabled; omitted otherwise (1-hour writes then bill at CacheWrite, or the plain prompt rate when no premium applies). type: string cached_prompt: type: string completion: type: string image: type: string prompt: type: string tiered_pricing: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ModelPrici\ ngTier" variants: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ModelPrice\ Variant" video: description: >- Video is the flat per-effective-output-second price for a single-rate video-generation model (wei in the native block, USD decimal in the USD block). Variants supersedes it when the model prices by request shape (resolution/duration) — see ModelPriceVariant. Both mirror the broker's /models pricing.video / pricing.variants so a video model shows a real per-second/per-resolution price instead of a misleading per-token one. type: string github_com_0glabs_0g-router_pkg_response.ModelPricingTier: type: object properties: cached_prompt: type: string completion: type: string max_input_tokens: type: integer prompt: type: string github_com_0glabs_0g-router_pkg_response.PartnerAPIKeyMeta: type: object properties: created_at: type: string key_id: type: string example: a1b2c3d4e5f60718 revoked: type: boolean example: false revoked_at: type: string github_com_0glabs_0g-router_pkg_response.PartnerListResponse: type: object properties: data: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.PartnerRes\ ponse" limit: type: integer example: 50 object: type: string example: list offset: type: integer example: 0 total: type: integer example: 5 github_com_0glabs_0g-router_pkg_response.PartnerResponse: type: object properties: api_keys: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.PartnerAPI\ KeyMeta" contact_email: type: string example: tech@foopartner.com created_at: type: string id: type: string example: foopartner link: type: string example: https://foopartner.com logo_url: type: string example: https://cdn.example/logo.png name: type: string example: FooPartner revoked_at: type: string status: type: string example: active updated_at: type: string github_com_0glabs_0g-router_pkg_response.PaymentLayerFunds: type: object properties: balance: type: string example: "7086440000000000000" queried_at: type: string example: 2026-05-25T16:21:00Z shared_across_products: type: boolean example: true github_com_0glabs_0g-router_pkg_response.ProjectGrantDetailResponse: type: object properties: created_at: type: string example: 2026-07-01T10:30:00Z created_by: type: string example: "0x0000000000000000000000000000000000000001" distribution_total: type: integer example: 128 distributions: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ProjectGra\ ntDistributionItem" effective_status: description: >- EffectiveStatus is the derived operator/expiry state: "expired" once past expires_at (expiry is lazy, judged at read time), else Status. It reflects ONLY the pause/expiry axis — an "active" grant can still reject distributions on quota (also check Remaining > 0). Never gate on Status alone; it hides expiry. type: string enum: - active - paused - expired example: active expires_at: type: string example: 2026-12-31T23:59:59Z grant_id: type: string example: pcg-6f9619ff-8b86-d011-b42d-00cf4fc964ff name: type: string example: 0G App launch campaign owner_address: type: string example: "0x15d34aaf54267db7d7c367839aaf71a00a2c6a65" remaining: type: string example: "750000000000000000000" status: description: Status is the STORED operator state — the writable domain of PATCH. type: string enum: - active - paused example: active total_quota: type: string example: "1000000000000000000000" updated_at: type: string example: 2026-07-01T10:30:00Z used_quota: type: string example: "250000000000000000000" github_com_0glabs_0g-router_pkg_response.ProjectGrantDistributionItem: type: object properties: amount: type: string example: "1000000000000000000" created_at: type: string example: 2026-07-01T10:30:00Z distributed_by: type: string example: "0x15d34aaf54267db7d7c367839aaf71a00a2c6a65" distribution_id: type: integer example: 42 grant_id: type: string example: pcg-6f9619ff-8b86-d011-b42d-00cf4fc964ff idempotency_key: type: string example: user-42-onboarding reason: type: string example: onboarding reward recipient_address: type: string example: "0x70997970c51812dc3a010c7d01b50e0d17dc79c8" github_com_0glabs_0g-router_pkg_response.ProjectGrantDistributionListResponse: type: object properties: data: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ProjectGra\ ntDistributionItem" object: type: string example: list total: type: integer example: 128 github_com_0glabs_0g-router_pkg_response.ProjectGrantDistributionResponse: type: object properties: amount: type: string example: "1000000000000000000" created_at: type: string example: 2026-07-01T10:30:00Z distributed_by: type: string example: "0x15d34aaf54267db7d7c367839aaf71a00a2c6a65" distribution_id: type: integer example: 42 grant: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ProjectGra\ ntQuotaSummary" grant_id: type: string example: pcg-6f9619ff-8b86-d011-b42d-00cf4fc964ff idempotency_key: type: string example: user-42-onboarding reason: type: string example: onboarding reward recipient_address: type: string example: "0x70997970c51812dc3a010c7d01b50e0d17dc79c8" replayed: type: boolean example: false github_com_0glabs_0g-router_pkg_response.ProjectGrantListResponse: type: object properties: data: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ProjectGra\ ntResponse" object: type: string example: list total: type: integer example: 3 github_com_0glabs_0g-router_pkg_response.ProjectGrantQuotaSummary: type: object properties: grant_id: type: string example: pcg-6f9619ff-8b86-d011-b42d-00cf4fc964ff remaining: type: string example: "749000000000000000000" total_quota: type: string example: "1000000000000000000000" used_quota: type: string example: "251000000000000000000" github_com_0glabs_0g-router_pkg_response.ProjectGrantResponse: type: object properties: created_at: type: string example: 2026-07-01T10:30:00Z created_by: type: string example: "0x0000000000000000000000000000000000000001" effective_status: description: >- EffectiveStatus is the derived operator/expiry state: "expired" once past expires_at (expiry is lazy, judged at read time), else Status. It reflects ONLY the pause/expiry axis — an "active" grant can still reject distributions on quota (also check Remaining > 0). Never gate on Status alone; it hides expiry. type: string enum: - active - paused - expired example: active expires_at: type: string example: 2026-12-31T23:59:59Z grant_id: type: string example: pcg-6f9619ff-8b86-d011-b42d-00cf4fc964ff name: type: string example: 0G App launch campaign owner_address: type: string example: "0x15d34aaf54267db7d7c367839aaf71a00a2c6a65" remaining: type: string example: "750000000000000000000" status: description: Status is the STORED operator state — the writable domain of PATCH. type: string enum: - active - paused example: active total_quota: type: string example: "1000000000000000000000" updated_at: type: string example: 2026-07-01T10:30:00Z used_quota: type: string example: "250000000000000000000" github_com_0glabs_0g-router_pkg_response.ProviderEntry: type: object properties: address: type: string example: "0x1234567890abcdef" architecture: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ModelArchi\ tecture" canonical_id: description: "[beta] Canonical model id this endpoint serves; empty if unmapped" type: string x-stability: beta example: glm-5.1 context_length: type: integer default_parameters: type: object additionalProperties: {} expiration_date: description: Upstream model availability expiration (RFC3339); empty = no expiration. Only visible before the instant — an expired endpoint drops out of this listing entirely. type: string is_healthy: description: nil=unknown, true=healthy, false=unhealthy type: boolean example: true latency: description: nil=no health data type: integer example: 150 max_completion_tokens: type: integer model_id: type: string example: qwen-2.5-7b-instruct name: description: Model metadata (populated when listing providers for a specific model) type: string pricing: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ModelPrici\ ng" pricing_usd: description: Per-token USD prices; only set when upstream metadata provides them allOf: - $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ModelPrici\ ng" provider_country: description: "[beta] Operator-declared serving country/locality" type: string x-stability: beta provider_name: description: >- Operator-declared, descriptive endpoint identity/locality (from the broker's /models metadata). Empty when the operator did not declare them. type: string x-stability: beta service_type: type: string example: chatbot serving_domain: description: "[beta] Operator-declared serving domain" type: string x-stability: beta supported_formats: type: array items: type: string supported_parameters: type: array items: type: string tee_acknowledged: type: boolean example: true tee_attested: type: boolean tee_type: type: string tee_verifier: type: string trust_mode: description: "[beta] Routable trust tier of this endpoint: standard | verified | private. Derived from provider_type — the authoritative tier signal (verifiability is a descriptive TEE label and is empty for standard). Use this to match a request's provider.trust_mode / X-0G-Provider-Trust-Mode floor." type: string x-stability: beta type: type: string uptime: description: nil=no health data type: number example: 99.5 verifiability: type: string github_com_0glabs_0g-router_pkg_response.ProviderListResponse: type: object properties: data: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ProviderEn\ try" object: type: string example: list github_com_0glabs_0g-router_pkg_response.PublicActivityPoint: type: object properties: date: type: string example: 2026-05-01 values: type: object additionalProperties: type: integer github_com_0glabs_0g-router_pkg_response.PublicActivityResponse: type: object properties: available: type: boolean example: true days: type: integer example: 90 metric: type: string example: tokens models: type: array items: type: string object: type: string example: usage_activity points: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.PublicActi\ vityPoint" updated_at: type: string example: 2026-05-30T08:00:00Z github_com_0glabs_0g-router_pkg_response.PublicModelUsageEntry: type: object properties: canonical_id: description: >- CanonicalID is the registry canonical id for joining /v1/models metadata; null when unresolved. [beta] type: string x-stability: beta example: glm-5 input_tokens: type: integer example: 4000000 model: type: string example: glm-5 output_tokens: type: integer example: 2400000 total_tokens: type: integer example: 6400000 github_com_0glabs_0g-router_pkg_response.PublicModelsCard: type: object properties: total: type: integer example: 6 github_com_0glabs_0g-router_pkg_response.PublicProvidersCard: type: object properties: by_type: type: object additionalProperties: type: integer total: type: integer example: 14 github_com_0glabs_0g-router_pkg_response.PublicSummaryMetric: type: object properties: total: type: integer example: 50600000 yesterday: type: integer example: 687000 github_com_0glabs_0g-router_pkg_response.PublicSummaryResponse: type: object properties: models: description: >- Models is the count of distinct user-facing (canonical) models available across the provider network. [beta] allOf: - $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.PublicMode\ lsCard" object: type: string example: usage_summary providers: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.PublicProv\ idersCard" requests: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.PublicSumm\ aryMetric" tee_proofs: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.PublicTeeP\ roofsCard" tokens: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.PublicSumm\ aryMetric" unique_wallets: description: >- UniqueWallets is the network-wide distinct-wallet count: wallets known to the Router (any user_balances row — created on login, so connected wallets count, not only funded ones) ∪ on-chain inference-ledger users, deduped. [beta] allOf: - $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.PublicUniq\ ueWalletsCard" updated_at: type: string example: 2026-05-30T08:00:00Z github_com_0glabs_0g-router_pkg_response.PublicTeeProofsCard: type: object properties: available: type: boolean example: false total: type: integer example: 24500 yesterday: type: integer example: 331 github_com_0glabs_0g-router_pkg_response.PublicUniqueWalletsCard: type: object properties: total: type: integer example: 8400 yesterday: type: integer example: 38 github_com_0glabs_0g-router_pkg_response.PublicUsageStatsResponse: type: object properties: models: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.PublicMode\ lUsageEntry" object: type: string example: usage_stats period: type: string example: 30d totals: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.PublicUsag\ eTotals" updated_at: type: string example: 2026-05-30T08:00:00Z github_com_0glabs_0g-router_pkg_response.PublicUsageTotals: type: object properties: input_tokens: type: integer example: 5000000 output_tokens: type: integer example: 3000000 total_tokens: type: integer example: 8000000 github_com_0glabs_0g-router_pkg_response.RevokeAPIKeyResponse: type: object properties: key_id: type: string example: abc12345 message: type: string example: API key revoked github_com_0glabs_0g-router_pkg_response.RevokeMgmtKeyResponse: type: object properties: key_id: type: string example: abc12345 message: type: string example: Management key revoked github_com_0glabs_0g-router_pkg_response.RevokePartnerResponse: type: object properties: id: type: string example: foopartner key_id: type: string example: a1b2c3d4e5f60718 status: type: string example: revoked github_com_0glabs_0g-router_pkg_response.RotatePartnerKeyResponse: type: object properties: api_key: type: string example: pk-foopartner-9b1c8a... key_id: type: string example: f0e1d2c3b4a59687 warning: type: string example: This api_key will not be shown again. Store it securely. github_com_0glabs_0g-router_pkg_response.RouterLedgerFunds: type: object properties: credit: type: string example: "3560000000000000" deposit: type: string example: "300000000000000000" pending_charge: type: string example: "0" subtotal: type: string example: "303560000000000000" github_com_0glabs_0g-router_pkg_response.ServiceTypeEntry: type: object properties: label: type: string example: Chat provider_count: type: integer example: 3 type: type: string example: chatbot github_com_0glabs_0g-router_pkg_response.ServiceTypeListResponse: type: object properties: data: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.ServiceTyp\ eEntry" object: type: string example: list github_com_0glabs_0g-router_pkg_response.SigninResponse: type: object properties: expires_at: type: string token: type: string wallet_address: type: string github_com_0glabs_0g-router_pkg_response.SignoutResponse: type: object properties: message: type: string example: Signed out successfully github_com_0glabs_0g-router_pkg_response.SourceUsageHistoryResponse: type: object properties: data: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.SourceUsag\ eLogEntry" limit: type: integer example: 20 next_cursor: description: NextCursor is the opaque token for the next page via `?cursor=`; absent on the last page. [beta] type: string x-stability: beta object: type: string example: list offset: type: integer example: 0 total: description: Total is the full-set count; omitted when include_total=false / cursor paging. type: integer example: 12345 github_com_0glabs_0g-router_pkg_response.SourceUsageLogEntry: type: object properties: api_key_id: description: ID of the end user's `sk-` API key (NOT your `pk-` partner key) — useful for distinguishing multiple keys under the same wallet. type: string example: abc12345 cache_write_1h_tokens: description: >- CacheWrite1hTokens is the 1-hour-TTL portion of cache_write_tokens (disjoint from it). Only the Anthropic path reports a TTL breakdown; elsewhere 0. Reflects volume, not billing. [beta] type: integer x-stability: beta example: 0 cache_write_tokens: description: >- CacheWriteTokens is the provider-reported cache-WRITE (creation) subset of input_tokens (the pair of cached_tokens). Non-zero whenever the provider reports cache-creation tokens — reflects volume, not billing (does NOT imply a premium was charged). A cache-write premium, when active, prices these above the plain prompt rate in `cost`; else they bill at the plain rate. Disjoint from cached_tokens; input_tokens already includes both (input_tokens = plain + cached_tokens + cache_write_tokens). [beta] type: integer x-stability: beta example: 0 cached_tokens: description: cache-READ subset of input_tokens (billed at the read-discount rate). type: integer example: 40 canonical_id: description: |- CanonicalID is the registry-resolved canonical model id captured at billing time (e.g. `glm-5`). Empty when the underlying on-chain `model_id` isn't a registered canonical / alias. Aggregate by this when you want "what model did the user use" (canonical concept); `model_id` stays as the on-chain literal so partner debugging tools can still drill into the specific endpoint served. [beta] type: string x-stability: beta example: glm-5 cost: description: Total request cost in neuron (1 0G = 10^18 neuron). type: string example: "150000000000000" created_at: type: string credit_used: description: Portion of `cost` paid from the user's promotional credit balance, in neuron. type: string example: "50000000000000" deposit_used: description: Portion of `cost` paid from the user's deposited (on-chain) balance, in neuron. type: string example: "100000000000000" id: type: integer example: 1 input_tokens: type: integer example: 100 model_id: type: string example: zai-org/GLM-5-FP8 output_tokens: type: integer example: 150 provider_address: description: On-chain address of the upstream 0G Compute provider that served the request. type: string example: 0x1234... request_id: description: Stable request identifier; quote this when raising support tickets. type: string example: req_abc123 trust_mode: description: |- TrustMode is the trust tier of the provider that served the request: `verified` (TeeTLS broker) or `private` (native TeeML). Always emitted; empty string for rows that pre-date the dimension (clients render as "—"). Mirror of the field on UsageLogEntry; included on the partner shape so SDKs generated from this swagger see the same contract the wire actually emits. type: string example: verified user_address: description: Wallet address that issued the inference request (your downstream end user). type: string example: "0xabc1230000000000000000000000000000000001" github_com_0glabs_0g-router_pkg_response.SourceUsageStatsResponse: type: object properties: completion_tokens: type: integer example: 120000 new_users: description: Distinct wallets whose first tagged request to this partner fell inside the date window. For lifetime queries (both dates omitted), the cumulative count of unique users you have brought to the platform. type: integer example: 158 prompt_tokens: type: integer example: 80000 total_cost: description: Cost in neuron (1 0G = 10^18 neuron). Decimal string. type: string example: "1234500000000000000" total_requests: type: integer example: 12345 total_tokens: type: integer example: 200000 github_com_0glabs_0g-router_pkg_response.StripeTopupEntry: type: object properties: amount_micro: description: AmountMicro is the gross credited amount in micro-USD. type: string example: "10000000" created_at: description: CreatedAt is when the credit was recorded (ISO8601). type: string example: 2026-07-16T06:18:42Z id: description: >- ID is the Stripe PaymentIntent id (pi_…) — the stable, idempotency-keyed reference to quote in support tickets. type: string example: pi_3QabcDEfgHIjkLMn refunded_amount_micro: description: >- RefundedAmountMicro is the cumulative refunded amount in micro-USD ("0" when none). A dispute is surfaced via `status`, not this field. type: string example: "0" status: description: >- Status: paid | partially_refunded | refunded | disputed. Derived from the clawback counters (every row is credited at baseline). type: string example: paid github_com_0glabs_0g-router_pkg_response.StripeTopupsResponse: type: object properties: currency: type: string example: usd data: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.StripeTopu\ pEntry" limit: type: integer example: 20 object: type: string example: list page: type: integer example: 1 total: type: integer example: 3 github_com_0glabs_0g-router_pkg_response.TopAppEntry: type: object properties: display_name: type: string example: FooPartner input_tokens: type: integer example: 4000000 link: type: string example: https://foopartner.com logo_url: type: string example: https://www.google.com/s2/favicons?domain=foopartner.com&sz=64 output_tokens: type: integer example: 2400000 request_count: type: integer example: 12345 source_type: description: >- SourceType distinguishes a registered partner ("registered", branding curated by 0G and spoof-proof) from a self-reported app ("self_reported", branding derived from the request's HTTP-Referer / X-Title). type: string example: registered total_tokens: type: integer example: 6400000 github_com_0glabs_0g-router_pkg_response.TopAppsResponse: type: object properties: apps: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.TopAppEntr\ y" object: type: string example: app_ranking period: type: string example: 30d updated_at: type: string example: 2026-05-30T08:00:00Z github_com_0glabs_0g-router_pkg_response.UsageHistoryResponse: type: object properties: currency: description: >- Currency is the unit of each row's cost/*_used fields: "0g" (wei) or "usd" (micro-USD). The list is filtered to a single currency (the account's current settlement mode), so all rows share this unit. type: string example: 0g data: type: array items: $ref: "#/components/schemas/github_com_0glabs_0g-router_pkg_response.UsageLogEn\ try" limit: type: integer example: 20 next_cursor: description: >- NextCursor is the opaque token to fetch the next page via `?cursor=`. Absent on the last page. [beta] type: string x-stability: beta object: type: string example: list offset: type: integer example: 0 total: description: >- Total is the full-set count. Omitted when include_total=false (or when paging by cursor). Pointer + omitempty so absence is unambiguous. type: integer example: 100 github_com_0glabs_0g-router_pkg_response.UsageLogEntry: type: object properties: api_key_id: type: string example: abc12345 cache_write_1h_tokens: description: >- CacheWrite1hTokens is the 1-hour-TTL portion of cache_write_tokens (disjoint from it; cache_write_tokens then carries only the default/5-minute portion). Only the Anthropic /v1/messages path reports a TTL breakdown; elsewhere it is 0 and cache_write_tokens carries the whole write count. Reflects volume, not billing. [beta] type: integer x-stability: beta example: 0 cache_write_tokens: description: >- CacheWriteTokens is the provider-reported cache-WRITE (creation) subset of input_tokens (the pair of cached_tokens). Non-zero whenever the provider reports cache-creation tokens — it reflects volume, not billing: it does NOT imply a premium was charged. When a cache-write premium is active it prices these tokens above the plain prompt rate in `cost`; otherwise they bill at the plain rate. Disjoint from cached_tokens, and input_tokens already includes both (input_tokens = plain + cached_tokens + cache_write_tokens) — do not add them on top of input_tokens. [beta] type: integer x-stability: beta example: 0 cached_tokens: description: cache-READ subset of input_tokens (billed at the read-discount rate) type: integer example: 40 canonical_id: description: >- CanonicalID is the registry-resolved canonical model id captured at billing time. Empty when the underlying on-chain `model_id` isn't a registered canonical / alias (same '' = "unmapped" sentinel as providers.canonical_id). Clients that need a non-empty display string should render `canonical_id || model_id`. `model_id` remains the on-chain literal so per-endpoint drill-down still works. [beta] type: string x-stability: beta example: glm-5 cost: type: string example: "150000000000000" created_at: type: string example: 2024-01-15T10:30:00Z credit_used: type: string example: "50000000000000" deposit_used: type: string example: "100000000000000" id: type: integer example: 1 input_tokens: type: integer example: 100 model_id: type: string example: qwen-2.5-7b-instruct output_tokens: type: integer example: 50 provider_address: type: string example: 0x1234... request_id: type: string example: req_abc123 trust_mode: description: |- TrustMode is the trust tier of the provider that served the request. Always emitted; empty string for rows that pre-date the dimension (clients render as "—"). usage_logs.trust_mode is NOT NULL DEFAULT '' (AutoMigrate creates the column that way), so the value is either a tier name or ''. type: string example: verified github_com_0glabs_0g-router_pkg_response.UsageStatsResponse: type: object properties: completion_tokens: type: integer example: 4345 currency: description: >- Currency is the unit of TotalCost: "0g" (wei) or "usd" (micro-USD). The figures are already filtered to a single currency (the account's current settlement mode), so a client renders TotalCost in this unit without a second lookup and never mislabels wei as USD. type: string example: 0g prompt_tokens: type: integer example: 8000 total_cost: type: string example: "1000000000000000" total_requests: type: integer example: 42 total_tokens: type: integer example: 12345 internal_handler.CreatePartnerRequest: type: object required: - id - name properties: contact_email: type: string example: tech@foopartner.com id: type: string example: foopartner link: type: string example: https://foopartner.com logo_url: type: string example: https://cdn.example/logo.png name: type: string example: FooPartner internal_handler.RoutePreviewCandidate: type: object properties: address: type: string canonical_id: type: string endpoint: type: string model_id: type: string internal_handler.RoutePreviewRequest: type: object properties: model: description: "OPTIONAL: canonical id (or alias / on-chain id); empty = any provider of this service type" type: string service_type: description: chatbot | text-to-image | image-editing | speech-to-text type: string internal_handler.RoutePreviewResponse: type: object properties: object: type: string providers: type: array items: $ref: "#/components/schemas/internal_handler.RoutePreviewCandidate" service_type: type: string internal_handler.UpdatePartnerRequest: type: object properties: contact_email: type: string link: type: string logo_url: type: string name: type: string example: FooPartner (renamed) status: type: string example: active internal_handler.UploadFileResponse: type: object properties: expires_at: type: string example: 2026-03-01T14:00:00Z filename: type: string example: document.pdf id: type: string example: file-abc123 mime_type: type: string example: application/pdf size: type: integer example: 1024 internal_handler.batchGrantRequest: type: object required: - grants properties: grants: type: array maxItems: 100 minItems: 1 items: $ref: "#/components/schemas/github_com_0glabs_0g-router_internal_service.GrantC\ reditRequest" internal_handler.createAdminScopedKeyRequest: type: object required: - name - scopes properties: expiration: description: |- Expiration is optional: RFC3339 (e.g. 2026-12-31T23:59:59+08:00) or "no_expiration"/empty for a non-expiring key. Same grammar as ParseExpiration elsewhere. type: string name: type: string scopes: description: admin-only scopes, e.g. ["usage:read"] type: array minItems: 1 items: type: string internal_handler.putAdminRoleRequest: type: object properties: scopes: type: array items: type: string internal_handler.setSettlementModeRequest: type: object properties: mode: description: '"0g" or "usd"' type: string example: usd internal_handler.stripeCheckoutRequest: type: object required: - amount_micro - success_url properties: amount_micro: type: string cancel_url: type: string email: description: >- Email (optional) → Stripe customer_email: prefills the email field + gets the receipt. Not persisted server-side. type: string success_url: description: >- SuccessURL (required) / CancelURL (optional) → the browser return targets. The frontend owns the path (e.g. its locale prefix); the backend validates only the origin against auth.allowed_origins. There is no server-side default — success_url must be supplied on every request. type: string internal_handler.updateProjectGrantRequest: type: object required: - status properties: status: description: '"active" | "paused"' type: string