> Source: https://wexa.ai/docs/get-started/quickstart/typescript

# Quickstart — TypeScript SDK

The TypeScript SDK is a typed client over the same tools the REST API exposes. It gives you typed
arguments, typed results and a distinct error class per failure, and it refuses some mistakes before
a request leaves your process.

## Before you start

You need Node 18 or newer, a Wexa project, and an administrator role on that project so you can
mint a credential.

## Step 1 — Install

**Install**

**TypeScript SDK**

```bash
npm install @wexa-fabric/sdk
```

The package ships both ESM and CommonJS builds with type declarations for each, so it works from
`import` and from `require` without a shim.

## Step 2 — Get a credential

The SDK does not mint credentials; you mint one once and hand it to the client. An API key is pinned
to exactly one project when it is created, and that scope never widens — which is why the client
takes no project argument anywhere.

`$FABRIC_USER_TOKEN` below is your own Wexa sign-in token, the session token the console holds
once you have signed in. The console mints keys from this same endpoint under
**Simple mode → Generate API key** if you would rather not handle it yourself.

```bash
curl -sS -X POST https://fabric.wexa.ai/v1/apikeys \
  -H "Authorization: Bearer $FABRIC_USER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"org_id":"org_...","dept_id":"dept_...","project_id":"proj_...","name":"quickstart"}'
```

The response carries the secret exactly once:

```json
{
  "id": "key_...",
  "name": "quickstart",
  "note": "store this secret now; it is not retrievable again",
  "secret": "fab_sk_..."
}
```

Minting a key requires an owner, organization-administrator or project-administrator role; any other
role is refused with `API key creation requires an admin role on this project`.

## Step 3 — Construct a client

The client needs the workspace and the credential. Pass them explicitly, or leave the options out
entirely and let it read `WEXA_WORKSPACE` and `WEXA_API_KEY` from the environment.

```bash
export WEXA_WORKSPACE="https://fabric.wexa.ai"
export WEXA_API_KEY="fab_sk_..."
```

```typescript
import { Fabric } from '@wexa-fabric/sdk'

const fabric = new Fabric()
```

## Step 4 — Make your first call

The documentation tool is the best first call: it is registered in every deployment, needs no data in
your project, and returns your own scope and limits, so a successful response proves the credential
and the scope together.

```typescript
const governance = await fabric.docs({ topic: 'governance' })

console.log(governance.mode)
console.log(JSON.stringify(governance.quota))
```

```text
auto
{"org_window_max":600,"org_window_used":4,"project_window_max":120,"project_window_used":4}
```

The SDK returns the `result` of the envelope directly, so you get the tool's own output rather than a
wrapper to unpack. `mode` is the project mode — `auto` is Simple mode — and `quota` is your rolling
call budget at both the project and the organization level.

## Step 5 — Write and read the context graph

`saveContext` writes nodes. Each node needs a label, a merge key naming the property that identifies
it, and the properties themselves.

```typescript
const written = await fabric.saveContext({
  nodes: [
    {
      label: 'Customer',
      key: 'name',
      properties: { name: 'Acme Corp', tier: 'enterprise' },
    },
  ],
})

console.log(JSON.stringify(written))
```

```text
{"nodes_merged":1,"relationships_merged":0,"ontology_extended":false}
```

`ontology_extended` reports whether writing that node taught the project a type it did not have. The
first time a `Customer` is written it is `true` and `new_labels` names it; afterwards it is `false`,
because there was nothing new to register.

Reading takes read-only Cypher, and the query must filter on the project:

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

console.log(rows.row_count)
```

## Step 6 — See a refusal

`$project_id` above is bound by Wexa from your credential. Try to supply it yourself and the SDK
stops the call at the call site, before any request is built:

```typescript
await fabric.queryContext({
  query: 'MATCH (n) WHERE n.project_id = $project_id RETURN n',
  project_id: 'someone_elses',
})
```

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

This is a run-time refusal rather than a compile-time one: the argument is rejected when the call
runs, not by the type checker. That is deliberate — the same check has to hold for arguments
assembled at run time, which no type system sees. The four arguments treated this way are
`project_id`, `projectID`, `organization_id` and `executed_by`.

## Step 7 — Release the client

The client holds an abort controller so that an in-flight request and any approval poll can be
cancelled together. In a long-lived process, close it when you are done:

```typescript
fabric.close()
```

Calling it twice is a no-op, and the client cannot be reused afterwards.

## Where to go next

**Before you start — Scope and server-bound arguments**
Why four arguments are never yours to send, and what the platform does with them.

**Reference — All tools**
Every tool, with the TypeScript method name beside the three other surfaces.

**Release notes — Changelog**
The version of the package you just installed.

**Concepts — Where your data goes**
The four stores a project holds, and which one `saveContext` writes to.
