> Source: https://wexa.ai/docs/surfaces/rest/overview

# REST API overview

The REST API is the surface with nothing to install. If you can make an HTTP request you can call
every Wexa tool, and the two SDKs are thin clients over exactly these routes — nothing is
reachable from them that is not reachable here.

If you have not made a first call yet, start at the
[REST quickstart](/docs/get-started/quickstart/rest) and come back here for the shape of the whole
surface.

## One convention for tools, conventional REST for everything else

Wexa splits its routes in two, and the split is worth learning before anything else.

**Every tool is `POST /v1/<tool-name>`**, kebab-cased, with a JSON body of arguments. There are no
path parameters, no query strings and no verbs other than POST. `GET` on a tool route answers `405`
with an `Allow: POST` header.

```bash
curl -s -X POST "$BASE/v1/query-context" \
  -H "Authorization: Bearer $WEXA_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"query":"MATCH (c:Customer) WHERE c.project_id = $project_id RETURN c.name AS name LIMIT 5"}'
```

**Everything else is conventional REST** — `GET` for reads, plural collections, sub-paths for
actions:

```bash
curl -s "$BASE/v1/whoami"        -H "Authorization: Bearer $WEXA_API_KEY"
curl -s "$BASE/v1/approvals"     -H "Authorization: Bearer $WEXA_API_KEY"
curl -s "$BASE/v1/lifecycles/qlc_000002" -H "Authorization: Bearer $WEXA_API_KEY"
```

The gateway states this rule about itself. Calling the documentation tool and reading the `rules`
array returns, verbatim:

```text
REST shape: every TOOL is `POST /v1/<tool-name>` in kebab-case; everything else (whoami, approvals,
audit, lifecycles, catalog) is conventional REST — GET for reads, plural collections, sub-paths for
actions
```

## The uniform envelope

Every successful tool call returns the same two-key object. There is no per-tool wrapper to learn.

```json
{
  "lifecycle_id": "qlc_000007",
  "result": { "nodes_merged": 1, "relationships_merged": 0, "ontology_extended": true, "new_labels": ["Customer"] }
}
```

`lifecycle_id` identifies the governed lifecycle the call ran as. Hand it to
`GET /v1/lifecycles/{id}` and you get the stage-by-stage record — which stage passed, which refused,
and why. A refusal carries the same `lifecycle_id` in its error body, so a failure is traceable by
exactly the same means as a success.

`result` is the tool's own payload, and its shape is documented per tool in the
[tool reference](/docs/tools/overview).

## The documentation tool is a tool, not a specification

`POST /v1/docs` is a tool like any other. It takes a `topic`, it returns prose and structured
facts about your own scope, and it is invoked to *read* — it is not a machine-readable API
specification, it serves no OpenAPI or Swagger document, and it returns no schema of any kind.

What it does return is worth a call before you debug anything: your effective grants, your project's
mode, your live quota headroom, and the gateway's own operating rules.

```bash
curl -s -X POST "$BASE/v1/docs" \
  -H "Authorization: Bearer $WEXA_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"topic":"governance"}'
```

That returns a `result` containing:

```json
{
  "grants": ["fabric:query.read", "fabric:docs.read", "fabric:ontology.write", "fabric:orchestrate.read",
             "fabric:orchestrate.write", "fabric:agent.run", "fabric:skill.write", "fabric:model.write"],
  "mode": "auto",
  "quota": { "org_window_max": 600, "org_window_used": 1, "project_window_max": 120, "project_window_used": 1 },
  "rules": ["..."],
  "scope": { "org_id": "org_c10", "project_id": "proj_c10", "role": "OWNER", "auth_kind": "api_key" }
}
```

It is the fastest answer to "why did that call fail", because it tells you what your credential
actually holds rather than what you meant it to hold.

## Four arguments are never yours to send

`project_id`, `projectID`, `organization_id` and `executed_by` are filled in from your credential's
scope. Both SDKs refuse them before a request is built.

## Every tool exists on every deployment

Sixty-two tools exist, and every gateway registers all of them. `GET /v1/connection-info` lists them in
a `tools` array. A tool whose backing service the deployment has not configured still has its route,
and a call to it fails with an error naming what is missing —
`<tool> is not configured (no data-service URL)`, `(no harness URL)` or
`(needs the harness and data-service URLs)`.

A path that names no tool is not a special error: the route simply is not there, and you get an
ordinary JSON 404 — `{"error":"not_found","error_description":"no route for POST /v1/<tool>"}` — with
`content-type: application/json` like every other response. Both SDKs map that to `NotFound`.

Per-tool arguments, returns and errors live in the [tool reference](/docs/tools/overview), whose
arguments are checked against the gateway's own registration and whose responses are captured from
real calls.

## The non-tool routes

| Route | What it is for |
|---|---|
| `GET /healthz` | Liveness. Unauthenticated. |
| `GET /v1/connection-info` | Discovery: the API base URL, the issuer, the registered tool list. Unauthenticated. |
| `GET /v1/whoami` | Who your credential says you are — user, role, organization, department, project, grants. |
| `POST /v1/apikeys`, `GET /v1/apikeys`, `DELETE /v1/apikeys/{id}` | Credential management. Requires a user token, not an API key. |
| `GET /v1/approvals`, `POST /v1/approvals/{id}/approve`, `POST /v1/approvals/{id}/reject` | The approval queue and its decisions. |
| `GET /v1/lifecycles/{id}` | The stage-by-stage record of one governed call. |
| `GET /v1/audit`, `GET /v1/audit/verify` | The audit record and its hash-chain verification. |
| `GET /v1/policy-decisions` | What the policy engine ruled, and when. |
| `GET` and `PUT /v1/projects/{projectId}/mode` | Read or change a project's mode. |
| `GET /v1/catalog/*` | The data catalog: assets, lineage, scorecards, connector status, sync statistics. |
| `/v1/codesync/*` | Repository registration, ingestion and job status for code sync. |
| `POST /v1/agents/{agentflowId}/chat/completions` | An OpenAI-compatible proxy onto a promoted agent version. |
| `/oauth/*` and `/.well-known/*` | OAuth 2.1 registration, authorization, token, and the two discovery documents. |

## Discovery before configuration

The only address you should hard-code is your workspace URL. `GET /v1/connection-info` is
unauthenticated and tells you the rest, so the gateway decides its own address and moving it needs
no client change:

```bash
curl -s "$WEXA_WORKSPACE/v1/connection-info"
```

```json
{
  "api": { "base_url": "http://localhost:7123/v1" },
  "issuer": "http://localhost:7123",
  "workspace_header": "X-Wexa-Workspace",
  "tools": ["query-context", "create-ontology", "save-context", "docs", "search-code", "fetch-code"]
}
```

Both SDKs do exactly this on your behalf before their first call.

## Related

  <Card title="Authentication" href="/docs/surfaces/rest/authentication">
    The four credential types and when each is the right one.
  </Card>
  <Card title="Errors and retries" href="/docs/surfaces/rest/errors-and-retries">
    Status codes, error bodies, and what is worth retrying.
  </Card>
  <Card title="Tool reference" href="/docs/tools/overview">
    Every tool, its arguments and its returns.
  </Card>
  <Card title="Server-bound arguments" href="/docs/get-started/scope-and-server-bound-arguments">
    Why four argument names are never yours to send.
  </Card>
