Start with self-hosted Actae (free)
Self-hosting is the default way to run Actae: one free, self-contained binary with the API, dashboard, and PostgreSQL embedded — no account, card, or control plane. Download it from actae.dev/download and run it.
$ tar -xzf actae-<version>-<platform>.tar.gz && chmod +x ./actae $ ./actae # the banner ends with: # Dashboard: http://127.0.0.1:8002 # API key: sk-… (shown once — store it now)
You still need an API key — a local key the binary generates and prints once on first boot, not an account or portal credential. There is no signup. Mint more with actae keys create <name>. (Anonymous access is off by default.) Point the SDK at http://127.0.0.1:8002 with that key. Prefer not to operate it? Actae Cloud is the managed runtime.
Install
$ pip install actae-client
Requires Python 3.9+ and aiohttp. Framework adapters (LangGraph, LangChain, CrewAI, Claude, and OpenAI Agents) are available as optional extras.
Quick start
pythonimport asyncio
from actae_client import ActaeClient
async def main():
async with ActaeClient(endpoint="http://localhost:8002", api_key="sk-…") as client:
event = await client.record("my-channel", "agent.step",
payload={"input": "hello"}, actor="agent")
print(f"cursor={event.cursor}")
events = await client.replay("my-channel", cursor=0)
for e in events:
print(f"[{e.cursor}] {e.event_type}")
asyncio.run(main())endpoint alone is enough to derive the WebSocket URL (http:// → ws://…/ws). The client is an async context manager — use it as one unless you need manual lifecycle control.
Which API should I use?
events.record(channel, type, payload, actor=…)events.transition(…)events.replay(…), events.query(…)ws.stream(channel) or ws.subscribe(…)AgentSessionchannels.fork(…)groups.*executions.claim_execution(…)wakeups.schedule_wakeup(…)channels.diff_states(left, right)Every method exists on the client (client.record(…)) and on a namespaced facade (client.events.record(…)) — same calls, pick what reads better.
Real time
pythonasync with ActaeClient(endpoint=..., api_key=..., echo_self=True) as client:
async for event in client.stream("my-channel"):
print(f"[{event.cursor}] {event.event_type}")
client.on_message(lambda topic, event: print(topic, event.event_type))
await client.subscribe("my-channel") # wait=True by default
await client.publish("my-channel", {"hello": "world"})- Both forms block until events arrive — a live stream never finishes.
on_messagecallbacks accumulate; other callbacks are single-slot.- The server does not echo publishes back to the publishing connection — use
echo_self=Truefor single-client demos. - Auto-reconnect resumes all topics from their last cursors (no gaps, no duplicates).
Sync code? No problem.
The most common calls have blocking variants (record_sync, replay_sync, transition_sync, fork_sync, latest_state_sync, disconnect_sync, …) — each runs on its own per-thread background loop, so sync and async use can mix freely. WebSocket, groups, executions, wake-ups, and AgentSession are async-only.
Sessions, adapters, and errors
AgentSession instruments whole runs: step/fork/resume with deterministic operation IDs, inherited state, and strict boundary modes. Adapters: StateManager, ActaeCheckpointSaver (LangGraph), ActaeCrewStateHook/CrewAIResumer, ActaeContextSaver/ChainResumer (LangChain), ActaeClaudeSessionStore/ActaeClaudeHook, and install_actae_tracing (OpenAI Agents).
Errors are typed: AuthError (401), RateLimitError (429), ActaeConnectionError (transport — never shadows builtins.ConnectionError), SessionError/SessionCompletedError/NoRestorableCheckpointError, and APIError for the rest.