How every tool call works
Every Wexa tool is the same tool on four surfaces: the Wexa MCP server, the REST API, the TypeScript SDK and the Python SDK. Each tool page shows only what is specific to that tool. Everything else is here, once.
Naming on each surface
A tool has one wire name, written in lowercase with hyphens: run-agent. Each surface spells it its own way.
| Surface | Name for run-agent | Rule |
|---|---|---|
| Wexa MCP server | run-agent | The wire name. |
| REST API | POST /v1/run-agent | POST https://fabric.wexa.ai/v1/ plus the wire name. |
| TypeScript SDK | fabric.runAgent() | The wire name in camel case. |
| Python SDK | fabric.run_agent() | The wire name with underscores. |
Argument names are the wire names on every surface; only method names change.
More naming
create-ontology is the one tool whose REST route differs: both SDKs post to /v1/ontology, and /v1/create-ontology answers too.
Both SDKs also have fabric.call(name, ...), which takes either spelling, run-agent or run_agent.
Return shape
Every tool returns the same data on every surface, wrapped differently on each. Code ported by hand between the two SDKs gets undefined rather than an error, because the id names differ.
| Surface | What you get |
|---|---|
| REST API | {"lifecycle_id": "qlc_...", "result": { ... }}. Read result. |
| TypeScript SDK | The result, already unwrapped: const result = await fabric.runAgent({...}). There is no .result to read: result.result is undefined. The ids are non-enumerable properties named lifecycleId and traceId, so Object.keys() and JSON.stringify() omit them. |
| Python SDK | The result, already unwrapped. The ids are attributes named lifecycle_id and trace_id. |
| Wexa MCP server | {"content": [{"type": "text", "text": "..."}], "isError": false}, where text is the REST body as a JSON string. Parse it. |
Lifecycle and governance
Every call passes the same stages, in order, and each is recorded: authenticate, resolve scope, rate and quota, validate, policy, approval, dry run, execute, post-process, record. A call that fails a stage stops there. The lifecycle_id in the response is the handle to that call's record. Keep it, and quote it when asking why a call was allowed, refused or held.
A tool that changes something is consequential. It is audited in full, and a policy rule may hold it for a person to approve. A tool page shows a "Can be held for approval" badge under its title when the write gate can hold its tool. A held call returns status 202 on REST, and the SDKs raise ApprovalRequired, which carries the approval id. The Wexa MCP server returns a non-error result (isError: false) holding the pending-approval payload instead. When a project admin turns on the project's write gate, an API key or an SDK session also has its consequential writes held. The write gate never holds a read, but a policy rule may still hold or refuse any tool, and every call uses quota.
To wait for the decision and carry on, pass wait_for_approval=True in Python or waitForApproval: true in TypeScript. The wait is 600 seconds by default.
The platform fills in four arguments from your credential: project_id, projectID, organization_id and executed_by. They are server-bound arguments. Both SDKs refuse them before the request leaves your process. See what scope a call carries.
Each tool page names the grant its tool needs. For the stages in full, see lifecycle and audit.
Errors
Both SDKs raise the same typed errors, all extending WexaError. They choose the class from the gateway's error string, then the HTTP status, so one refusal is the same class in both. In Python the timeout class is TimeoutError_. A parameter's rule sits in its row on a tool page; a refusal tied to no parameter sits in its collapsed Errors block.
| Class | Status | Raised when |
|---|---|---|
AuthError | 401 | Missing, malformed or revoked credential. |
ForbiddenError | 403 | The credential lacks the tool's grant or the project's scope. |
PolicyDenied | 403 | A policy rule refused the call. Extends ForbiddenError. |
ValidationError | 400 / 422 | A value breaks its parameter's rule, or the body is malformed. |
ApprovalRequired | 202 | A person must approve the call first. |
ApprovalError | 403 | The resume token is bad, expired or spent. |
QuotaExceeded | 429 | Over the request window or credit balance. |
NotFound | 404 | An id or name matches nothing in your project. |
ConflictError | 409 | The change collides with current state. |
UpstreamError | 502 | A downstream service refused or was unreachable. |
TimeoutError | 504 | An upstream deadline was exceeded. Extends UpstreamError. |
ConfigurationError | 502 / 503 | The tool is not enabled in your deployment. |
TransportError | none | No response arrived. TypeScript only: Python raises OSError, which is not a WexaError. |
These are the defaults for every tool, so a tool page does not restate them. It names any exception in a parameter's row or its Errors block:
- An id from another project is
NotFound(404). It reads as an id that never existed. - A value that breaks its parameter row's rule is
ValidationError(422): missing, empty, the wrong type or format, out of range. - A call that the write gate or a policy rule holds is not a failure. It raises
ApprovalRequired(202 on REST). The "Can be held for approval" badge marks the tools the write gate can hold; a policy rule may hold any tool, for example a read of classified data. See lifecycle and governance and policy and approvals.
On REST you get the status and an error and error_description pair. On the Wexa MCP server a refusal comes back as isError: true, with the stage and message in content[0].text. An unknown tool name is a JSON-RPC error instead.
Retries are off unless you pass retry. Only UpstreamError (so TimeoutError too) and QuotaExceeded are retried; ConfigurationError never is. The gateway's Retry-After is honoured. For the full set of classes and what to retry, see errors and retries.
Identifiers
Wexa issues opaque ids and checks their format before a tool runs, so an id in the wrong argument is refused. Read the id from the output field that names it and pass it unchanged. Which identifier goes where covers the two cases that need care.
Find a tool
Every tool has a page in the sidebar, except the documentation tool docs. The tools array of GET /v1/connection-info lists the tools your deployment registers; see discovery.
Sign-in calls are not governed tools
login, refresh-session and logout sign a Wexa user in through an SDK. They run before any tool call, on the REST API and both SDKs only, and they do not pass the lifecycle above. See login, refresh-session and logout.