run-model
Send a prompt to one of this organization's AI models and get the answer back, with the tokens
consumed, the cost and the latency. model takes any id list-models returns and is optional: omit
it to run the organization's default. Every response echoes the model that actually ran and a
source saying why it was chosen.
What it is called on each surface
The wire name is run-model everywhere: it is what the Wexa MCP server advertises, the REST path is
POST /v1/run-model, and each SDK exposes it under its own language's naming convention.
| Surface | Name |
|---|---|
| Wexa MCP server | run-model |
| REST API | POST /v1/run-model |
| TypeScript SDK | fabric.runModel() |
| Python SDK | fabric.run_model() |
Arguments
Send either prompt or messages. Everything else is optional.
| Argument | Type | Required | Notes |
|---|---|---|---|
prompt | string | One of | The prompt, treated as a single user message. Ignored when messages is set. |
messages | array | One of | A conversation as {role, content} pairs. Roles are system, user, assistant. Wins over prompt. |
model | string | Optional | Any id list-models returns. Omit to run the organization's default. |
max_tokens | integer | Optional | Ceiling on the tokens generated. Defaults to 512. |
temperature | number | Optional | 0 to 2. Defaults to 0.2. |
top_p | number | Optional | Nucleus sampling, 0 to 1. |
stop | array | Optional | Up to four sequences that end generation. |
seed | integer | Optional | Best-effort determinism, where the provider supports it. |
response_format | object | Optional | e.g. {"type": "json_object"}. |
stream | boolean | Optional | Not supported. true is refused — see no streaming. |
A value outside its range is refused, not clamped. Silently adjusting a number you chose would hand you a call you did not ask for.
project_id, projectID, organization_id and executed_by are not arguments you pass: the
platform binds all four from the credential you authenticated with, and both SDKs refuse them
before the request leaves your process — see
server-bound arguments.
Arguments bound to the model
run-model refuses a further set, bound to the model rather than to your credential: endpoint,
api_base, api_version, secret_ref, secret_param, api_key, provider and extra_params.
Wexa fills all of them from the model's own registry entry, and both SDKs refuse them before the
request leaves your process.
This is deliberate rather than incidental. The provider host is checked against your organization's allow-list at call time, not when the model was registered, so withdrawing permission for a host immediately blocks a model registered while it was still permitted. Provider pass-through is administered per model and checked before it reaches execution. A per-request version of either would be an unchecked route into provider routing and credential placement. Your provider credential is resolved at the point of execution and never reaches you.
Which models you can name
run-model accepts exactly the identifiers list-models returns — a
registry id from settable, or a composite from available. Discovery and invocation are one story:
list, then call. There is no second naming scheme.
A free-text provider name such as gpt-4o is refused. A bare provider name has no credential to
resolve and no host to check, and two registered entries may share a provider model id with different
credentials, so there is no correct one to pick. See
which identifier goes where.
What it returns
The three surfaces wrap this differently, and code written against one will not read another correctly — see return shape.
A real response, captured from a live call to run-model:
{
"lifecycle_id": "qlc_000023",
"result": {
"output": "pong",
"model": "mdl_bx_anthropic-claude-haiku-4-5-20251001-v1_28ccc24bfca8",
"source": "requested",
"tokens": 28,
"cost_usd": 0.000028,
"latency_ms": 621
}
}
model is the model that actually ran, not the one you asked for. source says why it was
chosen:
source | Meaning |
|---|---|
requested | You named this model |
organization default | You named none, and this is the default the organization chose |
platform default | You named none and the organization has chosen none, so a platform fallback ran |
Read both before attributing a charge. A platform default you did not expect is the signal that
nobody has chosen an organization default. On an enterprise deployment the call is refused outright in
that state rather than routed to a fallback, so no work runs on a model nobody selected.
Calling it
The same call on all four surfaces. Pick a tab once and every code block in the documentation follows it.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "run-model",
"arguments": {
"model": "mdl_bx_anthropic-claude-haiku-4-5-20251001-v1_28ccc24bfca8",
"prompt": "Summarize Q3 in one line."
}
}
}A conversation
messages carries a system message and as many turns as you like.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "run-model",
"arguments": {
"model": "mdl_bx_anthropic-claude-haiku-4-5-20251001-v1_28ccc24bfca8",
"messages": [
{ "role": "system", "content": "Answer in one sentence." },
{ "role": "user", "content": "What changed in Q3?" }
],
"temperature": 0.2
}
}
}There is no tool role, and no tool calling. Call run-agent when you want
an agent's tools.
Each call is independent: Wexa keeps no conversation for you. Send the turns you want the model to see.
Errors
Both SDKs raise the same typed errors, chosen from the gateway's own error string first and its HTTP
status second. run-model checks the fabric:model.run grant, and reaches the seven classes every
tool reaches — listed under the common set. These are the ones
specific to it:
| Code | Status | Raised when | Retry? |
|---|---|---|---|
S8:model-not-found | 404 | No such model in this organization | No — fix the id |
S8:model-forbidden | 403 | The model's host is no longer permitted | No — an administrator must act |
S8:model-invalid-parameters | 422 | A parameter was outside its range, or is not yours to set | No — fix the request |
S8:model-call-limited | 429 | This key's own model-call budget is full | Yes, shortly |
S8:model-rate-limited | 429 | The provider rate-limited the call | Yes, with backoff |
S8:model-upstream-error | 502 | The provider failed | Yes |
The two 429s are separate on purpose: one means you are asking Wexa for too much at once, the other means your provider is. The provider's own message is passed through, because it is what makes a credential or access failure diagnosable.
Both SDKs classify these for you and stop retrying the terminal ones, so a revoked model does not cost you a full backoff cycle on every call.
When the model registry is briefly unreachable
Calls naming a composite keep working — a registry interruption is not a total model-calling outage.
Calls naming a registry id fail with a reason that says the registry could not be read, deliberately
not with S8:model-not-found, which is terminal and would read as a typo. Retry those.
list-models reports the same condition, so you can tell a short model
list from a truncated one.
Limits and billing
Model calls are limited separately from ordinary API calls, and by how many are in flight rather than by how many per minute — a long generation and a listing are not the same load. Exhausting your model-call budget does not block your other calls, and exhausting the ordinary budget does not block your model calls.
Every call is metered against the API key that made it, so per-key spend answers "which of my
integrations is spending this" before the invoice does. The spend draws down the same balance as every
other metered use; there is not a second one to watch. The response's tokens and cost_usd let you
meter yourself without waiting for a billing report.
No streaming
run-model does not stream. stream: true is refused with a clear message rather than served the
whole answer framed as a single chunk.
A stream that does not stream invites interfaces built against timing and chunk boundaries that would change the moment real streaming arrived, with no way to tell who depended on the old behaviour. An honest refusal is a smaller promise than one that has to be kept.
Governance
Every call to run-model runs through the same ten-stage lifecycle as every other Wexa tool: the
credential is resolved to a scope, the fabric:model.run grant is checked, quota is drawn down, the
arguments are validated, policy rules are evaluated, the call is executed, the result is shaped and
redacted, and an audit record is written. The lifecycle_id in the response is the handle to that
record.
run-model is not marked consequential, so it does not enter the approval workflow. It does spend
money, as running an agent does, but a human approval per model call would make the capability
unusable in a request path; spend is controlled by metering and the separate rate-limit bucket
instead.
fabric:model.run is its own grant, deliberately not fabric:model.write: reading or editing a
model's configuration and spending money through it are different privileges, so an application key
that calls models cannot reconfigure the registry.
See also
Models for how a model is chosen and what platform-provided versus
bring-your-own means for the bill, list-models,
set-model, the tool index,
and errors.