On this page

SDKs and APIs

Chat completions

POST /v1/agents/{agentflowId}/chat/completions runs a governed Wexa agent behind an OpenAI-compatible response, so an OpenAI client can point at it. It is the endpoint of a Proxy agent and answers only for an agent with a promoted version. {agentflowId} is the part of the agent id before the dot, see identifiers.

curl -sS -X POST https://fabric.wexa.ai/v1/agents/af_abc123/chat/completions \
  -H "Authorization: Bearer $FABRIC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-4o","messages":[{"role":"user","content":"How did Q3 revenue go?"}]}'
{
  "id": "chatcmpl-exec_abc_77",
  "object": "chat.completion",
  "model": "af_abc123",
  "choices": [{
    "index": 0,
    "message": { "role": "assistant", "content": "Q3 revenue was 4.2M, up 11% on Q2." },
    "finish_reason": "stop"
  }]
}

Parameters

ParameterTypeRequiredNotes
agentflowIdstringRequiredPath. The agent id before the dot. An unknown agent is refused (404, agent_not_found); one with no promoted version, (409, not_ready).
messagesarrayRequiredThe last message with role: "user" and non-empty content becomes the agent's goal. Earlier messages and a system prompt never reach the agent. With no role: "user" message the call is refused (400, missing_messages).
streambooleanOptionaltrue switches the response to server-sent-event framing.
model, temperature, tools, top_p, max_tokens, response_formatOptionalAccepted and ignored.
Errors
StatusCodeRaised when
400invalid_jsonThe body is not JSON.
403forbiddenThe credential lacks the fabric:agent.run grant.
429rate_limitedMore than 60 calls in one minute on one key; Retry-After is always 1.
502data_service_unreachableThe execution target is unreachable.
503not_configuredThe deployment has no execution target.
504execution_timeoutThe run exceeded the deployment's timeout.
Show full response

A real response, captured from a live call:

{
  "id": "chatcmpl-exec_abc_77",
  "object": "chat.completion",
  "created": 1789469617,
  "model": "af_abc123",
  "choices": [{
    "index": 0,
    "message": { "role": "assistant", "content": "Q3 revenue was 4.2M, up 11% on Q2." },
    "finish_reason": "stop"
  }],
  "usage": { "prompt_tokens": 0, "completion_tokens": 812, "total_tokens": 812 }
}

id is chatcmpl- followed by the execution id, so a response can be traced back to the run that produced it. model in the response is the agent id, not whatever you sent.

More details
Which request fields are real, and streaming

Which request fields are real

Only two.

FieldEffect
messagesthe last message with role: "user" and non-empty content becomes the agent's goal
streamswitches the response to server-sent-event framing

Everything else — model, temperature, tools, top_p, max_tokens, response_format — is accepted and ignored. The call above sent "model":"gpt-4o", "temperature":0.2 and a tools array, and the execution target received only {"goal":"How did Q3 revenue go?","caller_id":"user_abc","project_id":"proj_abc"}.

Only the last user message is sent to the agent, as its goal. Earlier messages, and a system prompt sent alongside it, do not reach the agent, so the agent does not see the conversation the client holds. If there is no user message at all:

{ "error": { "message": "messages[] must include at least one message with role \"user\"",
             "type": "invalid_request_error", "code": "missing_messages" } }

Streaming is framing, not incremental tokens

"stream": true produces exactly the frames an OpenAI client expects, in the right order:

data: {"id":"chatcmpl-exec_abc_77","object":"chat.completion.chunk","created":1789469617,"model":"af_abc123","choices":[{"index":0,"delta":{"role":"assistant","content":"Q3 revenue was 4.2M, up 11% on Q2."},"finish_reason":null}]}

data: {"id":"chatcmpl-exec_abc_77","object":"chat.completion.chunk","created":1789469617,"model":"af_abc123","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: [DONE]

Content-Type becomes text/event-stream, and the whole answer arrives in the first chunk.

Rate limit and failure shapes

Rate limit: 60 per minute, per key

This route is the only place on the gateway where the per-key limiter is consulted.

A single fresh key was fired at the endpoint 65 times in one window. The first 60 answered 200; the 61st and everything after it answered:

HTTP/1.1 429 Too Many Requests
Retry-After: 1

{"error":{"message":"too many requests for this API key — try again shortly","type":"rate_limit_exceeded","code":"rate_limited"}}

A second key, minted on the same project, answered 200 at the same moment — the window is per key id, not per project or per user.

Three details that change how you should retry:

  • The window is a fixed one minute, not a sliding one. The allowance resets wholesale rather than draining.
  • Retry-After is a hard-coded 1, not the real seconds remaining. Back off on your own schedule; waiting one second immediately after the 61st call will usually be refused again.

The project and organization quota windows are separate, apply to the governed tool routes, and produce Wexa's own error body with a genuinely computed Retry-After. Quota and credits covers them.

A credential with no key id — a user token — is not subject to this limiter at all. That was confirmed: the same request with a user token answered 200.

Every failure shape

Six of the nine were produced live; the ledger at the foot of the page says which.

Statustype / codeWhen
400invalid_request_error / invalid_jsonthe body is not JSON
400invalid_request_error / missing_messagesno message with role: "user"
403insufficient_scope / forbiddenthe credential lacks the fabric:agent.run grant
404invalid_request_error / agent_not_foundno such agent
409invalid_request_error / not_readythe agent has no promoted version
429rate_limit_exceeded / rate_limitedthe per-key limit above
502upstream_error / data_service_unreachablethe execution target is unreachable
503unavailable / not_configuredthe deployment has no execution target configured
504timeout / execution_timeoutthe run exceeded the deployment's timeout

The 409 deserves a note, because its message is unusually long on purpose:

{ "error": { "type": "invalid_request_error", "code": "not_ready",
  "message": "agentflow has no promoted version promote-flow will NOT fix this: it snapshots the flow's shape, not the agent's config. If this agent already has versions, rollback-agent-version makes one live and this endpoint then works — list them with agent-versions. Creating the FIRST version needs the Fabric console (Agent Simulation, an Enterprise-tier feature). run-process-flow and run-agent require no promotion at all." } }

An agent must have a promoted version before this endpoint will run it. If yours does not, run-agent and run-process-flow need no promotion and are the way through.

Compatibility limits and the per-route ledger

Four compatibility limits

  1. 401 does not speak OpenAI. Every other failure on this route uses the OpenAI error envelope; a missing or invalid credential is refused by the gateway's shared authentication layer before the handler runs, and comes back as Wexa's own shape — {"error":"invalid_token","error_description":"missing bearer credential"}. An OpenAI client that parses errors strictly will fail to parse that one. Handle 401 separately.
  2. usage.prompt_tokens is always 0. The execution target reports a total; the gateway puts it in both total_tokens and completion_tokens and leaves the prompt count at zero. Do not compute cost from these fields.
  3. n, logprobs, tools and function calling are not supported. There is always exactly one choice, finish_reason is always stop on success, and tool use happens inside the agent rather than being negotiated with the caller.
  4. There is no GET /v1/models. The agent id in the URL is the model selector; a client that lists models at startup will not find one.

Per-route ledger

CaseResult
happy path, extra OpenAI fields sent200; execution target received only goal, caller_id, project_id
stream: true200, text/event-stream, three frames as quoted
no user message400 missing_messages
no promoted version409 not_ready with the full remedy message
unknown agent404 agent_not_found
key without fabric:agent.run403 forbidden
no credential401, in Wexa's envelope, not OpenAI's
user token instead of a key200, not rate-limited
65 calls on one key60 × 200, then 429 with Retry-After: 1
second key during the refusal200
execution target unreachable502 data_service_unreachable

The status mapping, the request translation and the rate limit are gateway behaviour. The answer text and the token count come from whatever execution target your deployment runs.

Every tool call works the same way: How every tool call works.