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

# Quickstart — Python SDK

The Python SDK is a thin, dependency-free client over the same tools the REST API exposes. It adds
three things worth having: it discovers the gateway's base URL for you, it raises a typed exception
per failure class instead of handing you a status code, and it refuses some mistakes before a
request leaves your process.

## Before you start

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

## Step 1 — Install

**Install**

**Python SDK**

```bash
pip install wexa
```

The package is `wexa` and the module is a single file, with no third-party dependencies.

## Step 2 — Get a credential

The SDK does not mint credentials; you mint one once and give 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 two things: the workspace it is talking to, and the credential. Pass them
explicitly, or leave both out 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_..."
```

```python
from wexa import Fabric

fabric = Fabric()

print(fabric.base)
```

```text
https://fabric.wexa.ai/v1
```

Constructing the client performs one unauthenticated request to the gateway's connection-info
endpoint to discover that base URL, so a client that constructs successfully has already proved the
workspace is reachable.

## 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.

```python
governance = fabric.docs(topic="governance")

print(governance["mode"])
print(governance["quota"])
```

```text
auto
{'org_window_max': 600, 'org_window_used': 1, 'project_window_max': 120, 'project_window_used': 1}
```

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

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

```python
result = fabric.save_context(
    nodes=[
        {
            "label": "Customer",
            "key": "name",
            "properties": {"name": "Acme Corp", "tier": "enterprise"},
        }
    ]
)

print(result)
```

```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:

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

print(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:

```python
fabric.query_context(
    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 type error: the argument is accepted by the signature and
rejected when the call runs. 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`.

## 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 Python 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 `save_context` writes to.
