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

# Quickstart — REST API

The REST API is the surface with nothing to install. If you can make an HTTP request, you can call
every Wexa tool. This page takes you from nothing to one successful call and one deliberate
failure, so that you know what both look like.

## Before you start

You need a Wexa project and an administrator role on it. Everything else is on this page.

## Step 1 — Get a credential

Every call is authenticated with an API key, and every key is pinned to exactly one project at the
moment it is minted. The key carries your organization, department, project, user and role, and
that scope never widens — which is why you never pass a project to a tool.

Keys are minted with your own Wexa sign-in token — the session token the console holds once you
have signed in, which is what `$FABRIC_USER_TOKEN` stands for below. The token identifies you; the
request body says which project the key is for. If you would rather not handle it yourself, the
console mints keys from the same endpoint under **Simple mode → Generate API key**.

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

```json
{
  "id": "key_...",
  "name": "quickstart",
  "note": "store this secret now; it is not retrievable again",
  "scope": {
    "org_id": "org_...",
    "dept_id": "dept_...",
    "project_id": "proj_...",
    "user_id": "user_...",
    "role": "OWNER",
    "grants": ["fabric:query.read", "fabric:docs.read", "fabric:ontology.write"],
    "auth_kind": "api_key"
  },
  "secret": "fab_sk_..."
}
```

The `note` is not decoration. The secret is returned once and is stored only as a hash, so a key you
lose is a key you replace. Put it somewhere your shell can reach it:

```bash
export WEXA_API_KEY="fab_sk_..."
```

## Step 2 — Confirm the credential works

Before calling a tool, ask Wexa who it thinks you are. This is a plain `GET` and it takes no
arguments, so a failure here is unambiguously about the credential.

```bash
curl -sS https://fabric.wexa.ai/v1/whoami \
  -H "Authorization: Bearer $WEXA_API_KEY"
```

```json
{
  "org_id": "org_...",
  "dept_id": "dept_...",
  "project_id": "proj_...",
  "user_id": "user_...",
  "role": "OWNER",
  "grants": ["fabric:query.read", "fabric:docs.read", "fabric:ontology.write"]
}
```

That response is the scope every later call runs in. Nothing you send can change it.

## Step 3 — Make your first tool call

Every tool is `POST /v1/<tool-name>`, in kebab-case, with a JSON body of the tool's arguments. The
documentation tool is the best first call: it is registered in every deployment, takes no data, and
returns your own limits — so a successful response proves the credential, the scope and the quota
all at once.

```bash
curl -sS -X POST https://fabric.wexa.ai/v1/docs \
  -H "Authorization: Bearer $WEXA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"topic": "governance"}'
```

```json
{
  "lifecycle_id": "qlc_...",
  "result": {
    "mode": "auto",
    "quota": {
      "org_window_max": 600,
      "org_window_used": 7,
      "project_window_max": 120,
      "project_window_used": 7
    },
    "grants": ["fabric:query.read", "fabric:docs.read", "fabric:ontology.write"],
    "rules": ["query-context is read-only; queries MUST filter project_id ..."],
    "scope": { "project_id": "proj_...", "role": "OWNER", "auth_kind": "api_key" }
  }
}
```

That is a successful call. Every tool returns the same envelope: a `lifecycle_id` naming the
governance record for this call, and a `result` holding whatever the tool produced. Reading `result`
and ignoring the rest is fine; keeping the `lifecycle_id` is what lets you ask later why a call was
allowed, refused or held.

## Step 4 — Write something, then read it back

`save-context` writes nodes into the project's context graph. Each node needs a label, a merge key
naming which property identifies it, and the properties themselves.

```bash
curl -sS -X POST https://fabric.wexa.ai/v1/save-context \
  -H "Authorization: Bearer $WEXA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "nodes": [
      {
        "label": "Customer",
        "key": "name",
        "properties": { "name": "Acme Corp", "tier": "enterprise" }
      }
    ]
  }'
```

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

`ontology_extended` and `new_labels` are the interesting part. In a Simple-mode project the ontology
self-organizes: a label Wexa has not seen before is registered as you write it, rather than being
refused until someone declares it. Write the same node again and `ontology_extended` comes back
`false`, because there was nothing new to register.

Reading is `query-context`, which takes read-only Cypher:

```bash
curl -sS -X POST https://fabric.wexa.ai/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, c.tier AS tier",
    "limit": 10
  }'
```

## Step 5 — See a refusal

A quickstart that only shows success teaches you half of the surface. Two refusals are worth
provoking now, because you will meet both.

Leave out the project filter and the query is rejected before it touches any data:

```json
{
  "error": "S4:validate-input",
  "error_description": "cypher rejected: query must filter on project_id: use `project_id = $project_id` for this project, or `project_id IN $project_ids` to include projects you were granted",
  "lifecycle_id": "qlc_..."
}
```

Send no credential at all and you never reach the tool:

```json
{
  "error": "invalid_token",
  "error_description": "missing bearer credential"
}
```

A key that has been revoked, or simply mistyped, reports itself as such rather than as a permission
problem:

```json
{
  "error": "invalid_token",
  "error_description": "api key invalid or revoked"
}
```

## Where to go next

**Reference — All tools**
Every tool that is `POST /v1/<tool-name>`, with its arguments and its errors.

**Quickstart — Move to an SDK**
The same calls, with typed arguments and typed errors.
