authoxi — Python SDK
The official Python client for authoxi: authorization for AI agents. An agent asks before it acts, and every answer — allow, deny, or "a human must sign this" — is a signed, offline-verifiable record.
pip install authoxi
The free half: make your agent verifiable, with no account
Everything in this section works with no authoxi account, no key from us, and no network
call to us — and keeps working if you never become a customer. It is authoxi.agentsig,
the same code the Agent Trust Grader scores strangers with and
the same code our own control plane verifies with.
The problem it solves is not that signing is hard. It is that four pieces — a key, a published directory, a request signature, and a record — have to line up exactly, and getting one of them subtly wrong fails silently: your signatures verify locally and nobody else can check them.
python -m authoxi.agentsig keygen # an identity. Store the seed; it IS your agent.
from authoxi.agentsig import AgentSigner
signer = AgentSigner.from_env() # reads AUTHOXI_AGENT_KEY
# 1. Publish this at /.well-known/http-message-signatures-directory
signer.directory()
# 2. Sign outbound requests — to anyone, not to us. An origin, CDN or API that
# speaks Web Bot Auth can now tell you apart from a scraper.
headers = signer.sign_request("GET", "https://api.partner.com/inventory")
httpx.get("https://api.partner.com/inventory", headers=headers)
# 3. Emit an audit record anyone can check later, offline, with no registry:
record = signer.sign_record({"tool": "search", "args_hash": "…"})
Verify anything, from anywhere, with no key:
echo "$RECORD" | python -m authoxi.agentsig verify # exits non-zero on a forgery
A record signed this way says "witness": "self" on its face, and the CLI says so out loud.
That is deliberate: "I signed my own log" and "an independent party countersigned it" are
the difference between a record and a proof, and a format that let them look alike would be
doing its readers real harm.
What paying adds is the part a library cannot do for you: a decision (may this agent do this, right now?), a witnessed record instead of a self-attested one, and someone else holding a copy of the evidence when yours is the thing in question. That is the rest of this README.
Quickstart
In your agent, one line does the work:
import authoxi
@authoxi.runtime().guard("payment", amount=lambda inv: inv["amount"])
def pay_invoice(inv):
return psp.charge(inv) # runs only if authoxi allows it
You write no if. On deny the body never executes and Denied is raised. On escalate
the body never executes either — by default the call blocks until a human signs (up to
timeout=, 120s), which is usually what a background worker wants. Pass wait=False to get
Escalated raised immediately and handle the resumption yourself:
@authoxi.runtime().guard("payment", amount=lambda inv: inv["amount"], wait=False)
def pay_invoice(inv):
return psp.charge(inv)
A callable amount is applied to the same arguments the function receives, so the amount you
authorized and the amount you charged stay one expression rather than two that drift apart.
That is the whole point: enforcement is structural, not something you remember to check.
authoxi.runtime() takes no arguments because an agent's URL, id, key and mandate are facts
about a deployment, not the program — set AUTHOXI_BASE_URL, AUTHOXI_AGENT_ID,
AUTHOXI_AGENT_KEY and AUTHOXI_MANDATE_ID. The key is a stored hex seed, so a booting agent
never imports a cryptography library to rebuild it.
The same object is also a block, when a decorator doesn't fit:
with authoxi.runtime().guard("payment", "250.00"):
psp.charge(invoice)
Issuing the authority (your console, not your agent)
The agent above can only ask. Creating agents and issuing mandates needs the tenant secret key, and lives on your server behind a human:
from authoxi import AgentControl
cp = AgentControl.from_url("https://api.authoxi.com", secret_key="sk_live_...")
agent = cp.create_agent("procurement-bot")
wallet = agent.issue_mandate(budget="5000.00", escalate_over="1000.00", actions=["payment"])
create_agent generates an Ed25519 keypair locally and registers only
the public half — authoxi never sees the private key, so it cannot impersonate your agent.
The mandate is signed: budget is the hard ceiling, escalate_over is where the agent stops
being autonomous, and actions is exhaustive (anything not listed is denied). Money is always a
decimal string, never a float.
When a human needs to sign off
Over the escalate_over threshold the action does not happen — authorize returns
escalate. authorize_and_wait blocks until a human decides, so there's no polling loop:
verdict = wallet.authorize_and_wait("payment", "2500.00") # over the threshold
if verdict.get("approved"):
... # a human signed for this; the signature is what makes it non-repudiable
Guard your own code
guard() wraps a call you already have, so it runs only if authoxi allows it. No proxy, no
sidecar — authoxi sees the decision, never your payload or your credential:
with wallet.guard("payment", "2500.00"):
stripe.PaymentIntent.create(...) # your key, your call — raises Denied if refused
Driving a fleet
The handles above are sugar over AgentControl, which names everything explicitly — the right
surface for an operator console governing many agents:
cp.authorize(agent_id=..., mandate_id=..., action="payment", amount="250.00")
cp.list_agents(); cp.list_pending(); cp.revoke_agent(agent_id)
Bring your own key (an HSM, an existing identity) with cp.register_agent(public_key=..., ...)
instead of create_agent.
Verify a decision offline
Anyone can check a decision record with no account, no key, and no call back to us:
from authoxi import verify_event
result = verify_event(event)
assert result.ok
Which client goes where
This matters more than it looks. AgentControl holds the tenant secret key, which can issue
authority — create agents, mint mandates, grant keyrings, raise budgets. Put it inside the agent
it governs and your policy is a suggestion: the agent can write itself an unbounded mandate with
no approver and authorize against it, and every signature will verify.
So ship AgentRuntime in the agent. It authenticates with the agent's own passport (proof of a
private key authoxi has never seen) and has no method that can issue anything — the most it
can do is ask:
from authoxi import AgentRuntime
rt = AgentRuntime.from_url(
"https://api.authoxi.com",
agent_id="agt_...", private_key=priv, # handed to it by your console
mandate_id="mnd_...",
)
with rt.guard("payment", "2500.00"):
stripe.PaymentIntent.create(...) # runs only if authorized; blocks for a human if needed
| Class | Credential | Where it belongs |
|---|---|---|
AgentRuntime |
the agent's own passport | inside the agent — can ask, can never issue |
AgentControl |
tenant secret key (sk_live_) |
server-side, behind a human: issuance + administration |
AuthoxiClient |
publishable key | humans — signup / signin / OTP / OAuth |
Both accept a URL (AgentControl.from_url(...), AuthoxiClient(base_url=...)), or you can inject
any httpx-style client (handy for tests: pass a FastAPI TestClient straight in).
Errors are typed — NotFound, Forbidden, Conflict, Unauthorized, InvalidRequest, plus the
domain events BudgetExceeded, Denied, Escalated — each carrying .code and .status_code.
- Docs: https://authoxi.com
- API: https://api.authoxi.com
- Source: https://github.com/aeternm/authoxi/tree/main/sdk/python
- JS SDK (browser):
@authoxi/js
License
MIT — see LICENSE. The SDK is MIT so you can embed it freely; the authoxi service itself is proprietary.
Metadata
Release files for authoxi 0.7.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| authoxi-0.7.0.tar.gz | 120.1 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| authoxi-0.7.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 257.6 kB
Release files / authoxi-0.7.0.tar.gz
| Download URL | authoxi-0.7.0.tar.gz |
|---|---|
| Size | 120.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a5caaa5bb9cc0ff69ca3016347de9b1c9dc64cb14512012bba6ca8dbd7a7b567
|
|
BLAKE2b-256 checksum How to use checksums |
3e5b00f6467691bc24a413431ce6bd03f636ee3003172cf44a66d51754fd65a9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 2, 2026.
Transparency logRelease files / authoxi-0.7.0-py3-none-any.whl
| Download URL | authoxi-0.7.0-py3-none-any.whl |
|---|---|
| Size | 137.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0012b846f897086ea10272025d6693a4beaf2b043e1cc6f0b1e69dfa9a2d55da
|
|
BLAKE2b-256 checksum How to use checksums |
c9a7b6571e2b45e9c07c34e2866f29a6bd3c3136a57870cdf8d15ff44c09778c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 2, 2026.
Transparency logSecurity for authoxi 0.7.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for authoxi-0.7.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 2, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.