Skip to main content

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.

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)

Source distribution for authoxi 0.7.0
File Size Uploaded Provenance
authoxi-0.7.0.tar.gz 120.1 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for authoxi 0.7.0
File Interpreter ABI Platform
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 log

Release 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 log

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

Select from 2 files

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 log

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

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page