call_api
This page answers how to call the OpenX api through the MCP server, what call_api takes, and the
refusals it answers with.
Purpose
call_api calls one read-only route of the OpenX api, as you: only the routes your access allows,
read from the api spec for your sign-in. Each saved warm query has its own route, so running a
source of truth is one call. Changes never go through call_api; they go through
manage_proposals.
It sends the call with a token the server gets for you from your sign-in, never your own token, and with
your workspace named in the x-oi-workspace header. It reads your route list again at most once a minute.
List the routes first
Pass route "" to list the routes you may call. Each entry has its key, a one-line summary and
the query keys it declares. The answer is { Routes, remedy }.
{ "route": "" }
Inputs
| Input | Type | Meaning |
|---|---|---|
route | text | The route key: a method, one space, then a path, such as "GET /warm-queries/orders-by-day". "" lists the routes. |
params | map of text | One value per {placeholder} in the route path, such as { "lookup": "orders" }. |
query | map | The route's declared query values. A key the route does not declare is refused. |
body | map | The JSON body, only for a read-only POST. |
Examples
Run a saved warm query:
{ "route": "GET /warm-queries/orders-by-day", "query": { "limit": 5 } }
Check a draft warm query without saving it:
{ "route": "POST /warm-queries", "body": { "WarmQuery": {}, "CheckOnly": true } }
The answer is { Status, ContentType, Data }: the route's whole answer. call_api returns JSON, CSV or
NDJSON; a route that answers anything else is refused.
A warm query as a file
A warm query's whole result can be downloaded as a file on its own address, with the reserved query key $cold
(see Cold-data export). A data connection's stored cold files are
available through call_api on GET /downloads/data-connections/{lookup}/cold.
The api spec declares $cold and $requestId on a warm query's routes, and call_api passes them through. Start the file
(202 with a requestId):
{
"route": "POST /warm-queries/orders-by-day",
"query": { "$cold": true },
"body": { "format": "csv" }
}
Then fetch it once:
{
"route": "GET /warm-queries/orders-by-day",
"query": { "$cold": true, "$requestId": "<requestId>" }
}
A GET with $cold and no $requestId lists your own downloads of that query. A draft that is not saved does the
same on its own route: POST /warm-queries with $cold in query and the draft envelope plus format as the
body, then GET /warm-queries with $cold and the $requestId. The answers, and what they refuse, are in
A warm query's rows as a file.
Licenses reads
call_api can read a workspace's licences. The route keys are relative, like every other, and each takes the optional
query value licenses, a comma list of licence lookups (absent reads every licence):
| Route key | Right it needs | Answers |
|---|---|---|
GET /licenses/catalogue | None | The plans and prices of every enabled licence |
GET /licenses/plan | Licenses.Read | What the next Deploy would change in Stripe |
GET /licenses/health | Licenses.Read | Each licence's Stripe health: Status, Problems, Checks |
GET /licenses/events | Licenses.Read | The Stripe events each licence received, and 24-hour counts |
GET /licenses/stats | Licenses.Stats.Read | The main Stripe figures: MRR, active, trials, past due, recent |
GET /licenses/subscribers | Licenses.Manage | Who holds which plan, with an email each |
GET /licenses/mine/{licLookup} | None: your own licence | The signed-in person's own licence and subscription |
{ "route": "GET /licenses/health", "query": { "licenses": "my-licence" } }
{ "route": "GET /licenses/mine/{licLookup}", "params": { "licLookup": "my-licence" } }
The answers are the ones in the Licenses API. A licence that could not be read
answers Known: false with a Reason, never zeros: say "could not be read" to the person, not "nothing". The
rights are flat, so a right is needed exactly as named; the route list you get for "" shows only the routes your
access allows. A call without the right is CALL_API_UPSTREAM_REFUSED with UpstreamCode ACCESS_RIGHT_REQUIRED
and the right in MissingRights.
These routes are read-only and GET only. Subscribing, cancelling and opening a checkout or portal are writes: they
are not reachable through call_api, and a person or their own app makes them (see the
Licenses API). No Stripe key or payment data is ever in an answer. The shorter
view is get_stats, whose Licenses line gives each licence's
health Status and problem codes.
Errors
Each refusal has a code, a message and a remedy.
| Code | Message | What to do |
|---|---|---|
CALL_API_NO_WORKSPACE | "This sign-in names no workspace, so no api route can be called for it." | Connect again with a sign-in for one workspace |
CALL_API_ROUTE_INVALID | "The route must be a method, one space, then a path from the api_spec resource." | Pass route "" and copy a key |
CALL_API_ROUTE_UNKNOWN | "No route in this workspace's api_spec has that key." | Use a listed key; the answer names the nearest ones in Nearest |
CALL_API_ROUTE_NOT_READ_ONLY | "call_api calls only routes the spec tags read-only." | Propose the change with manage_proposals; a person accepts it |
CALL_API_STREAM_NOT_SUPPORTED | "This route streams, and call_api returns one response." | Use a saved warm query for recent history |
CALL_API_PARAM_INVALID | A path value is missing, extra or holds a character that would change the path; or a query key is undeclared, not a string, number or true/false, or required and missing | The remedy names the route's own params and query keys |
CALL_API_BODY_NOT_ALLOWED | "Only a read-only POST takes a body." | Drop the body, or pass its values as query values |
CALL_API_URL_MISMATCH | "The call would leave the api this server is configured for." | Pick another route |
CALL_API_UNREACHABLE | The api did not answer, or answered with a redirect, which is not followed | Ask again shortly |
CALL_API_UPSTREAM_REFUSED | The api refused or failed the call. Carries Status, and the api's UpstreamCode, UpstreamRemedy and MissingRights when it sent them | Follow the remedy; see Errors and limits |
CALL_API_CONTENT_TYPE_REFUSED | "The route answered with a content type call_api does not return." | Only JSON, CSV or NDJSON routes can be called |
CALL_API_SPEC_UNAVAILABLE | The api spec for this sign-in could not be read | Ask again shortly |
CALL_API_SPEC_UNREADABLE | "The api answered with something that is not a spec." | Ask again shortly; the api serving the workspace may be out of date |
CALL_API_SPEC_INVALID | "The api spec is not a valid OpenAPI 3.1 document, so no route is called from it." | The api serving the workspace needs a fix |
CALL_API_SPEC_NOT_RELATIVE | "The api serving this workspace is older than call_api: its spec paths carry the api root." | call_api works again once that api is updated |