Metadata-Version: 2.5
Name: aura-x402
Version: 0.1.0
Summary: Give an autonomous agent a real email address, a webhook URL, durable memory, and the ability to wait for an event without burning tokens. Paid per call in USDC over x402.
Project-URL: Homepage, https://aura.rohnelt.dev
Project-URL: Documentation, https://aura.rohnelt.dev/llms-full.txt
Project-URL: Repository, https://github.com/phillypmack/aura-agent
Project-URL: Issues, https://github.com/phillypmack/aura-agent/issues
License: MIT
License-File: LICENSE
Keywords: agent-email,agent-memory,ai-agent,autonomous-agent,crewai,durable-execution,langchain,mcp,verification-code,x402
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.27
Provides-Extra: all
Requires-Dist: crewai>=0.80; extra == 'all'
Requires-Dist: eth-account>=0.11; extra == 'all'
Requires-Dist: langchain-core>=0.3; extra == 'all'
Provides-Extra: crewai
Requires-Dist: crewai>=0.80; extra == 'crewai'
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.3; extra == 'langchain'
Provides-Extra: wallet
Requires-Dist: eth-account>=0.11; extra == 'wallet'
Description-Content-Type: text/markdown

# aura-x402

**Give an autonomous agent a real email address, a webhook URL, durable memory, and the ability to wait for something without burning tokens.**

```bash
pip install aura-x402
```

> The import name is `aura_agent`. `aura-agent` on PyPI collides with an unrelated
> project, so the distribution is `aura-x402` while the module keeps the name the
> npm package uses.

No account. No API key form. No human. An agent proves it controls a wallet with one signature, and is billed per call against a prepaid USDC balance on Base.

- Docs for humans: <https://aura.rohnelt.dev>
- Docs for models: <https://aura.rohnelt.dev/llms-full.txt>
- MCP endpoint: `https://aura.rohnelt.dev/mcp`

---

## The three things an ephemeral agent cannot do

| Blocker | What normally happens | With Aura |
| --- | --- | --- |
| **It has no address.** | It cannot sign up for anything, because it cannot receive the verification code. | `client.await_email_code()` |
| **It cannot wait.** | It polls in a loop, burning tokens on every turn, or it dies and loses the work. | One blocking call. Zero tokens while waiting. |
| **It dies at the end of a run.** | Everything it learned is gone. | Durable memory keyed to the wallet. |

## Quickstart

```python
from aura_agent import Aura, LocalSigner

# Free. Rotates the token if this wallet already has an identity, so it is
# safe to run on every boot.
client = Aura.claim(LocalSigner(private_key))

me = client.whoami()
print(me["email"])         # ripe-harbor-91f2@agents.aura.rohnelt.dev  <- a real mailbox
print(me["webhook_base"])  # https://aura.rohnelt.dev/hook/ripe-harbor-91f2
print(client.token)        # aura_sk_...  <- persist this
```

`LocalSigner` needs the `wallet` extra:

```bash
pip install "aura-x402[wallet]"
```

Already have a token:

```python
client = Aura(token=os.environ["AURA_TOKEN"])
```

`AURA_TOKEN` and `AURA_URL` are read from the environment when not passed.

## Sign up for a third-party service, unattended

The whole point. The agent registers somewhere, blocks on the confirmation mail, and continues.

```python
sign_up_somewhere(email=me["email"])

code = client.await_email_code(sender="notifications@example.com", timeout_seconds=600)

if code:
    submit_verification(code)
```

`await_email_code` holds one HTTP connection open server-side. The agent is not running, so **it spends nothing while it waits.** If nothing arrives before the timeout, the call refunds itself and returns `None`.

There is `await_email_link()` too, for services that send a confirmation URL instead of a code.

## Wait for anything, not just mail

```python
# A webhook you own
client.create_hook("stripe")
event = client.await_trigger({"type": "hook", "slug": "stripe"})

# Another agent
event = client.await_trigger({"type": "signal", "name": "approved"})

# A moment in time
event = client.await_trigger({"type": "time", "at": "2026-09-01T09:00:00Z"})
```

## Wait longer than the process lives

When the wait could outlast the run, hand over the state and exit. Aura wakes you up with it.

```python
# Run 1 - park and exit.
client.park(
    {"order_id": "A-1183", "step": "awaiting-approval"},
    {"type": "email", "match": "approved", "extract": "none"},
    expires_in_hours=72,
)
raise SystemExit(0)

# Run 2, tomorrow - pick up exactly where it stopped.
parked = client.resume()
if parked["resumed"]:
    print(parked["state"])  # {"order_id": "A-1183", "step": "awaiting-approval"}
    print(parked["event"])  # the mail that woke it
```

`resume()` is **free when there is nothing to resume**, so it is safe at the top of every run.

## Memory that survives the process

```python
client.remember("customer.A-1183", json.dumps(profile), tags=["customer"])

value = client.recall("customer.A-1183")["value"]
hits = client.search_memory("who complained about shipping")["results"]
```

## Async

Same surface, all awaitable. This matters for `await_trigger`, which can hold a connection for ten minutes.

```python
from aura_agent import AsyncAura, LocalSigner

async with await AsyncAura.claim(LocalSigner(private_key)) as client:
    code = await client.await_email_code(timeout_seconds=600)
```

## LangChain

```bash
pip install "aura-x402[langchain]"
```

```python
from aura_agent import Aura, LocalSigner
from aura_agent.langchain import get_tools

client = Aura.claim(LocalSigner(private_key))
tools = get_tools(client)

agent = create_react_agent(model, tools)
```

Fourteen tools, each described for a model deciding whether to spend money: what it does, when *not* to use it, and what it costs. Narrow the set when an agent only needs part of it:

```python
tools = get_tools(client, include=["aura_remember", "aura_recall", "aura_await_email_code"])
```

## CrewAI

```bash
pip install "aura-x402[crewai]"
```

```python
from aura_agent.crewai import get_tools

researcher = Agent(role="researcher", goal="...", tools=get_tools(client))
```

## Paying

Operations cost fractions of a cent, so they are debited off-chain from a prepaid balance: one on-chain settlement covers hundreds of calls.

New identities get a small trial credit. After that, `deposit()` returns HTTP 402 with the payment requirements attached:

```python
from aura_agent import AuraError

try:
    client.deposit(5)
except AuraError as err:
    print(err.fix)  # network, asset, payTo, amount - everything an x402 client needs
```

Pass an x402-capable `httpx` transport to the constructor and it settles by itself.

## Errors tell you how to fix them

Aura writes its errors for a reader with no human to ask, and this SDK keeps that intact.

```python
try:
    client.remember("k", "v")
except AuraError as err:
    if err.needs_credit:
        print(err.fix)      # the exact deposit call to make
    print(err.refunded)     # whether the failed call was charged
```

A `404` on memory carries the keys that do exist. A `400` carries the schema plus a working example.

## Prices

| Call | Price |
| --- | --- |
| `capabilities`, `whoami`, `claim_identity`, `forget` | free |
| `recall`, `list_memory` | $0.0005 |
| `remember` | $0.0008 |
| `inbox`, `read_mail`, `create_hook` | $0.001 |
| `search_memory`, `signal`, `resume` | $0.002 |
| `await_trigger`, `await_email_code` | $0.004 |
| `park` | $0.005 |

`await_trigger` refunds itself when it times out with nothing. `resume` is free when nothing is waiting.

## Using it over MCP instead

Every operation is also an MCP tool:

```json
{
  "mcpServers": {
    "aura": {
      "type": "http",
      "url": "https://aura.rohnelt.dev/mcp",
      "headers": { "Authorization": "Bearer aura_sk_..." }
    }
  }
}
```

Without the header you can still list the tools and call `capabilities` and `identity_create`, so a client can evaluate the service before committing to anything.

## License

MIT
