> Source: https://wexa.ai/docs/surfaces/typescript/usage

# Using the TypeScript SDK

Everything the SDK does falls into three groups: **tool calls**, which all share one shape; the
handful of **non-tool methods** that are spelled out explicitly because they are not tools; and the
**options** that govern retry, approval and cancellation.

## Calling a tool

Every tool has a typed method. Field names in the payload are the gateway's own wire names,
verbatim — there is no case conversion, because the gateway's own format is mixed.

```ts
const rows = await fabric.queryContext({
  query: 'MATCH (c:Customer) WHERE c.project_id = $project_id RETURN c.name AS name LIMIT 5',
})
```

Every result is the uniform envelope, with the correlation ids attached to the object rather than
buried in it:

```ts
console.log(rows)              // { columns: …, rows: …, row_count: 0, stats: {…}, truncated: false }
console.log(rows.lifecycleId)  // qlc_000013
console.log(rows.traceId)      // 73d1ab052c9e3371ae3b38098c4b34ac
```

`$project_id` is bound for you from the credential. Never pass it yourself — see
[below](#server-bound-arguments).

### Or call by name

`call()` takes the tool's name, and accepts either spelling. These are the same call:

```ts
await fabric.call('run_process_flow', { goal: 'reconcile' })
await fabric.call('run-process-flow', { goal: 'reconcile' })
```

An unrecognised name is sent to the wire verbatim rather than rejected, so a tool the gateway gained
after your SDK was published is still reachable without an upgrade.

### The same call on every surface

**REST API**

```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"}'
```

**TypeScript SDK**

```ts
const rows = await fabric.queryContext({
  query: 'MATCH (c:Customer) WHERE c.project_id = $project_id RETURN c.name AS name LIMIT 5',
})
```

**Python SDK**

```python
rows = fabric.query_context(
    query="MATCH (c:Customer) WHERE c.project_id = $project_id RETURN c.name AS name LIMIT 5"
)
```

All three produced the identical envelope against a live gateway. Per-tool arguments are in the
[tool reference](/docs/tools/overview).

The SDK exports every tool as a method, named as the tool's page shows it.

## Server-bound arguments

Four argument names are never yours to send: `project_id`, `projectID`, `organization_id` and
`executed_by`. The platform fills them in from your credential's scope.

The SDK refuses them **client-side**, before the request is built:

```ts
await fabric.queryContext({ query: 'RETURN 1', project_id: 'proj_other', organization_id: 'o' })
```

```text
ValidationError: ['organization_id', 'project_id'] are bound server-side from your token scope; sending them is rejected
```

The reason it is refused rather than quietly dropped is that **the gateway does not refuse it for
you**. Sending a body `project_id` of another project alongside a valid `query-context` request
returned `HTTP 200` and a normal envelope when it was tried against a live gateway: the handler
reads only `query` and `parameters`, and binds `$project_id` from your credential's scope. Your
value was neither honoured nor rejected — it was ignored, and the call succeeded against the scope
you were always pinned to rather than the one you asked for.

That is the failure the client-side guard exists to prevent, and it is the worse of the two possible
ones: a refusal is visible, a silently ignored argument is not. For a REST caller with no SDK, this
guard is the only thing standing between you and that outcome.

The Python SDK applies the same guard over the same four names. See
[server-bound arguments](/docs/get-started/scope-and-server-bound-arguments) for the concept.

## The non-tool methods

Five methods are not tools and are spelled out explicitly, because they map onto conventional REST
routes rather than the `POST /v1/<tool>` convention.

| Method | Route | Returns |
|---|---|---|
| `whoami()` | `GET /v1/whoami` | Your user id, role, organization, department, project and grants. |
| `approvals(status?)` | `GET /v1/approvals` | Pending or decided approvals. `status` is URL-encoded for you. |
| `approve(approvalId)` | `POST /v1/approvals/{id}/approve` | The decision result. |
| `reject(approvalId, reason)` | `POST /v1/approvals/{id}/reject` | The decision result. `reason` is required; without one the call throws `ValidationError` before any request. |
| `lifecycle(lifecycleId)` | `GET /v1/lifecycles/{id}` | The stage-by-stage record of one governed call. |

```ts
const pending = await fabric.approvals('pending')
for (const a of pending) console.log(a.id, a.tool, a.what, a.why, a.expires_at)
await fabric.approve(pending[0].id)

const record = await fabric.lifecycle(rows.lifecycleId)
```

There is a sixth explicit method, `resume(tool, resumeToken, options?)`, which exists because a
typed payload cannot simply be omitted for tools with required fields. It is covered on
[error handling](/docs/surfaces/typescript/error-handling) alongside the rest of the approval path.

## Call options

Every tool method takes a second argument.

| Option | Default | Effect |
|---|---|---|
| `retry` | `false` | Retry `UpstreamError` and `QuotaExceeded`, up to the client's `retries`. |
| `waitForApproval` | `false` | Block and poll until a person decides, then resume automatically. |
| `approvalTimeout` | `600` | Seconds to wait for that decision. |
| `poll` | `5` | Seconds between approval checks. |
| `resumeToken` | — | Resume an approved call. Takes no payload. |
| `timeout` | client's `70` | Seconds, for this call only. |
| `signal` | — | An `AbortSignal` that cancels this call and any approval poll it is waiting on. |

```ts
await fabric.queryContext({ query }, { retry: true })              // safe: it is a read
await fabric.saveContext({ nodes }, { waitForApproval: true })     // blocks on a person
```

`retry` is off by default on purpose: the gateway has no idempotency key, so a retried `runAgent`
starts a second real run.

`waitForApproval` ends on the first final status of the approval. `approved` resumes the call. `rejected`,
`expired`, `overdue` and `consumed` throw `ApprovalError` with the status in the message (`approval rejected`)
and the approval in `raw`. An `overdue` approval can still be approved in the console; to keep waiting for it,
catch `ApprovalRequired`, keep the `resumeToken` and resume later.

### Consequential writes wait by default

**Breaking change.** Every project now holds consequential writes made with an API key or an SDK session until a
person approves them, admin API keys included. Table row inserts and updates (`insertTableRows`,
`updateTableRow`), table create and rename (`createTable`, `renameTable`), trigger changes (`setTriggers`)
and connector actions (`runConnectorAction`) are consequential now, next to the tools that already were, such as
`deleteTableRows` and `saveContext`. They throw `ApprovalRequired` unless the gate is off. A project admin
turns the gate off, or back on, in the project's settings. Actions taken by a person in the console are not held.

## Cancellation

```ts
const controller = new AbortController()
setTimeout(() => controller.abort(), 2_000)
await fabric.queryContext({ query }, { signal: controller.signal })
```

A caller-triggered abort rejects with the standard `AbortError`, **not** with a Wexa error class —
an abort is your own action rather than a failure of the call. A timeout, by contrast, is wrapped as
a `TransportError`, so the two stay distinguishable in one handler.

`close()` aborts everything in flight at once, which is what you want on shutdown.

## A typed result

`Result<T>` is the tool payload with `lifecycleId` and `traceId` attached. Pass a type argument when
you know the shape:

```ts
type Row = { name: string }
const rows = await fabric.queryContext<{ rows: Row[]; row_count: number }>({ query })
```

Payload types are exported with the methods — `QueryContextPayload`, `SaveContextPayload`,
`CreateOntologyPayload` and the rest — as are `WhoAmI`, `Approval`, `FabricOptions`, `CallOptions`
and `ToolCallOptions`.

## Related

  <Card title="Error handling" href="/docs/surfaces/typescript/error-handling">
    The full hierarchy, and plain retry versus resume retry.
  </Card>
  <Card title="Install" href="/docs/surfaces/typescript/install">
    Package, versions, and constructing a client.
  </Card>
  <Card title="Tool reference" href="/docs/tools/overview">
    Arguments and returns for every tool.
  </Card>
