Operating system set to macOS
Documentation jurisdiction set to United States
Sign inConnect Claude

Python SDK

The official Python client. Async-first, typed, and shipped on PyPI as heyarchie. The core client is hand-written; the per-skill surface is generated from the canonical skill registry, so it tracks the live catalogue. Read the source if you want to know how something works.

Install

your project venvbash
python3 -m venv .venv
source .venv/bin/activate
pip install heyarchie

Python 3.10 or newer. The package has zero runtime dependencies beyond httpx and pydantic.

Authenticate

Pass api_key or set ARCHIE_API_KEY in the environment. Tokens start with sk_.

from heyarchie import Archie

archie = Archie(api_key="sk_...")
# or, with env var:
archie = Archie()

Typed skill methods

Every public skill is a typed method on client.skills — 65 of them, one per skill, generated from the canonical skill registry (the same source that drives the skill catalogue). Required input fields are required keyword arguments; optional fields default to None. Each method delegates to skills.invoke(), so the wire format is unchanged.

Version note: typed methods ship in heyarchie 1.4.0. PyPI currently serves 1.3.0, which has the generic skills.invoke() only. Until 1.4.0 publishes, get the typed surface from a repo checkout: pip install -e apps/sdk-python.

from heyarchie import Archie

client = Archie()

# List topics — typed kwargs, no JSON body to assemble
topics = client.skills.topic_list(limit=5)

# Create one
client.skills.topic_create(name="FY26 audit — Acme", type="client")

# Advisory skills are typed too
advice = client.skills.advise_tax(
    topic="CGT discount on an active asset",
    jurisdiction="AU",
)

The async client gets the same 65 methods, awaitable:

from heyarchie import AsyncArchie

async with AsyncArchie() as archie:
    topics = await archie.skills.topic_list(limit=5)

Extra keyword arguments pass through to the wire untouched, so a field added server-side is usable before the SDK regenerates. The generic skills.invoke(slug, **inputs) remains the fallback for any skill by slug — and the only path on 1.3.0.

Async usage

Use AsyncArchie when you're inside an event loop. Same API, awaitable methods.

python
from heyarchie import AsyncArchie

async def main():
    async with AsyncArchie() as archie:
        reply = await archie.skills.invoke(
            "ask_archie",
            query="What does ASC 842 require for a finance lease?",
        )
        return reply.content

Resources

The client mirrors the REST surface. Each resource is a property on the client.

  • archie.skills: invoke any skill in the registry — generic invoke() plus one typed method per skill (1.4.0)
  • archie.messages: pretty alias for the ask_archie skill
  • archie.search: knowledge-base search
  • archie.conversations: list and update conversations
  • archie.drafts: communication drafting
  • archie.workstreams: list, run, observe workstream cycles
  • archie.documents: analyse uploaded documents
  • archie.standards: compare against AASB, FRS, IFRS, ASC
  • archie.reports: export reports, fetch metrics
  • archie.keys: manage your own API keys

Managing API keys

Full CRUD on /api/v1/connect-keys/* — every operation you can do in the portal is also reachable from client.keys. heyarchie 1.2.0+. See Managing keys for the full walkthrough.

from heyarchie import Archie

client = Archie()

# Mint a new key with per-key knobs at create time.
key = client.keys.create(
    name="ci-deploys",
    permissions=["ask:execute", "search:read"],
    rate_limit_per_minute=30,
    description="staging deploy bot",
)
print(key.value)  # one-time sk_… — store immediately

# Edit metadata without rotating. `permissions` updates return 501;
# use `rotate` for permission changes.
client.keys.update(
    key.id,
    workspace_id="b0000000-0000-0000-0000-000000000001",
    description="staging deploy bot — updated",
)

# Mint a replacement, parent dies after grace_period_hours.
new_key = client.keys.rotate(key.id, grace_period_hours=1)
print(new_key.rotated_from_key_id)  # = old key.id

# Idempotent revoke. Subsequent requests through the key 401.
client.keys.revoke(key.id)

# List + single-key read return the merged shape (WorkOS canonical
# + mirror canonical fields like workspace_id, rate_limit_per_minute,
# description, environment, revoked_at).
page = client.keys.list()
for k in page.data:
    print(k.id, k.obfuscated_value, k.workspace_id)

Framework adapters

One line to use Archie skills as native tools in LangGraph, LangChain, or the OpenAI Agents SDK. The adapter fetches the live registry at call time so your tool list stays in sync as the skill catalogue evolves.

LangGraph / LangChain

pip install heyarchie langchain-core langgraph
from heyarchie.adapters.langgraph import as_langgraph_tools
from langchain_anthropic import ChatAnthropic

# Standalone function
tools = as_langgraph_tools(api_key="sk_...", scope="ask:execute")
model = ChatAnthropic(model="claude-sonnet-4-6").bind_tools(tools)

# Or via the client
archie = Archie(api_key="sk_...")
tools = archie.skills.as_langgraph_tools(scope="ask:execute")

OpenAI Agents SDK

pip install heyarchie openai-agents
from heyarchie.adapters.openai_agents import as_openai_tools
from agents import Agent, Runner

tools = as_openai_tools(api_key="sk_...", scope="ask:execute")
agent = Agent(
    name="Archie Tax Advisor",
    instructions="Expert accounting assistant.",
    tools=tools,
)
result = Runner.run_sync(agent, "What is the BAS for Q1 2026?")
print(result.final_output)

The scope= parameter filters to skills whose permission prefix matches — the same vocabulary as MCP scopes and restricted API keys. Pass client_id= to set a default client context for all tool calls.

Skill discovery

Before picking a skill, call skills.discover() to survey the catalogue. Agents should use this instead of hard-coding slug names that can change as the registry evolves.

ranked = archie.skills.discover(goal="prepare Q1 financial statements")
best = ranked["ranked"][0]  # {"name": "reports.financial.prepare", "score": 0.95, ...}

# Invoke the top match
result = archie.skills.invoke(best["name"], period="2026-Q1")

# Filter by routing cluster (returns all skills in cluster, no scoring)
tax_skills = archie.skills.discover(cluster="TAX")

# Async variant
ranked = await async_archie.skills.discover(goal="reconcile bank accounts")

Two-tier dispatch

Skills route through a two-tier model. A general-intent classifier picks a primary cluster (TAX, ASR, CMP, WLT, BOP, XCT) from your query, then a specialist resolves the exact skill. The cluster is exposed as x-skill-cluster in the OpenAPI spec and on every reply, so you can route follow-ups to the same cluster for continuity. Skills are reachable at POST /api/v1/skills/{slug}/invoke; for chat-style flows, the ask_archie skill is the first-class entry point that routes through the orchestrator and accepts free-form queries.

Streaming

Long replies stream back as Server-Sent Events. Use skills.invoke_stream() instead of skills.invoke() to get an async generator of SkillStreamChunk objects. Each chunk has a delta string (or None for the terminal event).

python
from heyarchie import AsyncArchie

async with AsyncArchie() as archie:
    async for chunk in archie.skills.invoke_stream(
        "ask_archie",
        query="Walk me through ASC 842 lease accounting.",
    ):
        if chunk.delta:
            print(chunk.delta, end="", flush=True)

The sync client also has skills.invoke_stream(). It returns a regular generator, not an async generator.

Errors

The client raises typed exceptions per the OpenAPI error envelope:

from heyarchie import (
    ArchieAuthError,         # 401, invalid or revoked key
    ArchiePermissionError,   # 403, key lacks permission
    ArchieRateLimitError,    # 429, includes retry_after_seconds
    ArchieNotFoundError,     # 404
    ArchieAPIError,          # 5xx
)

try:
    reply = archie.skills.invoke("ask_archie", query="...")
except ArchieRateLimitError as e:
    time.sleep(e.retry_after_seconds)
except ArchieAPIError as e:
    log.exception("archie call failed", extra={"request_id": e.request_id})

Sandbox fixture for CI

We ship a deterministic sandbox harness so your tests don't hit production. Any test key (sk_ issued in a Test workspace) is routed against the seeded magic-data org.

python
from heyarchie.testing import sandbox_archie

def test_revenue_recognition_flow():
    with sandbox_archie() as archie:
        reply = archie.skills.invoke(
            "ask_archie",
            query="When is revenue recognized for a 30-day return policy?",
        )
        assert any(c.standard == "ASC 606" for c in reply.citations)

Reference

Full type signatures are checked into apps/sdk-python in the public repo. The REST reference lists every endpoint the SDK calls under the hood.