StateJar docs

Python SDK

The statejar package on PyPI, an HTTP client for StateJar's memory API. It stores what a message says, returns only the memory a question needs as the system message for your own model, and asks before it stores an unclear amount. Your model key never reaches it.

What it does

StateJar never calls a model for you. The package is an HTTP client and nothing else, its one dependency is httpx, and your model key stays in your code. It also carries the statejar command, which signs a terminal in.

Install

pip install statejar

You need Python 3.10 or later. PyPI serves version 0.6.0 (checked on 27 Oct 2026). With pip install "statejar[keyring]", the key statejar login stores goes to your system's keychain when it has one, instead of a file.

Set it up

Sign in once with statejar login (the statejar command). On a server or in CI, put a key from the API keys page in STATEJAR_API_KEY instead.

ArgumentDefaultWhat it does
api_keySTATEJAR_API_KEY, then the key statejar login storedThe developer key, sent as the X-API-Key header.
session_tagdefaultThe conversation. A new tag's first message builds on your newest memory in any tag.
base_urlSTATEJAR_BASE_URL, then https://api.statejar.com/api/v1The API, for example staging or a server you run.
timeout60Seconds a request may take.

StateJar is also a context manager: with StateJar(session_tag="user-42") as sj: closes its connections at the end.

Check it works

The first-turn example from getting started:

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

It prints a handle, then the facts your model would get. The run below used the key statejar login stored:

The first-turn example run with python: a handle, then memory_context with the name, city, contact preference and budget
A first turn with the Python SDK. The first-turn example with the key statejar login stored, on statejar 0.7.0 as built from StateJar's repository (PyPI's 0.6.0 has the same calls): two messages in, one question out, then the handle and the memory_context your model gets, the whole state since it is small. The account already held these facts from the terminal, so nothing new is stored and the handle is the one statejar add printed. Recorded 27 Oct 2026 against a local StateJar server; the output is real, with pauses over 3 s cut to 3 s. Full sizeThe recording (.cast)

The audit log then lists the disclosure, with the handle and the fields it sent.

Confirm before store

An amount with no claim attached is asked about, not stored. The question comes back in confirmations, and only your answer writes a version:

from statejar import StateJar

sj = StateJar(session_tag="user-42")

# money without a claim ("i have 5000 rs") is not stored: it is asked about
got = sj.ingest("go goa i have 5000rs ruppes")
for c in got["confirmations"]:
    print(c["question"])                     # Is ₹5,000 your budget?
    sj.confirm(c["id"], accept=True)         # or accept=False: nothing is stored

print(sj.recall("what is my budget")["subset"])

It prints the question StateJar asked, answers yes, and recalls: the budget is now stored, beside the destination the same message gave.

The confirm example run with python: the question "Is ₹5,000 your budget?", then the recalled state with the destination Goa and the budget of 5000 INR
Confirm before store, from Python. The confirm-before-store example: an amount with no claim attached is asked about, not stored; the yes writes it as a new version, beside the destination the same message gave. Recorded 27 Oct 2026 against a local StateJar server; the output is real, with pauses over 3 s cut to 3 s. Full sizeThe recording (.cast)

The audit trail

Every disclosure is in your audit log: which handle, which fields, which model, never the message. The same rows come back over the API:

import httpx, os
from statejar import StateJar

sj = StateJar(session_tag="user-42")
sj.ingest("I'm Meera from Pune, I prefer email, and my budget is ₹3,500.")
sj.recall("What is my budget?", audit=True)    # audit=True writes the disclosure

# every disclosure: which handle, which fields, which model — never the text
base = os.environ.get("STATEJAR_BASE_URL", "https://api.statejar.com/api/v1").rstrip("/")
rows = httpx.get(f"{base}/audit?limit=3", headers={"X-API-Key": sj.api_key}).json()
for row in rows["entries"]:
    print(row["kind"], row["handle_used"], row["subset_keys"])

The newest three disclosures, each with its kind, its handle and the fields it sent. The middle row is the confirm example's recall, which did not ask for an audit: a plain recall is logged too, as query.

The audit example run with python: three rows, query_audited, query and query_audited, each with its handle and the fields it sent
The audit trail, from Python. The audit example: the newest three disclosures, each with its kind, the handle it came from and the fields it sent. The message itself is never in the log. Recorded 27 Oct 2026 against a local StateJar server; the output is real, with pauses over 3 s cut to 3 s. Full sizeThe recording (.cast)

What it sends

Troubleshooting

This page as Markdown: /docs/python-sdk.md