# Getting started

> From a new account to a model that remembers, in seven steps — sign up, verify your address, make an API key, install the package, sign in from the terminal, remember and recall, and see your memory in the console. Each step has a recording of it running.

## The short answer

Make an account with your e-mail address, install `statejar`, run `statejar login` once, then call `ingest` to store what a message says and `recall` to get the memory a question needs. `recall` gives you `memory_context`, which you put in front of your own model as its system message. Your code calls your model, and its key never reaches StateJar.

Every recording on this page is a real run of StateJar on one machine, in one throwaway account: the site at `localhost:18173` and the API at `127.0.0.1:18100`, where statejar.com has `statejar.com` and `api.statejar.com`. Each caption says when it was recorded and how.

## 1. Sign up

Open the [sign-up page](/signup) and enter your e-mail address, then choose **Send me a code**. StateJar e-mails you a one-time code. Your account is made only when you type that code in, so every account starts from an address that was proved.

![The sign-up page: an address typed, Send me a code pressed, and the page saying a message is on its way](/media/casts/signup.webp)

If the sign-up page offers only a password, that server cannot send mail yet. Sign up with a password and go on to step 3.

## 2. Verify your e-mail

The message holds an 8-digit code that works for 10 minutes, once. Type it in and choose **Create my account**. You are signed in, and the account exists from this moment. The next page asks for a display name and the like, all optional.

![The message with the code in a test inbox, the code typed on the sign-up page, and the account made](/media/casts/verify-email.webp)

The recording reads the code from a test inbox on the same machine: the server sent the message over SMTP to a mail receiver there, not to the internet. Yours arrives in your own inbox. If it does not, look in the spam folder, or choose **Send a new code**.

## 3. Create an API key

A developer key is how your code calls StateJar from a server or CI. On [API keys](/api-keys), under *Developer API*, give the key a name, choose how long it lives (7 days, 30 days, 1 year or never), and choose **Generate key**. The key is shown once: copy it into your secret store, or into `STATEJAR_API_KEY`. StateJar keeps only its SHA-256 hash.

![The API keys page: a key named and generated, and the dialog that shows it once, the key blurred in the recording](/media/casts/api-key.webp)

On your own machine you do not have to copy a key: step 5 stores one for you.

## 4. Install the package

```bash
pip install statejar
```

The package is the Python SDK and the `statejar` command. For JavaScript, `npm install statejar` gives you the Node SDK (Node 20.3 or later).

![A new virtualenv, the statejar package installed, and statejar --version](/media/casts/install.svg)

## 5. Sign in from the terminal

```bash
statejar login
statejar whoami
```

`statejar login` prints a link with a code, opens it in your browser where it can, and waits. On that page, signed in, check the code and the device's name, and choose **Approve**. The terminal then receives a developer key for this machine. The key lasts a year, reaches only the memory API, and is stored in your keychain or in a file only you can read. Your password never goes through the terminal.

![statejar login printing the link and its code, the approval arriving, then statejar whoami naming the account and the key's last four characters](/media/casts/login.svg)

![The approval page for the code the terminal printed, Approve pressed, and the key's last four characters shown](/media/casts/device-approve.webp)

## 6. Remember and recall

`ingest` stores the facts a message states, as typed fields, never the message itself. `recall` returns the memory a question needs as `memory_context`. With no key in the code, the SDK uses the one `statejar login` stored.

```python
from statejar import StateJar

# the key comes from `statejar login` or STATEJAR_API_KEY; one tag per conversation
sj = StateJar(session_tag="user-42")

sj.ingest("I'm Meera from Pune and I prefer email.")    # typed facts, no transcript
sj.ingest("My budget is ₹3,500.")

hit = sj.recall("What is my budget?", audit=True)       # what the question needs
print(hit["handle_used"])        # shm_… — a SHA-256 of the canonical state
print(hit["memory_context"])     # the system message for YOUR model, nothing else
```

![The first-turn example run with python: the handle, then memory_context with the name, city, contact preference and budget](/media/casts/python-first-turn.svg)

The same in JavaScript, with the key in `STATEJAR_API_KEY`:

```js
import { StateJarClient } from "statejar";

const sj = new StateJarClient({
  baseUrl: process.env.STATEJAR_BASE_URL ?? "https://api.statejar.com/api/v1",
  // apiKey: STATEJAR_API_KEY is read automatically in Node (never in a browser)
});

const tag = "user-42";                     // one tag per conversation
await sj.ingest({ sessionTag: tag, text: "I'm Meera from Pune and I prefer email." });
await sj.ingest({ sessionTag: tag, text: "My budget is ₹3,500." });

const query = "What is my budget?";
const hit = await sj.recall({ sessionTag: tag, query, audit: true });
console.log(hit.handle_used);       // shm_…
console.log(hit.memory_context);    // the system message for your model
```

![The same example in JavaScript, after npm install: the same handle and the same memory_context](/media/casts/node-first-turn.svg)

The handle (`shm_…`) is the SHA-256 of the stored facts, so the same facts give the same handle on any machine. That is why the Python and JavaScript runs end on the same handle. To put `memory_context` in front of Claude, OpenAI, Gemini or a model on your own machine, see [any LLM provider](/docs/any-llm.html).

## 7. See your memory in the console

The [Dashboard](/dashboard) counts your sessions, versions and disclosures, and lists the newest handles with the scope each came from. The [Audit log](/audit) lists every disclosure with its handle and the fields it sent. **Replay** on an entry re-derives the handle from the stored state and shows the exact facts the model was given.

![The dashboard after the steps above, its newest handles, then the audit log with the newest disclosure replayed: byte-identical, and the facts it sent](/media/casts/dashboard.webp)

## Questions

- **There is no browser on this machine (a server, SSH, CI).** Run `statejar login --no-browser` and open the link it prints on any device where you are signed in. Or set `STATEJAR_API_KEY` to a key from step 3.
- **Can I remember and recall from the terminal alone?** Yes, with `statejar add` and `statejar search` in statejar 0.7.0, which is not on PyPI yet; [the statejar command](/docs/cli.html) shows them running.
- **I use an assistant or an agent, not my own code.** Use the plugin for [Claude Code](/docs/claude-code.html), [Codex](/docs/codex.html), [OpenCode](/docs/opencode.html), [OpenClaw](/docs/openclaw.html) or [Hermes Agent](/docs/hermes.html), or add [the MCP server](/docs/mcp.html) to any client that speaks MCP.
- **`401 invalid or missing API key`.** The key is not set, was revoked, or belongs to another server (staging and production keys differ). `statejar whoami` says which account a stored key belongs to; `STATEJAR_API_KEY` wins over the stored key.
- **`401 API key expired`.** Keys from `statejar login` last a year, and keys from the API keys page as long as you chose. Run `statejar login` again or make a new key.
- **`stored: false, handle: null`.** Not an error: the message held no fact to store, such as a greeting or a question. `memory_context` still comes back when earlier versions exist.
- **`confirmations: [...]`.** An amount with no claim attached ("i have 5000 rs") is asked about, not stored. Call `confirm(id, accept)`, or ignore it: nothing is written until you do ([the Python SDK](/docs/python-sdk.html) shows it running).
- **`429 Too Many Requests`.** Your plan's limit, or too many requests a minute from one key. Wait for the window ([what each limit counts](/docs/pricing-and-payments.html)).
- **`StateJarConnectionError`.** The request never reached StateJar: DNS, a proxy, or `STATEJAR_BASE_URL` pointing at something that is not an API root ending in `/api/v1`.
