---
title: "Python SDK"
description: "Drive agents, email, events, phone calls and agent-to-agent tasks from Python, sync or asyncio, with safe retries and webhook verification."
canonical_url: "https://www.agent-identity.dev/docs/sdk/python"
markdown_url: "https://www.agent-identity.dev/docs/sdk/python.md"
last_updated: "2026-10-23"
x_farming_labs_generated_preamble: true
agent:
  tokenBudget: 900
  task: "Use Agent Identity from Python: create an agent, send email and wait for the reply, place a call, and verify webhooks."
  outcome: "An AgentClient (or AsyncAgentClient) sends mail from an agent and events.wait returns the reply."
  appliesTo:
    package:
      - "agentidentity"
  prerequisites:
    - "Python 3.9 or newer."
    - "An API key in AID_API_KEY; use an agent key for operations that act as one agent, such as A2A tasks."
  files:
    - "packages/sdk-py/src/agentidentity/_resources.py"
  commands:
    - run: "pip install agentidentity"
      description: "Install the client. Its one dependency is httpx."
  sideEffects:
    - "Requests time out after 30s and retry twice on rate limits and server errors; a send is never repeated after a dropped connection."
  verification:
    - description: "Confirm the key works."
      expect: "AgentClient().whoami() returns the actor type and organization."
  rollback:
    - "Not applicable."
  failureModes:
    - symptom: "ValueError: pass api_key, or set AID_API_KEY."
      resolution: "Set AID_API_KEY, or pass api_key= to AgentClient."
    - symptom: "AidApiError 403 on a2a.send_task."
      resolution: "Send tasks with an agent's own key, and check the target is reachable under its access rules."
---

# Python SDK
URL: /docs/sdk/python
LLM index: /llms.txt
Description: Drive agents, email, events, phone calls and agent-to-agent tasks from Python, sync or asyncio, with safe retries and webhook verification.
Related: /docs/sdk, /docs/getting-started/quickstart, /docs/concepts/events, /docs/mcp

<!-- farming-labs:agent-contract:start -->
## Agent Contract

Task: Use Agent Identity from Python: create an agent, send email and wait for the reply, place a call, and verify webhooks.
Outcome: An AgentClient (or AsyncAgentClient) sends mail from an agent and events.wait returns the reply.

### Applies To

- Package: `agentidentity`

### Prerequisites

- Python 3.9 or newer.
- An API key in AID_API_KEY; use an agent key for operations that act as one agent, such as A2A tasks.

### Files

- `packages/sdk-py/src/agentidentity/_resources.py`

### Commands

- `pip install agentidentity` — Install the client. Its one dependency is httpx.

### Side Effects

- Requests time out after 30s and retry twice on rate limits and server errors; a send is never repeated after a dropped connection.

### Verification

- Confirm the key works.
  - Expected: AgentClient().whoami() returns the actor type and organization.

### Rollback

- Not applicable.

### Failure Modes

- ValueError: pass api_key, or set AID_API_KEY. — Recovery: Set AID_API_KEY, or pass api_key= to AgentClient.
- AidApiError 403 on a2a.send_task. — Recovery: Send tasks with an agent's own key, and check the target is reachable under its access rules.
<!-- farming-labs:agent-contract:end -->

# Python SDK

```bash
pip install agentidentity
```

```python title="hello.py"
from agentidentity import AgentClient

client = AgentClient()  # reads AID_API_KEY

agent = client.identities.create(handle="support", display_name="Support")
sent = client.mail.send(identity_id=agent.id, to="you@example.com", subject="Hello", text="From my own inbox.")

result = client.events.wait("mail.received", identity_id=agent.id, thread_id=sent.thread_id, timeout=55)
if not result.timed_out:
    client.mail.reply(identity_id=agent.id, message_id=result.event.payload["messageId"], text="Thanks!")
```

Responses are dicts you can also read by attribute, with the API's own field names, so fields a newer server adds
arrive without an upgrade. `AsyncAgentClient` has the same methods for asyncio; await each call.

## What it covers

| Area | Methods |
|---|---|
| Agents | `identities.create`, `list`, `get`, `update`, `retire` |
| Email | `mail.send`, `reply`, `list_threads`, `get_thread`, `update_thread`, `search`, `list_messages` |
| Events | `events.wait`, `subscribe`, `list` |
| Phone | `phone.place_call`, `list_calls`, `get_call`, `hang_up`, `transcript`, `recording`, `voicemail`, `list_numbers`, `provision_number`, `get_incoming_call_action`, `set_incoming_call_action` |
| Agent2Agent | `a2a.send_task`, `get_task`, `list_tasks`, `reply_to_task`, `cancel_task` |
| Webhooks | `verify_webhook(secret=..., body=raw_body, headers=...)` |

Everything else in the [API reference](/docs/api) works the same way over HTTP; the [TypeScript SDK](/docs/sdk)
covers the whole surface.

## Waiting without polling

```python
for event in client.events.subscribe("mail.received", identity_id=agent.id):
    handle(event)
```

`subscribe` advances on each event's own time, so nothing is skipped or repeated, and waits out outages. It stops
with `AidApiError` if the key is refused.

## Phone calls

```python
call = client.phone.place_call(identity_id=agent.id, to="+14155550100", reason="Confirm Thursday's 3 p.m. demo.")
client.events.wait("call.ended", identity_id=agent.id, timeout=55)
client.phone.transcript(call.id)
client.phone.recording(call.id)  # None until it is saved
```

With a `reason`, [Aid Voice](/docs/phone/aid-voice) makes the call and carries out the task.

## Errors and retries

`AidApiError` carries `status`, `code`, `request_id` and the server's message; `AidConnectionError` means the API was
never reached. Rate limits (429) and server errors on safe requests are retried with backoff, honouring
`Retry-After`. Pass `idempotency_key` to `mail.send` so your own retries never send twice.

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
Docs-scoped sitemap: [/docs/sitemap.md](/docs/sitemap.md).
Well-known sitemap: [/.well-known/sitemap.md](/.well-known/sitemap.md).
