# A2A Protocol Source: https://docs.useanima.sh/a2a/overview Agent-to-agent task exchange and discovery for AI agents, backed by did:web identity and public Agent Cards. # A2A Protocol Every Anima agent gets a `did:web` DID and a public Agent Card, so other agents -- Anima-hosted or not -- can discover it and learn what it can do. On top of that identity layer, Anima gives each agent a task queue: a structured way to hand a job to one of your agents, tag who it's from, and track it from submission through completion. ## How A2A Works 1. **Identity** -- every agent has a `did:web` DID and a DID document (containing its public key) published at a world-readable URL 2. **Discovery** -- fetch any agent's Agent Card (`/.well-known/agent.json`) to see its capabilities, DID, and contact info 3. **Task submission** -- submit a structured task onto one of your own agents' task queues (first-party), tagged with a sender DID 4. **Dispatch** -- send a *signed* task to another agent by DID; Anima signs it with your agent's key and the recipient verifies that signature against your published DID document before accepting it 5. **Tracking** -- poll for status (`submitted` -> `working` -> `completed` / `failed` / `input_required`), or cancel a task that hasn't finished yet Task submission (`submitTask`, `getTask`, `listTasks`, `cancelTask`) is scoped to your own org -- the `agentId` in every task endpoint must be an agent your org owns. `discover` is unscoped: it's a plain, unauthenticated fetch of the target's public Agent Card, on Anima or anywhere else. ## Discovering an Agent Fetch an agent's Agent Card directly from its `/.well-known/agent.json` URL. This works for any agent that publishes one -- it doesn't go through the Anima API and doesn't require an API key. ```ts theme={null} import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: "ak_..." }); const card = await anima.a2a.discover("https://sender.useanima.sh"); console.log(card.name, card.did, card.capabilities); ``` ```python theme={null} from anima import Anima anima = Anima(api_key="ak_...") card = anima.a2a.discover("https://sender.useanima.sh") print(card["name"], card["did"], card["capabilities"]) ``` ```bash theme={null} anima a2a discover https://sender.useanima.sh ``` ## Submitting a Task Submit a task to one of your agents. `agentId` is the receiving agent (must belong to your org); `from` is a sender DID you supply -- it's recorded on the task but not cryptographically verified. ```ts theme={null} const task = await anima.a2a.submitTask("ag_receiver456", { type: "purchase-order", input: { vendor: "Office Supplies Inc", items: [ { name: "Printer paper", quantity: 10, unitPrice: 8.99 }, { name: "Ink cartridges", quantity: 4, unitPrice: 24.99 }, ], budget: 200, }, fromDid: "did:web:agents.useanima.sh:org_abc123:ag_sender123", }); console.log(`Task ID: ${task.id}`); console.log(`Status: ${task.status}`); // "submitted" ``` ```python theme={null} task = anima.a2a.submit_task( "ag_receiver456", type="purchase-order", input={ "vendor": "Office Supplies Inc", "items": [ {"name": "Printer paper", "quantity": 10, "unitPrice": 8.99}, {"name": "Ink cartridges", "quantity": 4, "unitPrice": 24.99}, ], "budget": 200, }, from_did="did:web:agents.useanima.sh:org_abc123:ag_sender123", ) print(f"Task ID: {task.id}") print(f"Status: {task.status}") ``` ```go theme={null} import "github.com/anima-labs-ai/go" client := anima.NewClient("ak_...") task, err := client.A2A.SubmitTask(ctx, "ag_receiver456", anima.SubmitA2ATaskParams{ Input: map[string]any{ "vendor": "Office Supplies Inc", "items": []map[string]any{ {"name": "Printer paper", "quantity": 10, "unitPrice": 8.99}, }, "budget": 200, }, }) ``` ```bash theme={null} anima a2a send \ --agent ag_receiver456 \ --type purchase-order \ --input '{"vendor":"Office Supplies Inc","budget":200}' \ --from-did did:web:agents.useanima.sh:org_abc123:ag_sender123 ``` ## Dispatching a Task to Another Agent `dispatch` is the authenticated agent-to-agent path. Anima signs the task with your sending agent's key and delivers it to the recipient's public inbound endpoint, which verifies the signature against your published DID document before accepting it. Unlike `submitTask` -- where `from` is an unverified label on your own queue -- a dispatched task's sender identity is cryptographically proven. The recipient is addressed by DID. Your sending agent must be registered in the [Agent Registry](/registry/overview), and signing happens server-side -- your private key never leaves Anima. ```ts theme={null} const task = await anima.a2a.dispatch("ag_sender123", { toDid: "did:web:agents.useanima.sh:org_xyz:ag_receiver456", type: "purchase-order", input: { vendor: "Office Supplies Inc", budget: 200 }, }); console.log(`Dispatched: ${task.id} (${task.status})`); ``` ```python theme={null} task = anima.a2a.dispatch( "ag_sender123", to_did="did:web:agents.useanima.sh:org_xyz:ag_receiver456", type="purchase-order", input={"vendor": "Office Supplies Inc", "budget": 200}, ) ``` ```go theme={null} task, err := client.A2A.Dispatch(ctx, "ag_sender123", anima.DispatchA2ATaskParams{ ToDID: "did:web:agents.useanima.sh:org_xyz:ag_receiver456", Type: "purchase-order", Input: map[string]any{"vendor": "Office Supplies Inc", "budget": 200}, }) ``` ```bash theme={null} anima a2a dispatch \ --from ag_sender123 \ --to-did did:web:agents.useanima.sh:org_xyz:ag_receiver456 \ --type purchase-order \ --input '{"vendor":"Office Supplies Inc","budget":200}' ``` You never call the inbound endpoint (`POST /v1/a2a/inbound`) directly -- it's the public, signature-authenticated surface where *dispatched* tasks arrive. It takes no API key: the request is authenticated purely by the sender's DID signature (verified against the sender's DID document), then the task is recorded on the receiving agent, in the receiver's org. Resolving non-Anima `did:web` senders is off by default (see [Configuration](#configuration)). ## Tracking a Task ```ts theme={null} // Get a single task const task = await anima.a2a.getTask("ag_receiver456", "task_abc123"); // List tasks, optionally filtered by status const { items, nextCursor } = await anima.a2a.listTasks("ag_receiver456", { status: "working", limit: 20, }); // Cancel a submitted or working task await anima.a2a.cancelTask("ag_receiver456", "task_abc123"); ``` ```python theme={null} task = anima.a2a.get_task("ag_receiver456", "task_abc123") result = anima.a2a.list_tasks("ag_receiver456", status="working", limit=20) anima.a2a.cancel_task("ag_receiver456", "task_abc123") ``` ```go theme={null} task, err := client.A2A.GetTask(ctx, "ag_receiver456", "task_abc123") page, err := client.A2A.ListTasks(ctx, "ag_receiver456", &anima.A2ATaskListParams{ Status: anima.A2ATaskStatusCompleted, }) task, err = client.A2A.CancelTask(ctx, "ag_receiver456", "task_abc123") ``` ```bash theme={null} anima a2a tasks --agent ag_receiver456 --status working --limit 20 ``` ## Task Lifecycle | Status | Description | | ---------------- | ------------------------------------------------------------- | | `submitted` | Task created, not yet picked up | | `working` | Task is being processed | | `input_required` | Processing is paused, waiting on additional input | | `completed` | Task finished successfully | | `failed` | Task errored | | `canceled` | Task was canceled (only reachable from `submitted`/`working`) | ## API Reference | Endpoint | Method | Description | | ------------------------------------------------ | ------ | ---------------------------------------------- | | `/v1/agents/{fromAgentId}/a2a/dispatch` | POST | Dispatch a signed task to another agent by DID | | `/v1/agents/{agentId}/a2a/tasks` | POST | Submit a task to an agent | | `/v1/agents/{agentId}/a2a/tasks` | GET | List an agent's tasks | | `/v1/agents/{agentId}/a2a/tasks/{taskId}` | GET | Get task status and result | | `/v1/agents/{agentId}/a2a/tasks/{taskId}/cancel` | POST | Cancel a submitted or working task | Base URL: `https://api.useanima.sh/v1`. All endpoints above require `Authorization: Bearer ak_...` (or another valid key) for an org that owns `agentId`. Discovery and signed inbound are separate surfaces that take no API key: | Endpoint | Method | Description | | ------------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------- | | `https:///.well-known/agent.json` | GET | Fetch an agent's public Agent Card | | `https://agents.useanima.sh/{orgId}/{agentId}/did.json` | GET | Resolve an agent's `did:web` DID document | | `/v1/a2a/inbound` | POST | Receive a DID-signed task from another agent (authenticated by signature, not an API key) | ## Configuration Inbound A2A behavior is controlled by these environment variables: | Variable | Default | Description | | ------------------------------ | ------- | -------------------------------------------------------------------------------------------- | | `ANIMA_A2A_REQUIRE_DID_AUTH` | `true` | Require a valid DID signature on inbound tasks (set `false` to disable the inbound endpoint) | | `ANIMA_A2A_MAX_SKEW` | `300` | Maximum timestamp skew, in seconds, for a signed inbound request | | `ANIMA_A2A_ALLOW_EXTERNAL_DID` | `false` | Resolve non-Anima `did:web` senders (off by default; Anima-to-Anima works regardless) | | `ANIMA_A2A_INBOUND_RATE` | `30` | Maximum inbound requests per source IP per minute | ## CLI ```bash theme={null} # Discover an agent's capabilities anima a2a discover # Submit a task to one of your own agents anima a2a send --agent --type --input [--from-did ] # Dispatch a signed task to another agent by DID anima a2a dispatch --from --to-did --type --input # List tasks for an agent anima a2a tasks --agent [--status ] [--cursor ] [--limit ] ``` A2A pairs with the [Agent Registry](/registry/overview) and agent identity commands: ```bash theme={null} # Registry: publish and discover agents anima registry register --agent-id --name [--description ] [--tags ] [--public] anima registry search [--query ] [--capability ] [--trust-min <0-100>] [--tags ] anima registry lookup --did # Identity: inspect an agent's DID document and Agent Card anima identity did --agent anima identity card --agent ``` ## Next Steps * [Agent Cards](/identity/agent-cards) -- Agent Card format and publishing * [DID Method](/identity/did-method) -- `did:web` DID documents and resolution * [Agent Registry](/registry/overview) -- Publish and search for agents # For AI Agents Source: https://docs.useanima.sh/ai-agents Anima is built to be operated by AI agents. Point your agent at the skill manifest, connect the docs MCP server, and read the docs as machine-readable markdown. # For AI Agents Anima is designed to be set up and operated by an AI agent, not just a human clicking through a dashboard. If you are a coding agent — or you are handing this page to one — everything below is meant to be consumed directly. ## Hand your agent the setup manifest The fastest path is to let the agent read Anima's skill manifest and follow it. Paste this into Claude Code, Cursor, Claude Desktop, or any MCP-aware agent: ```text theme={null} Read https://useanima.sh/skill.md and get me set up with Anima. ``` [`https://useanima.sh/skill.md`](https://useanima.sh/skill.md) is an executable, agent-facing manifest. It walks the agent through installing the CLI, authenticating, provisioning an agent identity, wiring Anima as an MCP server, and finishing onboarding — using your existing API key if you already have one. ## Connect the docs MCP server Anima hosts a Model Context Protocol server for the documentation itself, so your agent can search and retrieve authoritative docs content instead of guessing from stale training data. | | | | ------------- | ---------------------------------------------- | | **Endpoint** | `https://docs.useanima.sh/mcp` | | **Transport** | Streamable HTTP | | **Auth** | None — the docs server is public and read-only | It exposes search and retrieval tools over the published documentation. Add it to any client that supports remote MCP. ```bash Claude Code theme={null} claude mcp add anima-docs --transport http --url https://docs.useanima.sh/mcp ``` ```json Cursor / other HTTP clients theme={null} { "mcpServers": { "anima-docs": { "url": "https://docs.useanima.sh/mcp" } } } ``` The docs MCP server answers questions about Anima from public documentation. To let an agent actually **do** things — send email, provision a number, place a call, use the vault — connect the product MCP servers described in [Connect your AI client](/integrations). ## Give your agent the product tools Anima's capabilities are exposed as MCP tools your agent can call. Wire them into your client with the CLI: ```bash theme={null} anima setup-mcp install --all ``` This configures the Anima MCP server for every supported client detected on the machine. See [Connect your AI client](/integrations) for per-client configuration, the hosted endpoint, and the full tool list. ## Read the docs as markdown Every documentation page is available as raw markdown — just append `.md` to the URL. This is the cleanest way for an agent to ingest a page without parsing HTML. ```bash theme={null} curl https://docs.useanima.sh/getting-started.md curl https://docs.useanima.sh/webhooks.md ``` For a whole-site view, two indexes follow the [llms.txt](https://llmstxt.org) convention: | File | Contents | | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------- | | [`https://docs.useanima.sh/llms.txt`](https://docs.useanima.sh/llms.txt) | A compact index of every documentation page, for discovery. | | [`https://docs.useanima.sh/llms-full.txt`](https://docs.useanima.sh/llms-full.txt) | The full documentation concatenated into one file, for ingestion. | ## Next steps Wire Anima's tools into Claude Code, Cursor, Claude Desktop, VS Code, and Windsurf. The CLI-first path: install, onboard, and send from the terminal. # Delete addresses Source: https://docs.useanima.sh/api-reference/delete-addresses /openapi.json delete /addresses/{id} # Delete agents Source: https://docs.useanima.sh/api-reference/delete-agents /openapi.json delete /agents/{id} # Delete api keys Source: https://docs.useanima.sh/api-reference/delete-api-keys /openapi.json delete /api-keys/{id} # Delete domains Source: https://docs.useanima.sh/api-reference/delete-domains /openapi.json delete /domains/{id} # Delete email rules Source: https://docs.useanima.sh/api-reference/delete-email-rules /openapi.json delete /email-rules/{id} # Delete emaildrafts Source: https://docs.useanima.sh/api-reference/delete-emaildrafts /openapi.json delete /email/drafts/{id} # Delete inboxes Source: https://docs.useanima.sh/api-reference/delete-inboxes /openapi.json delete /inboxes/{id} # Delete messages Source: https://docs.useanima.sh/api-reference/delete-messages /openapi.json delete /messages/{id} # Delete orgs Source: https://docs.useanima.sh/api-reference/delete-orgs /openapi.json delete /orgs/{id} # Delete orgs agents quarantine Source: https://docs.useanima.sh/api-reference/delete-orgs-agents-quarantine /openapi.json delete /orgs/{orgId}/agents/{agentId}/quarantine # Delete orgs anomaly rules Source: https://docs.useanima.sh/api-reference/delete-orgs-anomaly-rules /openapi.json delete /orgs/{orgId}/anomaly-rules/{ruleId} # Delete orgs compliancereports Source: https://docs.useanima.sh/api-reference/delete-orgs-compliancereports /openapi.json delete /orgs/{orgId}/compliance/reports/{reportId} # Delete registryagents Source: https://docs.useanima.sh/api-reference/delete-registryagents /openapi.json delete /registry/agents/{did} # Delete vaultcredentials Source: https://docs.useanima.sh/api-reference/delete-vaultcredentials /openapi.json delete /vault/credentials/{id} # Delete vaultoauthaccounts Source: https://docs.useanima.sh/api-reference/delete-vaultoauthaccounts /openapi.json delete /vault/oauth/accounts/{accountId} # Delete vaultoauthapps custom Source: https://docs.useanima.sh/api-reference/delete-vaultoauthapps-custom /openapi.json delete /vault/oauth/apps/{appSlug}/custom/{id} # Delete webhooks Source: https://docs.useanima.sh/api-reference/delete-webhooks /openapi.json delete /webhooks/{id} # Get addresses Source: https://docs.useanima.sh/api-reference/get-addresses /openapi.json get /addresses # Get addresses 1 Source: https://docs.useanima.sh/api-reference/get-addresses-1 /openapi.json get /addresses/{id} # Get agents Source: https://docs.useanima.sh/api-reference/get-agents /openapi.json get /agents # Get agents 1 Source: https://docs.useanima.sh/api-reference/get-agents-1 /openapi.json get /agents/{id} # Get agents a2atasks Source: https://docs.useanima.sh/api-reference/get-agents-a2atasks /openapi.json get /agents/{agentId}/a2a/tasks # Get agents a2atasks 1 Source: https://docs.useanima.sh/api-reference/get-agents-a2atasks-1 /openapi.json get /agents/{agentId}/a2a/tasks/{taskId} # Get agents card Source: https://docs.useanima.sh/api-reference/get-agents-card /openapi.json get /agents/{agentId}/card # Get agents credentials Source: https://docs.useanima.sh/api-reference/get-agents-credentials /openapi.json get /agents/{agentId}/credentials # Get agents did Source: https://docs.useanima.sh/api-reference/get-agents-did /openapi.json get /agents/{agentId}/did # Get agents email identities Source: https://docs.useanima.sh/api-reference/get-agents-email-identities /openapi.json get /agents/{agentId}/email-identities # Get agents permissions Source: https://docs.useanima.sh/api-reference/get-agents-permissions /openapi.json get /agents/{agentId}/permissions # Get agents policy Source: https://docs.useanima.sh/api-reference/get-agents-policy /openapi.json get /agents/{agentId}/policy # Get agentstatus Source: https://docs.useanima.sh/api-reference/get-agentstatus /openapi.json get /agent/status # Get api keys Source: https://docs.useanima.sh/api-reference/get-api-keys /openapi.json get /api-keys # Get api keysscopes Source: https://docs.useanima.sh/api-reference/get-api-keysscopes /openapi.json get /api-keys/scopes # Get attachments download Source: https://docs.useanima.sh/api-reference/get-attachments-download /openapi.json get /attachments/{id}/download # Get attachments text Source: https://docs.useanima.sh/api-reference/get-attachments-text /openapi.json get /attachments/{id}/text # Get billingfeatures Source: https://docs.useanima.sh/api-reference/get-billingfeatures /openapi.json get /billing/features/{feature} # Get billinginvoices Source: https://docs.useanima.sh/api-reference/get-billinginvoices /openapi.json get /billing/invoices # Get billingoverage Source: https://docs.useanima.sh/api-reference/get-billingoverage /openapi.json get /billing/overage # Get billingplans Source: https://docs.useanima.sh/api-reference/get-billingplans /openapi.json get /billing/plans # Get billingtier Source: https://docs.useanima.sh/api-reference/get-billingtier /openapi.json get /billing/tier # Get billingusage Source: https://docs.useanima.sh/api-reference/get-billingusage /openapi.json get /billing/usage # Get demoinbox messages Source: https://docs.useanima.sh/api-reference/get-demoinbox-messages /openapi.json get /demo/inbox/{token}/messages # Get domains Source: https://docs.useanima.sh/api-reference/get-domains /openapi.json get /domains # Get domains 1 Source: https://docs.useanima.sh/api-reference/get-domains-1 /openapi.json get /domains/{id} # Get domains deliverability Source: https://docs.useanima.sh/api-reference/get-domains-deliverability /openapi.json get /domains/{id}/deliverability # Get domains dns records Source: https://docs.useanima.sh/api-reference/get-domains-dns-records /openapi.json get /domains/{id}/dns-records # Get domains zone file Source: https://docs.useanima.sh/api-reference/get-domains-zone-file /openapi.json get /domains/{id}/zone-file # Get email Source: https://docs.useanima.sh/api-reference/get-email /openapi.json get /email/{id} # Get email 1 Source: https://docs.useanima.sh/api-reference/get-email-1 /openapi.json get /email # Get email rules Source: https://docs.useanima.sh/api-reference/get-email-rules /openapi.json get /email-rules # Get emaildrafts Source: https://docs.useanima.sh/api-reference/get-emaildrafts /openapi.json get /email/drafts # Get emaildrafts 1 Source: https://docs.useanima.sh/api-reference/get-emaildrafts-1 /openapi.json get /email/drafts/{id} # Get emailsuppressions Source: https://docs.useanima.sh/api-reference/get-emailsuppressions /openapi.json get /email/suppressions # Get extensionsettings Source: https://docs.useanima.sh/api-reference/get-extensionsettings /openapi.json get /extension/settings # Get feedback Source: https://docs.useanima.sh/api-reference/get-feedback /openapi.json get /feedback # Get inboxes Source: https://docs.useanima.sh/api-reference/get-inboxes /openapi.json get /inboxes # Get inboxes 1 Source: https://docs.useanima.sh/api-reference/get-inboxes-1 /openapi.json get /inboxes/{id} # Get meagents Source: https://docs.useanima.sh/api-reference/get-meagents /openapi.json get /me/agents # Get meorgs Source: https://docs.useanima.sh/api-reference/get-meorgs /openapi.json get /me/orgs # Get messages Source: https://docs.useanima.sh/api-reference/get-messages /openapi.json get /messages/{id} # Get messages 1 Source: https://docs.useanima.sh/api-reference/get-messages-1 /openapi.json get /messages # Get oauthapps Source: https://docs.useanima.sh/api-reference/get-oauthapps /openapi.json get /oauth/apps/{clientId} # Get oauthuserinfo Source: https://docs.useanima.sh/api-reference/get-oauthuserinfo /openapi.json get /oauth/userinfo # Get openclawagents Source: https://docs.useanima.sh/api-reference/get-openclawagents /openapi.json get /openclaw/agents # Get openclawauthorize Source: https://docs.useanima.sh/api-reference/get-openclawauthorize /openapi.json get /openclaw/authorize # Get openclawcallback Source: https://docs.useanima.sh/api-reference/get-openclawcallback /openapi.json get /openclaw/callback # Get orgs Source: https://docs.useanima.sh/api-reference/get-orgs /openapi.json get /orgs # Get orgs 1 Source: https://docs.useanima.sh/api-reference/get-orgs-1 /openapi.json get /orgs/{id} # Get orgs access reviews Source: https://docs.useanima.sh/api-reference/get-orgs-access-reviews /openapi.json get /orgs/{orgId}/access-reviews # Get orgs agents baselines Source: https://docs.useanima.sh/api-reference/get-orgs-agents-baselines /openapi.json get /orgs/{orgId}/agents/{agentId}/baselines # Get orgs agents quarantine Source: https://docs.useanima.sh/api-reference/get-orgs-agents-quarantine /openapi.json get /orgs/{orgId}/agents/{agentId}/quarantine # Get orgs anomaly alerts Source: https://docs.useanima.sh/api-reference/get-orgs-anomaly-alerts /openapi.json get /orgs/{orgId}/anomaly-alerts # Get orgs anomaly alerts 1 Source: https://docs.useanima.sh/api-reference/get-orgs-anomaly-alerts-1 /openapi.json get /orgs/{orgId}/anomaly-alerts/{alertId} # Get orgs anomaly rules Source: https://docs.useanima.sh/api-reference/get-orgs-anomaly-rules /openapi.json get /orgs/{orgId}/anomaly-rules # Get orgs audit logs Source: https://docs.useanima.sh/api-reference/get-orgs-audit-logs /openapi.json get /orgs/{orgId}/audit-logs # Get orgs audit logs 1 Source: https://docs.useanima.sh/api-reference/get-orgs-audit-logs-1 /openapi.json get /orgs/{orgId}/audit-logs/{logId} # Get orgs compliancecontrols Source: https://docs.useanima.sh/api-reference/get-orgs-compliancecontrols /openapi.json get /orgs/{orgId}/compliance/controls # Get orgs compliancecontrols 1 Source: https://docs.useanima.sh/api-reference/get-orgs-compliancecontrols-1 /openapi.json get /orgs/{orgId}/compliance/controls/{controlId} # Get orgs compliancecontrols evidence Source: https://docs.useanima.sh/api-reference/get-orgs-compliancecontrols-evidence /openapi.json get /orgs/{orgId}/compliance/controls/{controlId}/evidence # Get orgs compliancedashboard Source: https://docs.useanima.sh/api-reference/get-orgs-compliancedashboard /openapi.json get /orgs/{orgId}/compliance/dashboard # Get orgs compliancedsars Source: https://docs.useanima.sh/api-reference/get-orgs-compliancedsars /openapi.json get /orgs/{orgId}/compliance/dsars # Get orgs compliancedsars 1 Source: https://docs.useanima.sh/api-reference/get-orgs-compliancedsars-1 /openapi.json get /orgs/{orgId}/compliance/dsars/{dsarId} # Get orgs compliancereports Source: https://docs.useanima.sh/api-reference/get-orgs-compliancereports /openapi.json get /orgs/{orgId}/compliance/reports # Get orgs compliancereports 1 Source: https://docs.useanima.sh/api-reference/get-orgs-compliancereports-1 /openapi.json get /orgs/{orgId}/compliance/reports/{reportId} # Get orgs compliancesummary Source: https://docs.useanima.sh/api-reference/get-orgs-compliancesummary /openapi.json get /orgs/{orgId}/compliance/summary # Get orgs compliancetemplates Source: https://docs.useanima.sh/api-reference/get-orgs-compliancetemplates /openapi.json get /orgs/{orgId}/compliance/templates # Get orgs members Source: https://docs.useanima.sh/api-reference/get-orgs-members /openapi.json get /orgs/{id}/members # Get orgs securityevents Source: https://docs.useanima.sh/api-reference/get-orgs-securityevents /openapi.json get /orgs/{orgId}/security/events # Get orgs securityscanner status Source: https://docs.useanima.sh/api-reference/get-orgs-securityscanner-status /openapi.json get /orgs/{orgId}/security/scanner-status # Get orgsclaimable Source: https://docs.useanima.sh/api-reference/get-orgsclaimable /openapi.json get /orgs/claimable # Get orgsme Source: https://docs.useanima.sh/api-reference/get-orgsme /openapi.json get /orgs/me # Get orgsmeusage Source: https://docs.useanima.sh/api-reference/get-orgsmeusage /openapi.json get /orgs/me/usage # Get orgsmeworkspace health Source: https://docs.useanima.sh/api-reference/get-orgsmeworkspace-health /openapi.json get /orgs/me/workspace-health # Get phonenumbers Source: https://docs.useanima.sh/api-reference/get-phonenumbers /openapi.json get /phone/numbers # Get phonerequirements Source: https://docs.useanima.sh/api-reference/get-phonerequirements /openapi.json get /phone/requirements # Get phonesearch Source: https://docs.useanima.sh/api-reference/get-phonesearch /openapi.json get /phone/search # Get phonesms suppressions Source: https://docs.useanima.sh/api-reference/get-phonesms-suppressions /openapi.json get /phone/sms-suppressions # Get phonesmsthreads Source: https://docs.useanima.sh/api-reference/get-phonesmsthreads /openapi.json get /phone/sms/threads # Get phonesmsthreads 1 Source: https://docs.useanima.sh/api-reference/get-phonesmsthreads-1 /openapi.json get /phone/sms/threads/{id} # Get provisioning requests Source: https://docs.useanima.sh/api-reference/get-provisioning-requests /openapi.json get /provisioning-requests # Get provisioning requests 1 Source: https://docs.useanima.sh/api-reference/get-provisioning-requests-1 /openapi.json get /provisioning-requests/{requestId} # Get registryagents Source: https://docs.useanima.sh/api-reference/get-registryagents /openapi.json get /registry/agents/{did} # Get registryagentssearch Source: https://docs.useanima.sh/api-reference/get-registryagentssearch /openapi.json get /registry/agents/search # Get scoped tokens Source: https://docs.useanima.sh/api-reference/get-scoped-tokens /openapi.json get /scoped-tokens # Get threads Source: https://docs.useanima.sh/api-reference/get-threads /openapi.json get /threads # Get vaultaudit Source: https://docs.useanima.sh/api-reference/get-vaultaudit /openapi.json get /vault/audit # Get vaultcredential requests Source: https://docs.useanima.sh/api-reference/get-vaultcredential-requests /openapi.json get /vault/credential-requests # Get vaultcredential requests 1 Source: https://docs.useanima.sh/api-reference/get-vaultcredential-requests-1 /openapi.json get /vault/credential-requests/{requestId} # Get vaultcredentials Source: https://docs.useanima.sh/api-reference/get-vaultcredentials /openapi.json get /vault/credentials # Get vaultcredentials 1 Source: https://docs.useanima.sh/api-reference/get-vaultcredentials-1 /openapi.json get /vault/credentials/{id} # Get vaultidentities Source: https://docs.useanima.sh/api-reference/get-vaultidentities /openapi.json get /vault/identities # Get vaultoauthaccounts Source: https://docs.useanima.sh/api-reference/get-vaultoauthaccounts /openapi.json get /vault/oauth/accounts # Get vaultoauthapps Source: https://docs.useanima.sh/api-reference/get-vaultoauthapps /openapi.json get /vault/oauth/apps # Get vaultoauthapps 1 Source: https://docs.useanima.sh/api-reference/get-vaultoauthapps-1 /openapi.json get /vault/oauth/apps/{slug} # Get vaultoauthlink Source: https://docs.useanima.sh/api-reference/get-vaultoauthlink /openapi.json get /vault/oauth/link/{token} # Get vaultsearch Source: https://docs.useanima.sh/api-reference/get-vaultsearch /openapi.json get /vault/search # Get vaultshares Source: https://docs.useanima.sh/api-reference/get-vaultshares /openapi.json get /vault/shares # Get vaultstatus Source: https://docs.useanima.sh/api-reference/get-vaultstatus /openapi.json get /vault/status # Get vaulttotp Source: https://docs.useanima.sh/api-reference/get-vaulttotp /openapi.json get /vault/totp/{id} # Get voiceanalytics Source: https://docs.useanima.sh/api-reference/get-voiceanalytics /openapi.json get /voice/analytics # Get voicecalls Source: https://docs.useanima.sh/api-reference/get-voicecalls /openapi.json get /voice/calls # Get voicecalls 1 Source: https://docs.useanima.sh/api-reference/get-voicecalls-1 /openapi.json get /voice/calls/{callId} # Get voicecalls recording Source: https://docs.useanima.sh/api-reference/get-voicecalls-recording /openapi.json get /voice/calls/{callId}/recording # Get voicecalls score Source: https://docs.useanima.sh/api-reference/get-voicecalls-score /openapi.json get /voice/calls/{callId}/score # Get voicecalls security Source: https://docs.useanima.sh/api-reference/get-voicecalls-security /openapi.json get /voice/calls/{callId}/security # Get voicecalls summary Source: https://docs.useanima.sh/api-reference/get-voicecalls-summary /openapi.json get /voice/calls/{callId}/summary # Get voicecalls transcript Source: https://docs.useanima.sh/api-reference/get-voicecalls-transcript /openapi.json get /voice/calls/{callId}/transcript # Get voicecallscontacts Source: https://docs.useanima.sh/api-reference/get-voicecallscontacts /openapi.json get /voice/calls/contacts # Get voicecatalog Source: https://docs.useanima.sh/api-reference/get-voicecatalog /openapi.json get /voice/catalog # Get webhooks Source: https://docs.useanima.sh/api-reference/get-webhooks /openapi.json get /webhooks # Get webhooks 1 Source: https://docs.useanima.sh/api-reference/get-webhooks-1 /openapi.json get /webhooks/{id} # Get webhooks dead letters Source: https://docs.useanima.sh/api-reference/get-webhooks-dead-letters /openapi.json get /webhooks/{webhookId}/dead-letters # Get webhooks deliveries Source: https://docs.useanima.sh/api-reference/get-webhooks-deliveries /openapi.json get /webhooks/{webhookId}/deliveries # Get webhooks stats Source: https://docs.useanima.sh/api-reference/get-webhooks-stats /openapi.json get /webhooks/{id}/stats # Get webhooksevent types Source: https://docs.useanima.sh/api-reference/get-webhooksevent-types /openapi.json get /webhooks/event-types # Patch agents Source: https://docs.useanima.sh/api-reference/patch-agents /openapi.json patch /agents/{id} # Patch api keys Source: https://docs.useanima.sh/api-reference/patch-api-keys /openapi.json patch /api-keys/{id} # Patch domains Source: https://docs.useanima.sh/api-reference/patch-domains /openapi.json patch /domains/{id} # Patch extensionsettings Source: https://docs.useanima.sh/api-reference/patch-extensionsettings /openapi.json patch /extension/settings # Patch extensiontoken Source: https://docs.useanima.sh/api-reference/patch-extensiontoken /openapi.json patch /extension/token/{id} # Patch inboxes Source: https://docs.useanima.sh/api-reference/patch-inboxes /openapi.json patch /inboxes/{id} # Patch messages labels Source: https://docs.useanima.sh/api-reference/patch-messages-labels /openapi.json patch /messages/{id}/labels # Patch oauthapps Source: https://docs.useanima.sh/api-reference/patch-oauthapps /openapi.json patch /oauth/apps/{clientId} # Patch orgs Source: https://docs.useanima.sh/api-reference/patch-orgs /openapi.json patch /orgs/{id} # Patch orgs anomaly rules Source: https://docs.useanima.sh/api-reference/patch-orgs-anomaly-rules /openapi.json patch /orgs/{orgId}/anomaly-rules/{ruleId} # Patch orgs compliancecontrols Source: https://docs.useanima.sh/api-reference/patch-orgs-compliancecontrols /openapi.json patch /orgs/{orgId}/compliance/controls/{controlId} # Patch orgs compliancedsars Source: https://docs.useanima.sh/api-reference/patch-orgs-compliancedsars /openapi.json patch /orgs/{orgId}/compliance/dsars/{dsarId} # Patch phonenumbers Source: https://docs.useanima.sh/api-reference/patch-phonenumbers /openapi.json patch /phone/numbers/{phoneIdentityId} # Patch threads labels Source: https://docs.useanima.sh/api-reference/patch-threads-labels /openapi.json patch /threads/{threadId}/labels # Post addresses Source: https://docs.useanima.sh/api-reference/post-addresses /openapi.json post /addresses # Post addresses validate Source: https://docs.useanima.sh/api-reference/post-addresses-validate /openapi.json post /addresses/{id}/validate Structural (format-only) address validation: normalizes the country to ISO 3166-1 alpha-2, checks US state codes and ZIP shape, and verifies required fields are present. It does NOT check that the address exists or is deliverable, and `suggestions` is currently always empty (reserved for a future postal-data provider). # Post agentelevate Source: https://docs.useanima.sh/api-reference/post-agentelevate /openapi.json post /agent/elevate # Post agentelevaterequest Source: https://docs.useanima.sh/api-reference/post-agentelevaterequest /openapi.json post /agent/elevate/request # Post agents Source: https://docs.useanima.sh/api-reference/post-agents /openapi.json post /agents # Post agents a2adispatch Source: https://docs.useanima.sh/api-reference/post-agents-a2adispatch /openapi.json post /agents/{fromAgentId}/a2a/dispatch # Post agents a2atasks Source: https://docs.useanima.sh/api-reference/post-agents-a2atasks /openapi.json post /agents/{agentId}/a2a/tasks # Post agents a2atasks cancel Source: https://docs.useanima.sh/api-reference/post-agents-a2atasks-cancel /openapi.json post /agents/{agentId}/a2a/tasks/{taskId}/cancel # Post agents a2atasks update Source: https://docs.useanima.sh/api-reference/post-agents-a2atasks-update /openapi.json post /agents/{agentId}/a2a/tasks/{taskId}/update # Post agents credentials Source: https://docs.useanima.sh/api-reference/post-agents-credentials /openapi.json post /agents/{agentId}/credentials # Post agents credentials revoke Source: https://docs.useanima.sh/api-reference/post-agents-credentials-revoke /openapi.json post /agents/{agentId}/credentials/{vcId}/revoke # Post agents didrotate Source: https://docs.useanima.sh/api-reference/post-agents-didrotate /openapi.json post /agents/{agentId}/did/rotate # Post agents email identities verify Source: https://docs.useanima.sh/api-reference/post-agents-email-identities-verify /openapi.json post /agents/{agentId}/email-identities/{identityId}/verify # Post agents permissions Source: https://docs.useanima.sh/api-reference/post-agents-permissions /openapi.json post /agents/{agentId}/permissions # Post agents rotate key Source: https://docs.useanima.sh/api-reference/post-agents-rotate-key /openapi.json post /agents/{id}/rotate-key # Post agentsign up Source: https://docs.useanima.sh/api-reference/post-agentsign-up /openapi.json post /agent/sign-up # Post agentverify Source: https://docs.useanima.sh/api-reference/post-agentverify /openapi.json post /agent/verify # Post api keys Source: https://docs.useanima.sh/api-reference/post-api-keys /openapi.json post /api-keys # Post api keys rotate Source: https://docs.useanima.sh/api-reference/post-api-keys-rotate /openapi.json post /api-keys/{id}/rotate # Post billingchange plan Source: https://docs.useanima.sh/api-reference/post-billingchange-plan /openapi.json post /billing/change-plan # Post billingcheckout Source: https://docs.useanima.sh/api-reference/post-billingcheckout /openapi.json post /billing/checkout # Post billingcontact enterprise Source: https://docs.useanima.sh/api-reference/post-billingcontact-enterprise /openapi.json post /billing/contact-enterprise # Post billingportal Source: https://docs.useanima.sh/api-reference/post-billingportal /openapi.json post /billing/portal # Post demoinbox Source: https://docs.useanima.sh/api-reference/post-demoinbox /openapi.json post /demo/inbox # Post domains Source: https://docs.useanima.sh/api-reference/post-domains /openapi.json post /domains # Post domains verify Source: https://docs.useanima.sh/api-reference/post-domains-verify /openapi.json post /domains/{id}/verify # Post email forward Source: https://docs.useanima.sh/api-reference/post-email-forward /openapi.json post /email/{id}/forward # Post email reply Source: https://docs.useanima.sh/api-reference/post-email-reply /openapi.json post /email/{id}/reply # Post email rules Source: https://docs.useanima.sh/api-reference/post-email-rules /openapi.json post /email-rules # Post email rulesevaluate Source: https://docs.useanima.sh/api-reference/post-email-rulesevaluate /openapi.json post /email-rules/evaluate # Post emaildrafts Source: https://docs.useanima.sh/api-reference/post-emaildrafts /openapi.json post /email/drafts # Post emaildrafts send Source: https://docs.useanima.sh/api-reference/post-emaildrafts-send /openapi.json post /email/drafts/{id}/send # Post emailsend Source: https://docs.useanima.sh/api-reference/post-emailsend /openapi.json post /email/send # Post emailunsuppress Source: https://docs.useanima.sh/api-reference/post-emailunsuppress /openapi.json post /email/unsuppress # Post extensionconnect Source: https://docs.useanima.sh/api-reference/post-extensionconnect /openapi.json post /extension/connect # Post extensionexchange Source: https://docs.useanima.sh/api-reference/post-extensionexchange /openapi.json post /extension/exchange # Post extensionrevoke Source: https://docs.useanima.sh/api-reference/post-extensionrevoke /openapi.json post /extension/revoke # Post extensiontoken Source: https://docs.useanima.sh/api-reference/post-extensiontoken /openapi.json post /extension/token # Post feedback Source: https://docs.useanima.sh/api-reference/post-feedback /openapi.json post /feedback # Post identities Source: https://docs.useanima.sh/api-reference/post-identities /openapi.json post /identities # Post identityverify Source: https://docs.useanima.sh/api-reference/post-identityverify /openapi.json post /identity/verify # Post inboxes Source: https://docs.useanima.sh/api-reference/post-inboxes /openapi.json post /inboxes # Post mcp authsessions Source: https://docs.useanima.sh/api-reference/post-mcp-authsessions /openapi.json post /mcp-auth/sessions Deprecated: session-based MCP auth was replaced by the OAuth 2.0 Authorization Code + PKCE flow. Always returns 400. # Post mcp authsessions complete Source: https://docs.useanima.sh/api-reference/post-mcp-authsessions-complete /openapi.json post /mcp-auth/sessions/{sessionId}/complete Deprecated: session-based MCP auth was replaced by the OAuth 2.0 Authorization Code + PKCE flow. Always returns 400. # Post mcp authsessions deny Source: https://docs.useanima.sh/api-reference/post-mcp-authsessions-deny /openapi.json post /mcp-auth/sessions/{sessionId}/deny Deprecated: session-based MCP auth was replaced by the OAuth 2.0 Authorization Code + PKCE flow. Always returns 400. # Post mcp authsessionspoll Source: https://docs.useanima.sh/api-reference/post-mcp-authsessionspoll /openapi.json post /mcp-auth/sessions/poll Deprecated: session-based MCP auth was replaced by the OAuth 2.0 Authorization Code + PKCE flow. Always returns 400. # Post messages attachments Source: https://docs.useanima.sh/api-reference/post-messages-attachments /openapi.json post /messages/{messageId}/attachments # Post messages restore Source: https://docs.useanima.sh/api-reference/post-messages-restore /openapi.json post /messages/{id}/restore # Post messagesemail Source: https://docs.useanima.sh/api-reference/post-messagesemail /openapi.json post /messages/email # Post messagessearch Source: https://docs.useanima.sh/api-reference/post-messagessearch /openapi.json post /messages/search # Post messagessearchsemantic Source: https://docs.useanima.sh/api-reference/post-messagessearchsemantic /openapi.json post /messages/search/semantic # Post messagessms Source: https://docs.useanima.sh/api-reference/post-messagessms /openapi.json post /messages/sms # Post oauthapps Source: https://docs.useanima.sh/api-reference/post-oauthapps /openapi.json post /oauth/apps # Post oauthauth codesmint Source: https://docs.useanima.sh/api-reference/post-oauthauth-codesmint /openapi.json post /oauth/auth-codes/mint # Post oauthregister Source: https://docs.useanima.sh/api-reference/post-oauthregister /openapi.json post /oauth/register # Post oauthrevoke Source: https://docs.useanima.sh/api-reference/post-oauthrevoke /openapi.json post /oauth/revoke # Post oauthtoken Source: https://docs.useanima.sh/api-reference/post-oauthtoken /openapi.json post /oauth/token # Post openclawsignup Source: https://docs.useanima.sh/api-reference/post-openclawsignup /openapi.json post /openclaw/signup # Post orgs Source: https://docs.useanima.sh/api-reference/post-orgs /openapi.json post /orgs # Post orgs access reviews Source: https://docs.useanima.sh/api-reference/post-orgs-access-reviews /openapi.json post /orgs/{orgId}/access-reviews # Post orgs access reviews complete Source: https://docs.useanima.sh/api-reference/post-orgs-access-reviews-complete /openapi.json post /orgs/{orgId}/access-reviews/{reviewId}/complete # Post orgs agents quarantine Source: https://docs.useanima.sh/api-reference/post-orgs-agents-quarantine /openapi.json post /orgs/{orgId}/agents/{agentId}/quarantine # Post orgs anomaly alerts acknowledge Source: https://docs.useanima.sh/api-reference/post-orgs-anomaly-alerts-acknowledge /openapi.json post /orgs/{orgId}/anomaly-alerts/{alertId}/acknowledge # Post orgs anomaly alerts false positive Source: https://docs.useanima.sh/api-reference/post-orgs-anomaly-alerts-false-positive /openapi.json post /orgs/{orgId}/anomaly-alerts/{alertId}/false-positive # Post orgs anomaly alerts resolve Source: https://docs.useanima.sh/api-reference/post-orgs-anomaly-alerts-resolve /openapi.json post /orgs/{orgId}/anomaly-alerts/{alertId}/resolve # Post orgs anomaly rules Source: https://docs.useanima.sh/api-reference/post-orgs-anomaly-rules /openapi.json post /orgs/{orgId}/anomaly-rules # Post orgs audit logsexport Source: https://docs.useanima.sh/api-reference/post-orgs-audit-logsexport /openapi.json post /orgs/{orgId}/audit-logs/export # Post orgs claim Source: https://docs.useanima.sh/api-reference/post-orgs-claim /openapi.json post /orgs/{id}/claim # Post orgs compliancecontrols collect Source: https://docs.useanima.sh/api-reference/post-orgs-compliancecontrols-collect /openapi.json post /orgs/{orgId}/compliance/controls/{controlId}/collect # Post orgs compliancecontrols evidence Source: https://docs.useanima.sh/api-reference/post-orgs-compliancecontrols-evidence /openapi.json post /orgs/{orgId}/compliance/controls/{controlId}/evidence # Post orgs compliancedsars Source: https://docs.useanima.sh/api-reference/post-orgs-compliancedsars /openapi.json post /orgs/{orgId}/compliance/dsars # Post orgs compliancereports Source: https://docs.useanima.sh/api-reference/post-orgs-compliancereports /openapi.json post /orgs/{orgId}/compliance/reports # Post orgs compliancereports export Source: https://docs.useanima.sh/api-reference/post-orgs-compliancereports-export /openapi.json post /orgs/{orgId}/compliance/reports/{reportId}/export # Post orgs complianceseed Source: https://docs.useanima.sh/api-reference/post-orgs-complianceseed /openapi.json post /orgs/{orgId}/compliance/seed # Post orgs messages approve Source: https://docs.useanima.sh/api-reference/post-orgs-messages-approve /openapi.json post /orgs/{orgId}/messages/{messageId}/approve # Post orgs rotate key Source: https://docs.useanima.sh/api-reference/post-orgs-rotate-key /openapi.json post /orgs/{id}/rotate-key # Post orgsfeature interest Source: https://docs.useanima.sh/api-reference/post-orgsfeature-interest /openapi.json post /orgs/feature-interest # Post phoneprovision Source: https://docs.useanima.sh/api-reference/post-phoneprovision /openapi.json post /phone/provision # Post phonerelease Source: https://docs.useanima.sh/api-reference/post-phonerelease /openapi.json post /phone/release # Post phonesend sms Source: https://docs.useanima.sh/api-reference/post-phonesend-sms /openapi.json post /phone/send-sms # Post phonesms unsuppress Source: https://docs.useanima.sh/api-reference/post-phonesms-unsuppress /openapi.json post /phone/sms-unsuppress # Post provisioning requests Source: https://docs.useanima.sh/api-reference/post-provisioning-requests /openapi.json post /provisioning-requests # Post provisioning requests approve Source: https://docs.useanima.sh/api-reference/post-provisioning-requests-approve /openapi.json post /provisioning-requests/{requestId}/approve # Post provisioning requests cancel Source: https://docs.useanima.sh/api-reference/post-provisioning-requests-cancel /openapi.json post /provisioning-requests/{requestId}/cancel # Post provisioning requests decline Source: https://docs.useanima.sh/api-reference/post-provisioning-requests-decline /openapi.json post /provisioning-requests/{requestId}/decline # Post registryagents Source: https://docs.useanima.sh/api-reference/post-registryagents /openapi.json post /registry/agents # Post scoped tokens Source: https://docs.useanima.sh/api-reference/post-scoped-tokens /openapi.json post /scoped-tokens # Post scoped tokensrevoke Source: https://docs.useanima.sh/api-reference/post-scoped-tokensrevoke /openapi.json post /scoped-tokens/revoke # Post vaultcredential requests Source: https://docs.useanima.sh/api-reference/post-vaultcredential-requests /openapi.json post /vault/credential-requests # Post vaultcredential requests cancel Source: https://docs.useanima.sh/api-reference/post-vaultcredential-requests-cancel /openapi.json post /vault/credential-requests/{requestId}/cancel # Post vaultcredentials Source: https://docs.useanima.sh/api-reference/post-vaultcredentials /openapi.json post /vault/credentials # Post vaultcredentials use Source: https://docs.useanima.sh/api-reference/post-vaultcredentials-use /openapi.json post /vault/credentials/{id}/use # Post vaultdeprovision Source: https://docs.useanima.sh/api-reference/post-vaultdeprovision /openapi.json post /vault/deprovision # Post vaultgenerate password Source: https://docs.useanima.sh/api-reference/post-vaultgenerate-password /openapi.json post /vault/generate-password # Post vaultoauthapps custom Source: https://docs.useanima.sh/api-reference/post-vaultoauthapps-custom /openapi.json post /vault/oauth/apps/{appSlug}/custom # Post vaultoauthlink Source: https://docs.useanima.sh/api-reference/post-vaultoauthlink /openapi.json post /vault/oauth/link # Post vaultoauthrequire auth Source: https://docs.useanima.sh/api-reference/post-vaultoauthrequire-auth /openapi.json post /vault/oauth/require-auth # Post vaultprovision Source: https://docs.useanima.sh/api-reference/post-vaultprovision /openapi.json post /vault/provision # Post vaultshare Source: https://docs.useanima.sh/api-reference/post-vaultshare /openapi.json post /vault/share # Post vaultsharerevoke Source: https://docs.useanima.sh/api-reference/post-vaultsharerevoke /openapi.json post /vault/share/revoke # Post vaultsync Source: https://docs.useanima.sh/api-reference/post-vaultsync /openapi.json post /vault/sync # Post vaulttoken Source: https://docs.useanima.sh/api-reference/post-vaulttoken /openapi.json post /vault/token # Post vaulttokenexchange Source: https://docs.useanima.sh/api-reference/post-vaulttokenexchange /openapi.json post /vault/token/exchange # Post vaulttokenrevoke Source: https://docs.useanima.sh/api-reference/post-vaulttokenrevoke /openapi.json post /vault/token/revoke # Post voicecalls Source: https://docs.useanima.sh/api-reference/post-voicecalls /openapi.json post /voice/calls # Post voicesearch Source: https://docs.useanima.sh/api-reference/post-voicesearch /openapi.json post /voice/search # Post voicesearchcross channel Source: https://docs.useanima.sh/api-reference/post-voicesearchcross-channel /openapi.json post /voice/search/cross-channel # Post webhooks Source: https://docs.useanima.sh/api-reference/post-webhooks /openapi.json post /webhooks # Post webhooks reenable Source: https://docs.useanima.sh/api-reference/post-webhooks-reenable /openapi.json post /webhooks/{id}/reenable # Post webhooks rotate secret Source: https://docs.useanima.sh/api-reference/post-webhooks-rotate-secret /openapi.json post /webhooks/{id}/rotate-secret # Post webhooks test Source: https://docs.useanima.sh/api-reference/post-webhooks-test /openapi.json post /webhooks/{id}/test # Post webhooksdeliveries replay Source: https://docs.useanima.sh/api-reference/post-webhooksdeliveries-replay /openapi.json post /webhooks/deliveries/{deliveryId}/replay # Put addresses Source: https://docs.useanima.sh/api-reference/put-addresses /openapi.json put /addresses/{id} # Put agents policy Source: https://docs.useanima.sh/api-reference/put-agents-policy /openapi.json put /agents/{agentId}/policy # Put billingoverage Source: https://docs.useanima.sh/api-reference/put-billingoverage /openapi.json put /billing/overage # Put registryagents Source: https://docs.useanima.sh/api-reference/put-registryagents /openapi.json put /registry/agents/{did} # Put vaultcredentials Source: https://docs.useanima.sh/api-reference/put-vaultcredentials /openapi.json put /vault/credentials/{id} # Put webhooks Source: https://docs.useanima.sh/api-reference/put-webhooks /openapi.json put /webhooks/{id} # How We Built Cryptographic Identity for AI Agents with DIDs Source: https://docs.useanima.sh/blog/agent-identity-did A technical deep dive into Anima's agent identity: standard did:web Decentralized Identifiers, world-readable DID documents, and signed agent-to-agent requests. # How We Built Cryptographic Identity for AI Agents with DIDs > **Updated 2026-07:** Anima's DID method migrated from a custom `did:anima:` method to the standard [`did:web:`](https://w3c-ccg.github.io/did-method-web/) method, and this post has been updated to match what is shipped today. Where a capability is still on the roadmap (automatic credential issuance), it says so. When an AI agent sends an email, makes a purchase, or calls an API, the receiving party has a reasonable question: **who is this, and should I trust it?** Today, most agents authenticate with API keys or OAuth tokens. These prove authorization, not identity. They say "this request is allowed" but not "this is agent X, operated by company Y, with these specific capabilities and constraints." That distinction matters as agents start interacting with each other, with vendors, and with compliance systems. We use the W3C-standard `did:web` method to solve this. ## Why Agents Need Verifiable Identity There are three forces pushing agents toward cryptographic identity: ### 1. Agent-to-Agent Trust When agent A contacts agent B to negotiate a contract or request a service, B needs to verify that A is who it claims to be. API keys do not help here — they authenticate a request to a specific service, not an identity across services. DIDs provide a portable, verifiable identity that works across any protocol. ### 2. Compliance and Accountability Regulators are catching up to agentic systems. SOC 2 auditors want to know which agent performed which action and under whose authority. AML rules require knowing the identity behind financial transactions. A DID ties every action to a verifiable identity with a clear chain of custody. ### 3. Discovery and Interoperability As the agent ecosystem grows, services need to discover agent capabilities and verify their legitimacy. The combination of DIDs and Agent Cards gives every agent a machine-readable, cryptographically verifiable profile that other systems can query. ## Standard `did:web`, not a custom method Every agent created on Anima is automatically assigned a DID following the W3C Decentralized Identifiers specification, using the registered `did:web` method: ``` did:web:agents.useanima.sh:: ``` We deliberately chose `did:web` over inventing a custom method: `did:web` resolution is plain HTTPS, already supported by standard resolver tooling, with nothing bespoke to implement on the verifier side. The DID resolves to a DID Document containing the agent's public key and authentication methods: ```json theme={null} { "@context": [ "https://www.w3.org/ns/did/v1", "https://w3id.org/security/suites/jws-2020/v1" ], "id": "did:web:agents.useanima.sh:cmb1x9k2l0000abcd:cmb1xa3f50001abcd", "verificationMethod": [ { "id": "did:web:agents.useanima.sh:cmb1x9k2l0000abcd:cmb1xa3f50001abcd#key-1", "type": "JsonWebKey2020", "controller": "did:web:agents.useanima.sh:cmb1x9k2l0000abcd:cmb1xa3f50001abcd", "publicKeyJwk": { "kty": "OKP", "crv": "Ed25519", "x": "…" } } ], "authentication": ["did:web:agents.useanima.sh:cmb1x9k2l0000abcd:cmb1xa3f50001abcd#key-1"], "assertionMethod": ["did:web:agents.useanima.sh:cmb1x9k2l0000abcd:cmb1xa3f50001abcd#key-1"] } ``` ### Key Design Decisions **Ed25519 keys**: We use Ed25519 for signing because it is fast, produces compact signatures, and is widely supported across platforms. Each agent gets a dedicated key pair at creation time — and the private key is stored encrypted at rest. **World-readable documents**: DID documents must be verifiable by parties who cannot authenticate to Anima, so they are served publicly with open CORS. **Automatic provisioning**: You do not need to manage keys or DID documents manually. Creating an agent automatically provisions the DID, generates keys, and publishes the document. ## Resolving and Verifying DIDs Any system can fetch an Anima agent's DID document — no API key required: ```bash theme={null} curl https://api.useanima.sh/.well-known/did// ``` The document is served as `application/did+json`. Authenticated callers can also fetch it through the API: ```bash theme={null} curl https://api.useanima.sh/v1/agents//did \ -H "Authorization: Bearer ak_..." ``` This is what powers [signed A2A dispatch](/a2a/overview): Anima signs an outbound task with the sender's key, and the recipient verifies that signature against the sender's published DID document before accepting it. ## Verifiable Credentials DIDs establish identity. Verifiable Credentials (VCs) establish **attributes** of that identity — "this agent's email is verified", "this org passed billing verification". Anima's identity layer verifies and revokes W3C VCs today (`POST /v1/identity/verify` checks signature, expiry, and revocation of a JWT VC). **Automatic credential issuance has not shipped yet** — agents don't accumulate credentials on real events (email verified, phone provisioned) until that lands, and Agent Cards honestly report `verification.level: "basic"` in the meantime. See [Verifiable Credentials](/identity/verifiable-credentials) for the current status. ## Agent Cards: machine-readable agent profiles Every Anima agent has an [Agent Card](/identity/agent-cards) — a machine-readable JSON profile generated from its live state: ```json theme={null} { "name": "Acme Purchasing Agent", "did": "did:web:agents.useanima.sh:cmb1x9k2l0000abcd:cmb1xa3f50001abcd", "capabilities": { "email": true, "phone": true, "vault": true, "address": false, "protocols": ["a2a"] }, "contact": { "email": "purchasing@agents.useanima.sh" } } ``` Agent Cards serve the same role for agents that OpenAPI specs serve for APIs: a machine-readable description of what this agent can do and how to reach it. The `capabilities` block is derived from what the agent actually has provisioned — not self-declared. ## Agent Registry For discovery across your organization, Anima runs an Agent Registry — register your agent, and other agents can find it by capability or DID: ```ts theme={null} // Register an agent in the registry await anima.registry.register({ agentId: agent.id, capabilities: ["procurement", "invoice-processing"], }); // Discover agents by capability const { items } = await anima.registry.search("invoice-processing"); ``` ## Putting It All Together Here is the flow when Agent A wants to hand a task to Agent B: ```mermaid theme={null} sequenceDiagram participant A as Agent A participant Registry as Anima Registry participant B as Agent B A->>Registry: Search for invoice-processing agents Registry-->>A: Agent B (did:web:…) A->>B: Fetch Agent Card B-->>A: Card (capabilities, DID, contact) A->>B: Signed A2A task (Anima signs with A's key) B->>B: Resolve A's DID document, verify signature B-->>A: Task accepted → status updates ``` Every step is verifiable. Every identity is cryptographic. No shared secrets between agents. ## What This Enables * **Signature-verified agent-to-agent tasks**: recipients verify the sender's signature against its published DID document before accepting work * **Compliance reporting**: every action is tied to an identity with a clear [audit trail](/security/audit-log) * **Standards-based portability**: `did:web` is a registered W3C method — verifiers need no Anima-specific code * **Key rotation and revocation**: rotate an agent's keypair (`POST /v1/agents/{agentId}/did/rotate`) or revoke credentials at any time ## Start Building Agent identity is not optional anymore. As agents handle real money, real data, and real decisions, verifiable identity becomes infrastructure. ```ts theme={null} const agent = await anima.agents.create({ name: "My Agent" }); // That's it — the agent's did:web identity and DID document are provisioned automatically. ``` [Read the DID method docs](/identity/did-method) | [Explore Verifiable Credentials](/identity/verifiable-credentials) | [Set up Agent Cards](/identity/agent-cards) # Anima vs AgentMail: Why You Need More Than Just Email Source: https://docs.useanima.sh/blog/anima-vs-agentmail AgentMail provides excellent email for AI agents. Anima provides email plus phone, voice, vault, and cryptographic identity. Here is an honest comparison. # Anima vs AgentMail: Why You Need More Than Just Email AgentMail does one thing and does it well: email for AI agents. If all your agent needs is to send and receive email, AgentMail is a solid choice — and this page will say so plainly. But most production agents need more than an inbox. > **How this comparison works.** Every claim about AgentMail cites their public documentation with the date we checked it (2026-07-16). Where their docs don't document something, we say "not documented" rather than "no". Where AgentMail is ahead of us, we say that too. ## The Problem with Email-Only Consider a customer support agent. It receives a complaint via email, looks up the order, sends an SMS confirmation, and logs into the merchant portal to update the ticket. That workflow touches: * **Email** (receive complaint, send resolution) * **Phone** (send SMS confirmation) * **Vault** (store merchant portal credentials) * **Identity** (prove which agent did what, sign agent-to-agent requests) With AgentMail, you get step one. The rest requires separate vendors, separate SDKs, and separate failure modes. ## Feature Comparison Checked against [docs.agentmail.to](https://docs.agentmail.to) and [agentmail.to](https://agentmail.to) on **2026-07-16**. | Capability | AgentMail | Anima | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------ | | Agent email inboxes | Yes | Yes | | Custom domains | Yes ([docs](https://docs.agentmail.to/custom-domains), 2026-07-16) | Yes | | Email threading | Yes ([docs](https://docs.agentmail.to/threads), 2026-07-16) | Yes — RFC 5322 `Message-ID`/`References` threading on the wire, so replies thread in Gmail/Outlook too | | Drafts | Yes ([docs](https://docs.agentmail.to/drafts), 2026-07-16) | Yes | | Labels / message state | Yes ([docs](https://docs.agentmail.to/labels), 2026-07-16) | **Not yet** — on the roadmap | | Inbound webhooks | Yes ([docs](https://docs.agentmail.to/webhooks-overview), 2026-07-16) | Yes (signed, with delivery stats + retries) | | Semantic search over mail | Not documented (docs.agentmail.to, 2026-07-16) | Yes — `POST /v1/messages/search/semantic` (vector search, REST) | | Phone numbers (SMS) | No — email-only product ([agentmail.to](https://agentmail.to), 2026-07-16) | Yes | | Voice calls + transcripts | No — email-only product (same source) | Yes | | Encrypted credential vault | No (not offered; same source) | Yes (encrypted, server-side "use-not-see" broker) | | Browser extension for logins | No (not offered; same source) | Yes (Chrome Web Store: Anima Vault) | | DID-based identity | No (not offered; same source) | Yes (standard `did:web`, world-readable DID documents) | | Signed agent-to-agent tasks | No (not offered; same source) | Yes — between Anima agents, DID-signature-verified | | MCP server | Yes ([docs](https://docs.agentmail.to/integrations/mcp), 2026-07-16) | Yes — hosted `mcp.useanima.sh/mcp`, 65 tools | | SDKs | Python, TypeScript ([agentmail.to](https://agentmail.to), 2026-07-16) | Node, Python, Go + CLI | | SOC 2 | **Type I + Type II** ([docs](https://docs.agentmail.to/documentation/resources/security-privacy/soc-2-compliance), 2026-07-16) | Not yet certified — audit logging, agent quarantine, and content scanning are built in | Two honest reads of that table: if you need labels or a SOC 2 report **today**, AgentMail is ahead. If your agent touches anything beyond email — a phone number, a login, a signature — Anima replaces a stack of vendors. ## When to Use AgentMail AgentMail is a reasonable choice if: * Your agent only sends and receives email * You need a SOC 2 Type II report today * You are building a prototype and email is the only channel * You prefer a narrowly scoped vendor for email specifically ## When to Use Anima Anima is the right choice when: * Your agent operates across multiple channels (email + SMS + voice) * Your agent signs into things — the vault stores credentials agents can *use but never see* * You want cryptographic agent identity (`did:web`) and signed agent-to-agent tasks * You want one SDK and one audit log across every channel ## Code Comparison ### AgentMail: Send an email ```ts theme={null} // AgentMail — email only import { AgentMail } from "agentmail"; const client = new AgentMail({ apiKey: "am_..." }); await client.send({ from: "agent@myco.com", to: "user@example.com", subject: "Your order update", body: "Your order has shipped.", }); ``` ### Anima: Full agent workflow ```ts theme={null} // Anima — email + SMS + vault in one SDK import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: "ak_..." }); const agent = await anima.agents.create({ name: "Support Agent" }); // Send email await anima.messages.sendEmail({ agentId: agent.id, to: ["user@example.com"], subject: "Your refund has been processed", body: "We've issued a $45.00 refund to your original payment method.", }); // Send SMS confirmation await anima.messages.sendSms({ agentId: agent.id, to: "+15551234567", body: "Refund of $45.00 processed. Check your email for details.", }); // Store the case notes for follow-up await anima.vault.createCredential({ agentId: agent.id, type: "secure_note", name: "Case #4821 — Refund details", notes: JSON.stringify({ orderId: "ORD-9921", refundAmount: 4500, }), }); ``` Same agent. Same API key. No glue code between vendors. ## The Unified Identity Advantage The deeper issue with using AgentMail alongside other point solutions is identity fragmentation. Your agent has one identity in AgentMail, another in your phone service. There is no single source of truth for "who is this agent and what is it authorized to do?" With Anima, every agent gets a standard [`did:web`](/identity/did-method) identifier that spans all capabilities. Security policy and [quarantine](/security/anomaly-detection) apply across email, SMS, and voice from a single configuration. Audit logs capture every action across every channel in one place. This matters for compliance. When an auditor asks "what did agent X do last month?", you query one system — not five. ## Migration Path If you are already on AgentMail and want to move to Anima: 1. Install the Anima SDK alongside AgentMail 2. Create agents in Anima and set up custom domains 3. Migrate email sending calls from AgentMail's API to `anima.messages.sendEmail()` — the attachment shape is modeled on AgentMail's, so payloads port with minimal changes 4. Add phone and vault capabilities as needed 5. Remove the AgentMail dependency The main structural difference: Anima scopes everything to a unified agent identity via `agentId`. ## Bottom Line AgentMail is genuinely good at email — better than most, and ahead of us on labels and certification today. But production agents do not just send email — they text, call, authenticate, and act. Anima provides the full stack so you can build agents that operate in the real world without stitching together five services. [Get started with Anima](/getting-started) | [See the full SDK reference](/sdks) # Give Your AI Agent a Phone Number in 5 Minutes Source: https://docs.useanima.sh/blog/give-agent-phone-number Step-by-step tutorial: provision a US phone number for your AI agent, send SMS, place a first voice call, and handle inbound events. # Give Your AI Agent a Phone Number in 5 Minutes Your AI agent can send email, but some workflows need a phone path too: urgent updates, appointment reminders, SMS replies, voice confirmation, and verification flows. This tutorial provisions a number, sends a real SMS, places a first voice call, and wires inbound events to a webhook. ## Prerequisites * An Anima API key from [console.useanima.sh](https://console.useanima.sh) * Python 3.10+ or Node.js 18+ * An existing `agent_id` * Phone access for SMS * Voice access if you want to place the call step * Consent to contact the destination number Use your own phone number for the first run. ## 1. Install the SDK ```bash theme={null} npm install @anima-labs/sdk ``` ```bash theme={null} pip install anima-labs ``` ## 2. Search for available numbers ```ts theme={null} import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: "ak_..." }); const agentId = "AGENT_ID"; const numbers = await anima.phones.search({ countryCode: "US", areaCode: "415", capabilities: ["sms", "voice"], limit: 3, }); for (const number of numbers.items) { console.log(number.phoneNumber, number.region); } ``` ```python theme={null} from anima import Anima anima = Anima(api_key="ak_...") agent_id = "AGENT_ID" numbers = anima.phones.search( country_code="US", area_code="415", capabilities=["sms", "voice"], limit=3, ) for number in numbers["items"]: print(number["phoneNumber"], number.get("region")) ``` ## 3. Provision the number ```ts theme={null} const phone = await anima.phones.provision({ agentId, countryCode: "US", areaCode: "415", capabilities: ["sms", "voice"], }); console.log(`Provisioned: ${phone.phoneNumber}`); ``` ```python theme={null} phone = anima.phones.provision( agent_id=agent_id, country_code="US", area_code="415", capabilities=["sms", "voice"], ) print(f"Provisioned: {phone.phone_number}") ``` The number is linked to the agent's unified identity: the same `agent_id` used for email, vault, DID, audit logs, and webhooks. ## 4. Text yourself ```ts theme={null} const sms = await anima.messages.sendSms({ agentId, to: "+15551234567", body: "Hi - this is my Anima agent texting from its own number.", }); console.log(`SMS sent: ${sms.id} (${sms.status})`); ``` ```python theme={null} sms = anima.messages.send_sms( agent_id=agent_id, to="+15551234567", body="Hi - this is my Anima agent texting from its own number.", ) print(f"SMS sent: {sms.id} ({sms.status})") ``` ## 5. Call yourself Outbound voice calls run through the voice gate before dialing. Only call recipients where you have the required consent. ```ts theme={null} const call = await anima.calls.create({ agentId, to: "+15551234567", tier: "basic", greeting: "Hi, this is my Anima agent. I am calling from my own phone number.", }); console.log(`Call started: ${call.callId}, state: ${call.state}`); ``` ```python theme={null} call = anima.calls.create( agent_id=agent_id, to="+15551234567", tier="basic", greeting="Hi, this is my Anima agent. I am calling from my own phone number.", ) print(f"Call started: {call.call_id}, state: {call.state}") ``` After the call ends, fetch the transcript: ```ts theme={null} const transcript = await anima.calls.getTranscript(call.callId); for (const segment of transcript.segments) { console.log(`${segment.speaker}: ${segment.text}`); } ``` ```python theme={null} transcript = anima.calls.get_transcript(call.call_id) for segment in transcript.segments: print(f"{segment.speaker}: {segment.text}") ``` ## 6. Handle inbound events Inbound SMS arrives as `message.received`; inspect the message channel/payload to distinguish SMS from email. Call lifecycle events use the `call.*` namespace. ```bash theme={null} curl -X POST https://api.useanima.sh/v1/webhooks \ -H "Authorization: Bearer ak_..." \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-app.example.com/webhooks/anima", "events": ["message.received", "phone.provisioned", "call.ended"] }' ``` Example handler: ```ts theme={null} app.post("/webhooks/anima", (req, res) => { const event = req.body; if (event.type === "message.received") { console.log("Inbound message:", event.data); } if (event.type === "call.ended") { console.log("Call ended:", event.data.callId); } res.status(200).send("ok"); }); ``` ## 10DLC and voice compliance For US SMS, 10DLC registration may be required depending on your use case and volume. Anima tracks phone identity status and carrier capability flags with the provisioned number. For US outbound voice calls, Anima enforces plan eligibility, your organization's TCPA consent attestation, per-tier and per-second call caps, and the voice spend ceiling server-side. If a gate fails, the call is rejected before the provider dials. Reassigned-number scrubbing, Do-Not-Call scrubbing, and calling-hour windows are not enforced by the platform — those obligations stay with you. ## What makes this different The phone number is not a disconnected CPaaS resource. It is part of the agent identity: * Same `agent_id` across email, SMS, voice, vault, and DID * Same policy and audit surface across channels * Same webhook system for inbound and lifecycle events * Same vault-backed credential boundary when the agent needs to act after the conversation [Read the Phone docs](/phone) | [Set up webhooks](/webhooks) | [Get started](/getting-started) # Introducing Anima: The Unified Identity Platform for AI Agents Source: https://docs.useanima.sh/blog/introducing-anima Anima gives AI agents real-world identity — email, phone, encrypted vault, and cryptographic identity — in a single API. # Introducing Anima: The Unified Identity Platform for AI Agents AI agents are crossing from demos into production. They book travel, manage procurement, handle customer support, and negotiate with vendors. But to do any of that in the real world, an agent needs more than an LLM and a prompt. It needs **identity**. ## The Identity Gap Think about what a human employee gets on day one: a work email, a phone extension, access credentials, and an ID badge. An AI agent doing the same job needs the same things — and until now, you had to stitch them together yourself. The current landscape looks like this: * **Email**: One vendor gives you agent inboxes * **Phone**: Another gives you programmable numbers * **Credentials**: You roll your own secrets management * **Identity**: You hope nobody asks "which agent did that?" Each integration has its own SDK, its own auth model, its own billing, and its own failure modes. You end up building an identity layer from scratch every time you deploy an agent. ## One Platform. One API. One Identity. Anima collapses the entire stack into a single platform. Every agent gets a unified identity that spans: * **Email** — Real inboxes on custom domains with inbound/outbound, content scanning, and webhooks * **Phone** — US phone numbers with SMS and voice, with 10DLC compliance * **Vault** — Encrypted storage for logins, API keys, and secrets * **Identity** — W3C DIDs, Verifiable Credentials, and Agent Cards for discovery and trust All bound to a single `agent_id`. All managed through one SDK. ## What This Looks Like in Code Here is a complete agent setup — email, phone, vault, and identity — in a single script: ```ts theme={null} import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: "ak_..." }); // Create the agent const agent = await anima.agents.create({ name: "Procurement Agent" }); // Email: send from a real inbox await anima.messages.send_email({ agentId: agent.id, to: "vendor@example.com", subject: "PO #1042 — Confirm delivery date", body: "Please confirm the expected delivery for PO #1042.", }); // Phone: provision a number and send SMS await anima.phones.provision({ agentId: agent.id, countryCode: "US", capabilities: ["sms", "voice"], }); await anima.messages.sendSms({ agentId: agent.id, to: "+15551234567", body: "Your order has shipped. Track at https://example.com/track/1042", }); // Vault: store vendor credentials await anima.vault.provision({ agentId: agent.id }); await anima.vault.createCredential({ agentId: agent.id, type: "login", name: "Vendor Portal", username: "procurement-bot", password: "rotated-secret", uris: ["https://vendor.example.com/login"], }); // Identity: every agent gets a did:web DID + world-readable DID document // GET https://api.useanima.sh/.well-known/did/{orgId}/{agentId} console.log(agent.did); // did:web:agents.useanima.sh:… ``` That is one SDK, one API key, one agent ID. No glue code. No multi-vendor orchestration. ## Key Differentiators ### Cryptographic Identity (DIDs) Every Anima agent gets a standard `did:web` decentralized identifier. This is not just a database row — it is a W3C-compliant DID with a world-readable DID document and signing keys. Agents prove who they are to other agents and services via signed A2A requests. ### Policy Engine Every action goes through a configurable policy engine. You define rules like "never spend more than \$50 per transaction" or "only send emails to @acme.com domains." Policies are enforced at the platform level, not in your application code. ### SOC 2 Controls Anima is built for production. Rule-based content + injection scanning on every inbound and outbound message, API key scoping (master keys vs. agent keys), encrypted vault storage, and full audit logging. ## SDKs and Tools Anima ships SDKs for the ecosystems where agents run: | SDK | Install | Status | | ---------- | ---------------------------------------------- | ------ | | Python | `pip install anima-labs` | GA | | Node.js | `npm install @anima-labs/sdk` | GA | | Go | `go get github.com/anima-labs-ai/go` | GA | | CLI | `npm install -g @anima-labs/cli` | GA | | MCP Server | 53 tools for Claude, GPT, and other LLM agents | GA | The MCP server is worth highlighting: it exposes every Anima capability as a tool that LLM agents can call directly. Your agent does not need custom integration code — it discovers and uses Anima through the Model Context Protocol. ## Who Is This For? * **Agent developers** who need production-ready identity primitives without building them from scratch * **Enterprises** deploying agents that must comply with SOC 2, AML, and audit requirements * **AI-native startups** building products where agents transact, communicate, and authenticate autonomously ## Get Started Anima is available today. You can be up and running in under 5 minutes: 1. Sign up at [console.useanima.sh](https://console.useanima.sh) 2. Install the SDK: `pip install anima-labs` or `npm install @anima-labs/sdk` 3. Create your first agent and send an email — see the [Quickstart](/quickstart-email) We are building the identity layer for the agentic internet. If you are building agents that need to operate in the real world, we would love to hear from you. [Star us on GitHub](https://github.com/anima-labs) | [Join the Discord](https://discord.gg/anima) | [Read the Docs](/getting-started) # Call Intelligence Source: https://docs.useanima.sh/call-intelligence Read call records, stream live transcription, fetch transcripts, and use post-call intelligence endpoints. # Call Intelligence Anima stores voice call records under the same agent identity as email, SMS, and vault events. Use call intelligence to monitor calls while they run, fetch the finalized transcript after they end, and read post-call artifacts such as summaries, scores, recordings, and security scans when enabled for your account. ## Start a call ```python theme={null} from anima import Anima anima = Anima(api_key="ak_...") call = anima.calls.create( agent_id="AGENT_ID", to="+15551234567", greeting="Hi, this is my Anima agent. I am calling from my own number.", ) print(call.call_id, call.state) ``` ```ts theme={null} import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: "ak_..." }); const call = await anima.calls.create({ agentId: "AGENT_ID", to: "+15551234567", greeting: "Hi, this is my Anima agent. I am calling from my own number.", }); console.log(call.callId, call.state); ``` ## Stream live transcription Use the voice WebSocket for live call events and bidirectional control. ```python theme={null} conn = anima.calls.connect(agent_id="AGENT_ID") def on_message(message: dict): if message.get("type") == "call.transcription": payload = message.get("data", message) print(payload.get("text")) conn.on_message(on_message) conn.create_call( "+15551234567", greeting="Hi, this is my Anima agent.", ) ``` ```ts theme={null} const conn = anima.calls.connect({ agentId: "AGENT_ID" }); conn.on("message", (message) => { if (message.type === "call.transcription") { console.log(message.data?.text); } }); conn.createCall("+15551234567", { greeting: "Hi, this is my Anima agent.", }); ``` ## Fetch the transcript After the call ends, fetch the finalized transcript. ```python theme={null} transcript = anima.calls.get_transcript(call.call_id) for segment in transcript.segments: print(f"[{segment.start_time}s] {segment.speaker}: {segment.text}") ``` ```ts theme={null} const transcript = await anima.calls.getTranscript(call.callId); for (const segment of transcript.segments) { console.log(`[${segment.startTime}s] ${segment.speaker}: ${segment.text}`); } ``` ## List and inspect calls ```python theme={null} calls = anima.calls.list(agent_id="AGENT_ID", limit=20) for item in calls["calls"]: print(item.id, item.state, item.duration_seconds) detail = anima.calls.get(call.call_id) print(detail.state, detail.started_at, detail.ended_at) ``` ```ts theme={null} const calls = await anima.calls.list({ agentId: "AGENT_ID", limit: 20 }); for (const item of calls.calls) { console.log(item.id, item.state, item.durationSeconds); } const detail = await anima.calls.get(call.callId); console.log(detail.state, detail.startedAt, detail.endedAt); ``` ## Post-call intelligence endpoints Some post-call artifacts are exposed as REST endpoints and MCP tools even when a specific SDK helper is not present yet. ```bash theme={null} curl https://api.useanima.sh/v1/voice/calls/CALL_ID/transcript \ -H "Authorization: Bearer ak_..." curl https://api.useanima.sh/v1/voice/calls/CALL_ID/summary \ -H "Authorization: Bearer ak_..." curl https://api.useanima.sh/v1/voice/calls/CALL_ID/score \ -H "Authorization: Bearer ak_..." curl https://api.useanima.sh/v1/voice/calls/CALL_ID/recording \ -H "Authorization: Bearer ak_..." curl https://api.useanima.sh/v1/voice/calls/CALL_ID/security \ -H "Authorization: Bearer ak_..." ``` MCP equivalents include `phone_call_get`, `phone_call_transcript_get`, `phone_call_recording_get`, `voice_get_summary`, `voice_get_score`, and `voice_get_security_scan` depending on the connected MCP server version. ## Guardrails Outbound calls are gated server-side before dialing. The API enforces plan eligibility, your organization's TCPA consent attestation, per-tier and per-second call caps, and the voice spend ceiling. If one of those checks fails, the call is rejected before reaching the telephony provider. Reassigned-number scrubbing, Do-Not-Call scrubbing, and calling-hour windows are **not** enforced by Anima and remain yours. See [Compliance guardrails](/phone#compliance-guardrails). ## Next steps * [Voice Catalog](/voice-catalog) - Browse the multilingual voice catalog and preview a voice * [Voice WebSocket Protocol](/protocols/voice-websocket) - Full real-time protocol reference * [Phone & Voice](/phone) - Provision numbers, send SMS, and place calls # Compliance Reporting Source: https://docs.useanima.sh/compliance/reporting Generate compliance reports, handle DSAR requests, and monitor compliance posture through dashboards and templates. # Compliance Reporting Anima provides built-in reporting tools for compliance workflows including SOC 2 audit preparation, DSAR (Data Subject Access Request) fulfillment, and executive compliance dashboards. These are tools for preparing **your** audit. They do not constitute an attestation of Anima's own compliance posture, and generating a report here is not evidence that Anima has been audited. ## Report Templates Generate pre-built reports for common compliance needs: ```ts theme={null} import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: "mk_..." }); // Generate a SOC 2 summary. Compliance calls are org-scoped: the org id // is the first argument, not a field on the body. const report = await anima.compliance.generateReport(orgId, { type: "SOC2_SUMMARY", periodStart: "2026-01-01", periodEnd: "2026-03-31", }); console.log(`Report: ${report.id}`); console.log(`Status: ${report.status}`); // PENDING | GENERATING | COMPLETED | FAILED // Once it completes, export it. The bytes come back inline — there is no // signed download URL. const exported = await anima.compliance.exportReport(orgId, report.id, { format: "PDF" }); console.log(`${exported.filename} (${exported.contentType})`); ``` ```python theme={null} from anima import Anima anima = Anima(api_key="mk_...") report = anima.compliance.generate_report( org_id=org_id, type="SOC2_SUMMARY", period_start="2026-01-01", period_end="2026-03-31", ) print(f"Report: {report.id}") print(f"Status: {report.status}") exported = anima.compliance.export_report(org_id=org_id, report_id=report.id, format="PDF") print(f"{exported.filename} ({exported.content_type})") ``` ```go theme={null} import "github.com/anima-labs-ai/go" client := anima.NewClient("mk_...") report, err := client.Compliance.GenerateReport(ctx, orgID, anima.GenerateReportInput{ Type: anima.ComplianceReportTypeSOC2Summary, PeriodStart: "2026-01-01", PeriodEnd: "2026-03-31", }) ``` ### Available Templates Template identifiers are the uppercase values below. `GET /v1/orgs/{orgId}/compliance/templates` returns the live list. | Template | Description | | ----------------- | -------------------------------------------------------------- | | `SOC2_SUMMARY` | Control status and collected evidence, for your own audit prep | | `ACTIVITY_REPORT` | Agent activity across channels over the period | | `ACCESS_REVIEW` | API keys, permission changes, and access grants | | `AUDIT_EXPORT` | Raw audit-log export for the period | | `GDPR_DSAR` | The data-subject bundle backing a DSAR response | ## DSAR (Data Subject Access Requests) Handle GDPR and CCPA data subject access requests. Published SDK releases up to and including Node 0.6.0, Python 0.7.0 and the current Go tag send `requestType` with lowercase values here, which the API rejects. The fix is merged but unreleased. On those versions, call the DSAR routes directly as below — the field is `type` and the values are uppercase. ```bash theme={null} curl -X POST https://api.useanima.sh/v1/orgs/{orgId}/compliance/dsars \ -H "Authorization: Bearer mk_..." \ -H "Content-Type: application/json" \ -d '{ "type": "ACCESS", "subjectEmail": "user@example.com", "description": "GDPR Article 15 request received via support ticket #4521", "dueInDays": 30 }' ``` Then poll it: ```bash theme={null} curl https://api.useanima.sh/v1/orgs/{orgId}/compliance/dsars/{dsarId} \ -H "Authorization: Bearer mk_..." ``` ### Request types | Value | Meaning | | ------------- | ---------------------------------------- | | `ACCESS` | Article 15 — give the subject their data | | `DELETE` | Article 17 — erase the subject's data | | `RECTIFY` | Article 16 — correct inaccurate data | | `PORTABILITY` | Article 20 — export in a portable format | | `RESTRICT` | Article 18 — restrict processing | ### Status values `RECEIVED` → `VERIFIED` → `IN_PROGRESS` → `COMPLETED`, or `DENIED`. A request past its due date reports `OVERDUE`. `dueInDays` accepts 1–90 and defaults to 30, the GDPR response window. ## Compliance Dashboard Get a real-time view of your compliance posture: The dashboard has three sections: `reports`, `dsars`, and `compliance`. ```ts theme={null} const dashboard = await anima.compliance.getDashboard(orgId); console.log(`Overall progress: ${dashboard.compliance.overallProgress}%`); for (const fw of dashboard.compliance.frameworkSummaries) { console.log(`${fw.framework}: ${fw.implementedCount}/${fw.totalControls} (${fw.progress}%)`); } // `overdue` is the number that matters — those are blown GDPR deadlines. console.log(`DSARs: ${dashboard.dsars.total} total, ${dashboard.dsars.overdue} overdue`); console.log(`Average resolution: ${dashboard.dsars.averageResolutionDays ?? "n/a"} days`); console.log(`Reports: ${dashboard.reports.total}`); for (const report of dashboard.reports.recentReports) { console.log(` ${report.type} — ${report.status} — ${report.createdAt}`); } ``` ## API Reference Every compliance route is org-scoped and lives under `/v1`. Base URL `https://api.useanima.sh`. | Endpoint | Method | Description | | ------------------------------------------------------- | ------ | ---------------------------------- | | `/v1/orgs/{orgId}/compliance/reports` | POST | Generate a report | | `/v1/orgs/{orgId}/compliance/reports` | GET | List generated reports | | `/v1/orgs/{orgId}/compliance/reports/{reportId}` | GET | Get report status and download URL | | `/v1/orgs/{orgId}/compliance/reports/{reportId}/export` | POST | Export a generated report | | `/v1/orgs/{orgId}/compliance/reports/{reportId}` | DELETE | Delete a report | | `/v1/orgs/{orgId}/compliance/templates` | GET | List available report templates | | `/v1/orgs/{orgId}/compliance/dsars` | POST | Create a DSAR request | | `/v1/orgs/{orgId}/compliance/dsars` | GET | List DSARs | | `/v1/orgs/{orgId}/compliance/dsars/{dsarId}` | GET | Get DSAR status and results | | `/v1/orgs/{orgId}/compliance/dsars/{dsarId}` | PATCH | Update a DSAR | | `/v1/orgs/{orgId}/compliance/dashboard` | GET | Get compliance dashboard | These routes require a master key (`mk_*`). ## Next Steps * [Audit Log](/security/audit-log) -- Underlying data for reports * [Anomaly Detection](/security/anomaly-detection) -- Security posture data # Conversational Calls Source: https://docs.useanima.sh/conversational-calls Understand Anima's REST-hosted and WebSocket-controlled voice-call modes. # Conversational Calls Anima supports two voice-call modes. Pick the mode based on who should drive the live conversation: Anima's hosted loop, or your own agent over WebSocket. ## Modes | Mode | How to start | Who drives the conversation | Best for | | ------------------- | -------------------------------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | | REST hosted | `POST /v1/voice/calls` | Anima attaches a server-side conversation loop after dialing | "Call me now" demos, scripted follow-up calls, quick production calls | | WebSocket BYO agent | Voice WebSocket / SDK connection | Your agent runtime accepts calls, streams events, and sends speech/control messages | Custom agents, complex routing, external memory, custom tool orchestration | Both modes use the same voice pipeline, phone identity, call records, transcripts, guardrails, and plan caps. The agent speaks with its configured voice — the catalog is multilingual (English, Spanish, French, German, Italian, Japanese, Dutch), so see the [Voice Catalog](/voice-catalog) to browse and set one. ## REST hosted calls Use REST when you want one HTTP request to start a call and let Anima handle the live response loop. ```bash theme={null} curl -X POST https://api.useanima.sh/v1/voice/calls \ -H "Authorization: Bearer ak_..." \ -H "Content-Type: application/json" \ -d '{ "agentId": "agt_...", "to": "+15551234567", "greeting": "Hi, this is my Anima agent. I am calling from my own number." }' ``` Anima attaches the hosted conversation loop after dialing. The first spoken line comes from `greeting`; deeper behavior comes from the agent configuration and the hosted voice loop. ## WebSocket-controlled calls Use the Voice WebSocket when your own agent runtime needs full control over the call — you bring the brain (your own LLM) and Anima handles the voice I/O. ```ts theme={null} const conn = anima.calls.connect({ agentId: "agt_..." }); conn.on("message", (message) => { if (message.type === "call.transcription") { console.log(message.data?.text); } }); conn.createCall("+15551234567", { greeting: "Hi, this is my Anima agent.", }); ``` The WebSocket path is the right choice when you need custom memory, custom tool routing, multi-agent handoff, or external application state during the live call. ## What Anima handles * Number ownership and call placement * The voice pipeline (speech-to-text, speech, barge-in) * TCPA consent attestation, plan-cap, and voice spend gates (RND scrubbing, DNC scrubbing and calling-hour windows stay with you) * Call lifecycle records * Transcripts and post-call artifacts * Webhooks for call lifecycle events ## What your app still owns * The lawful basis and consent record for contacting the recipient * Reassigned Numbers Database scrubbing — Anima does not query it for you * Do-Not-Call registry scrubbing, federal and state * Calling hours in the recipient's local time — Anima does not check the clock * Agent behavior and escalation policy * Any business-specific data used during the call * Follow-up workflows after the call ends **Before your agent places its first call,** enable outbound by completing the one-time consent attestation in **Settings → Outbound Calling & SMS** (Starter plan and above) — see [Compliance guardrails](/phone#compliance-guardrails). ## Related docs * [Quickstart: Voice Calls](/quickstart-voice) * [Voice WebSocket Protocol](/protocols/voice-websocket) * [Call Intelligence](/call-intelligence) * [Pricing & Limits](/pricing-and-limits) # Custom Domains Source: https://docs.useanima.sh/custom-domains Set up branded sending domains in Anima with DNS records, verification guidance, and troubleshooting tips. # Custom Domains Use your own domain for branded agent email. Anima generates the required DNS records, shows what still needs to propagate, and lets you verify once your provider publishes the changes. *** ### Why use a custom domain? Send from your own brand, improve trust with recipients, and separate production sending from shared or testing domains. ### What you need first A domain you control plus access to its DNS provider — for example Cloudflare, Route 53, Namecheap, GoDaddy, or another registrar. ### How long does it take? Many DNS changes appear within minutes, but some providers take up to 48 hours to fully propagate. *** ## What Anima sets up for you Your agents both send and receive mail on a custom domain, so Anima generates a complete set of DNS records covering both directions. You publish them once at your DNS provider; Anima handles delivery, signing, reputation, and inbound routing from there. | Direction | What the records do | | ------------ | -------------------------------------------------------------------------------------- | | **Inbound** | Route incoming mail and replies to your agents' inboxes. | | **Outbound** | Authenticate outgoing mail (SPF, DKIM, DMARC) so recipient mailbox providers trust it. | Your only job is DNS. Once the records verify, sending and receiving work with no further configuration. *** ## 1. Create the domain in Anima Go to **Dashboard → Domains**, click **Add domain**, and enter the root domain you want to send from — for example `example.com`. Once created, Anima opens the DNS setup surface immediately so you can copy the required records without hunting through the table. ## 2. Copy the required DNS records Anima generates the ownership TXT, the mail-routing MX records, the DKIM CNAME records, the SPF TXT record, and the DMARC TXT record for you. The product shows both the full record name and the shorter host value most DNS providers expect. | Record | Host example | Why it matters | | ------ | -------------------- | ----------------------------------------------------------------------------------------------------------- | | TXT | `@` | Verifies that you control the domain. | | MX | `mail` | Routes bounce and return-path traffic for outbound mail. | | TXT | `mail` | Publishes SPF for the dedicated return-path subdomain. | | CNAME | `token-a._domainkey` | Publishes DKIM keys so mailbox providers trust mail signed for your domain. | | MX | `@` | Routes inbound agent mail and replies for the domain. | | TXT | `@` or merge | Authorizes Anima to send on your behalf via SPF. Merge into an existing SPF record if you already have one. | | TXT | `_dmarc` | Tells inbox providers how to handle failed authentication and where to send aggregate reports. | ## 3. Use the right host format in your DNS provider This is the most common source of mistakes. Many DNS providers automatically append your root domain, so the safe default is to paste the **Host** column values from Anima instead of manually reconstructing the full record name. Avoid entering `_dmarc.example.com` into a provider that automatically appends `example.com` — that creates `_dmarc.example.com.example.com`. ### Provider-specific notes **Cloudflare** — Usually appends the root domain automatically. Paste the Host column, not the full name. **Route 53** — Supports manual entry and zone imports, but review long TXT values carefully if you paste them manually. **Namecheap** — Uses Host and Value fields. Prefer `@`, `mail`, and `_dmarc` instead of full FQDNs where possible. **GoDaddy** — Treat the UI as relative hostnames unless the screen explicitly asks for the full record name. The `mail` host is reserved for outbound return-path and bounce handling. Leave your apex MX pointed at the inbound record Anima provides so agent mail and replies are delivered. ## 4. Download the zone file if your provider supports imports The DNS setup surface includes a **Download zone file** action. Use it when your DNS provider accepts BIND-style imports — it's the fastest way to publish all records at once. If your provider does not support imports, copy each record individually from the setup table. ## 5. Verify and troubleshoot When you click **Verify records**, Anima checks each required DNS record and marks it as **Valid**, **Missing**, or **Invalid**. Fix only the rows that fail — if a record already matches, leave it alone and allow DNS time to propagate. ### Verification statuses **Missing** — The provider has not published the record yet, or propagation is still in progress. **Invalid** — A record exists, but the value or host is wrong. Compare the row in Anima against what your provider shows. **Valid** — That record is correct and does not need changes. ### SPF merge rule A domain should publish only one SPF TXT record at the root. If you already use SPF elsewhere, keep the existing record and merge Anima's `include:` mechanism into it rather than creating a second SPF TXT record. ## Ready to configure your domain? Open the Domains page to create a domain, copy records, and verify the setup in one place. * [Open domains](https://console.useanima.sh/domains) # Encryption & Security Source: https://docs.useanima.sh/encryption Learn how Anima uses AES-256-GCM field-level encryption, envelope encryption, per-agent key derivation, and key rotation to protect sensitive data. # Encryption & Security Anima protects sensitive integration secrets with field-level encryption built on AES-256-GCM and envelope key management. ### AES-256-GCM by Default Field-level encryption uses AES-256-GCM for secure secret storage. ### Envelope Encryption Org data encryption keys are wrapped and managed through envelope encryption. ## Field-Level Encryption Model Sensitive values are encrypted at the field level before persistence, keeping secrets protected while preserving application-level access controls. * **Algorithm:** AES-256-GCM. * **Scope:** Encrypt sensitive secret fields individually. * **Integrity:** Authenticated encryption provides tamper detection. * **Tenant isolation:** Each organization uses isolated encryption context. ## Envelope Encryption (KEK → DEK) Envelope encryption separates key encryption from data encryption so secrets can be protected with layered key management. ```text theme={null} 1. Create org DEK 2. Wrap DEK with KEK (stored in KMS/HSM boundary) 3. Encrypt secret field with org DEK (AES-256-GCM) 4. Persist ciphertext + iv + authTag + keyVersion 5. On read, unwrap DEK and decrypt if access policy allows ``` ## Encrypted Fields * API keys * Webhook secrets * Email provider credentials ## Key Rotation Key rotation is versioned so encrypted data can move to newer key material over time without breaking existing records. > **Note:** Rotation is non-breaking: reads support legacy key versions until migration is complete. ## Prisma Encryption Extension Example This Prisma extension encrypts secrets on write and decrypts them on read. ```ts title="prisma/encryption-extension.ts" theme={null} import { PrismaClient } from "@prisma/client"; import { encryptField, decryptField } from "@/lib/crypto/field-encryption"; const prisma = new PrismaClient().$extends({ query: { integrationCredential: { async create({ args, query }) { const organizationId = args.data.organizationId; return query({ ...args, data: { ...args.data, secretCiphertext: encryptField({ organizationId, plaintext: args.data.secretPlaintext, field: "secretPlaintext", }), }, }); }, async findUnique({ args, query }) { const record = await query(args); if (!record) return null; return { ...record, secretPlaintext: decryptField({ organizationId: record.organizationId, ciphertext: record.secretCiphertext, field: "secretPlaintext", }), }; }, }, }, }); export { prisma }; ``` > **Warning:** Never log decrypted secrets, and never render plaintext credentials in client-side UI. ## Vault Per-Agent Encryption The [Vault](/vault) extends this model with a unique Data Encryption Key (DEK) per agent, derived from the organization DEK with HKDF: ```text theme={null} HKDF-SHA256( ikm = orgDek, salt = SHA256(agentId), info = "anima-vault-agent-dek" ) → agentDek (32 bytes) ``` This gives three properties: * **Cross-agent isolation** — Agent A cannot decrypt Agent B's credentials, because each derives a different DEK. * **No extra key storage** — the derivation is deterministic, so agent DEKs are computed on demand rather than stored. * **Rotation cascades** — rotating the organization DEK automatically re-derives (and so rotates) every agent DEK. See the [Vault documentation](/vault) for how these keys protect credential storage, sharing, and injection. # API Error Reference Source: https://docs.useanima.sh/errors Every error code the Anima API emits — RFC 7807 problem types, what they mean, and how to fix them. # API Errors The Anima API returns errors as [RFC 7807](https://www.rfc-editor.org/rfc/rfc7807) `application/problem+json`. The `type` URI is always `https://docs.useanima.sh/errors/` (the code, lowercased, `_` → `-`) — those URIs are stable forever, and they all resolve to this page. Machine clients should branch on the `code` extension field. ```json theme={null} { "type": "https://docs.useanima.sh/errors/rate-limited", "title": "Too Many Requests", "status": 429, "detail": "Too many requests", "instance": "/v1/email/send", "code": "RATE_LIMITED", "details": { "retryAfter": 12 }, "error": { "code": "RATE_LIMITED", "message": "Too many requests" } } ``` * `code` and `details` are Anima extensions (RFC 7807 permits extensions). * Many errors include `details.remediation` — a `hint` written for both humans and LLM agents, sometimes with a `docsUrl` or `verificationUrl`. Agents should surface or act on the hint before retrying. * The legacy `error: { code, message, details }` sibling is preserved through SDK v1.x and will be removed in v2. * Every response carries `x-request-id` (unique per call) and `x-correlation-id` (stable per workflow) — include both when contacting support. ## Request errors (4xx) | Code | HTTP | Meaning | What to do | | ---------------------------- | ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `VALIDATION_ERROR` | 400 | The request body or parameters failed schema validation. | Fix the request shape; `details` carries field-level errors. | | `BAD_REQUEST` | 400 | The request is well-formed but invalid in the current state (e.g. re-enabling an already-active webhook, replaying a delivery that isn't dead-lettered). | Read `detail` — it names the state that blocked the operation. | | `INVALID_IDEMPOTENCY_KEY` | 400 | `Idempotency-Key` isn't 1–255 printable ASCII characters. | Send a UUID (or any conforming string). | | `UNAUTHORIZED` | 401 | Missing or invalid credentials. | Send `Authorization: Bearer ` with a live `ak_`/`mk_` key or OAuth token. | | `PAYMENT_REQUIRED` | 402 | A plan limit was reached (agents, identities, monthly volume), **or** outbound voice used up its included minutes and the authorized overage. On the voice case `details` carries `resource: "voice"` with `includedCredits`, `usedCredits`, `overageUsedCents` and `overageCapCents`. | Upgrade the tier, wait for the billing period, or enable [metered overage](/pricing-and-limits#metered-overage) and raise the limit. | | `VOICE_MONTHLY_CAP_EXCEEDED` | 402 | The tier's monthly outbound-call cap is used up. | Upgrade or wait until the next billing period; `details` carries `current`/`cap`. | | `FORBIDDEN` | 403 | Authenticated, but not allowed: acting on another agent's resources, or an [agent capability policy](/security#agent-capability-policies) refused the action. | Use the right key for the agent, or have an org admin adjust the agent's policy. | | `MASTER_KEY_REQUIRED` | 403 | The operation needs org-admin authority (agent creation, billing, policy writes). | Use an `mk_` key or the `admin:full` OAuth scope; on the CLI, just run the command — it steps up on its own. For every route, see [Reaching master authority](/security#reaching-master-authority). | | `NOT_FOUND` | 404 | The resource doesn't exist — or belongs to another organization (deliberately indistinguishable). | Check the id and which org your key belongs to. | | `CONFLICT` | 409 | State conflict, e.g. a duplicate slug or an already-existing resource. | Change the conflicting value or reuse the existing resource. | | `IDEMPOTENCY_BODY_MISMATCH` | 409 | The same `Idempotency-Key` was reused with a different body or path. | Use a fresh key per distinct request; reuse keys only for identical retries. | | `IDENTITY_NOT_VERIFIED` | 409 | The sending email identity isn't verified in the send region. | Follow `details.remediation` — verify the identity or attach one on a verified domain ([Custom Domains](/custom-domains)). | | `DOMAIN_NOT_VERIFIED` | 409 | Provision-time gate: the email domain isn't added/verified for this workspace. | Add and verify the domain first — see [Custom Domains](/custom-domains). | | `RECIPIENT_SUPPRESSED` | 409 | The recipient unsubscribed, hard-bounced, or complained — Anima refuses to send to them. | Remove the address from the recipient list; suppressions are inspectable per workspace. | | `RATE_LIMITED`, `RATE_LIMIT` | 429 | Per-minute/per-hour quota exceeded for the channel. | Honor `Retry-After` and the `X-RateLimit-*` headers; back off and retry. | | `RATE_LIMIT_PER_SECOND` | 429 | The per-org per-second soft cap was hit. | Smooth the burst; caps scale with tier. | | `VOICE_RATE_LIMIT_EXCEEDED` | 429 | Outbound dialing exceeded the per-second call cap. | Slow the dialing loop; `details` carries `current`/`cap`. | | `TCPA_GATE_BLOCKED` | 451 | Outbound calling is blocked until the org completes consent attestation (TCPA/DNC), or this call failed the gate. | Complete the consent attestation in the console; `details.missingFields` lists what's missing. | ## Server errors (5xx) | Code | HTTP | Meaning | What to do | | ---------------------------- | ---- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | | `INTERNAL_ERROR`, `INTERNAL` | 500 | Unexpected server failure. | Retry with backoff; if it persists, contact support with the `x-request-id`. | | `BAD_GATEWAY` | 502 | The upstream email provider rejected or failed the request. | Retry; if persistent, the `detail` names the provider error. | | `PHONE_PROVIDER_ERROR` | 502 | The upstream phone/voice provider failed the operation. | Retry; `detail` names the provider and operation. | | `SERVICE_UNAVAILABLE` | 503 | The feature is disabled or its infrastructure isn't configured for this environment. | Nothing client-side — the `detail` names the service; contact support if unexpected. | ## Related * [Webhooks](/webhooks) — delivery signing and retry behavior * [Security](/security) — key types and agent capability policies * [Pricing & Limits](/pricing-and-limits) — tier quotas behind 402/429 # Examples Source: https://docs.useanima.sh/examples Example agents demonstrating the Anima platform. # Example Agents Explore complete, runnable example agents that demonstrate how to use the Anima platform. ## Available Examples | Example | Services Used | Description | | --------------------------------------------------------------------------------------- | ------------- | -------------------------------------------------------------------------------- | | [Email Agent](https://github.com/anima-labs-ai/examples/tree/main/email-agent) | Email | AI agent that monitors an inbox and auto-replies using GPT-4 | | [E-Commerce Agent](https://github.com/anima-labs-ai/examples/tree/main/ecommerce-agent) | Email, Vault | Purchase agent with AI evaluation, vault-backed checkout, and credential storage | | [Support Agent](https://github.com/anima-labs-ai/examples/tree/main/support-agent) | Email, Vault | Customer support with AI triage, response generation, and escalation | | [Travel Agent](https://github.com/anima-labs-ai/examples/tree/main/travel-agent) | Email, Vault | Travel booking with AI planning, loyalty credentials, and vault-backed checkout | | [OpenAI Terminal](https://github.com/anima-labs-ai/examples/tree/main/openai-terminal) | Email | Terminal chat agent using OpenAI Agents SDK | | [Vercel AI Agent](https://github.com/anima-labs-ai/examples/tree/main/vercel-ai-agent) | Email | Streaming agent using Vercel AI SDK (TypeScript) | ## Getting Started ```bash theme={null} git clone https://github.com/anima-labs-ai/examples.git cd examples/ cp .env.example .env # Add your API keys pip install -r requirements.txt # or npm install python main.py # or npx tsx main.ts ``` ## Building Your Own Agent Every Anima agent follows a similar pattern: 1. **Create an agent** — your agent's identity on the platform 2. **Provision capabilities** — email inbox, vault, phone number 3. **Implement logic** — use AI to process inputs and make decisions 4. **Act through Anima** — send emails, store credentials, place calls 5. **Clean up** — deprovision resources when done See the [quickstart guides](/quickstart-email) to get started with each capability. # Headless Extension Connect Source: https://docs.useanima.sh/extension-connect Connect the Anima browser extension to an agent with no human — mint a connect URL, open it in a headless browser (e.g. a Puppeteer worker), and the extension auto-binds its bridge. # Headless Extension Connect The [Anima browser extension](/vault#browser-extension) links an agent to a real browser so it can autofill vault credentials and drive logins without any secret entering the model's context. Normally a person installs the extension and clicks **Connect**. For automation — a Puppeteer worker, a scheduled scraper, a partner integration — there is no person to click. **Headless connect** closes that gap: mint a one-time connect URL with the API, open it in the headless browser, and the extension binds its bridge to the agent automatically. The connect response never contains a token or a secret. `connectUrl` itself carries a short-lived, single-use exchange code; the extension redeems it out of band. Nothing sensitive passes through the model. ## The flow Call `POST /v1/extension/connect`. The API returns a `connectUrl` pointing at the console connect page, carrying a single-use `exc_auto_…` exchange code. Navigate the automation browser (with the Anima extension loaded) to `connectUrl`. The `exc_auto_` prefix tells the page to relay the code to the extension automatically — no click. The page exposes `data-connect-status`, which transitions to `connected` once the extension has bound its bridge to the agent. Wait on `[data-connect-status="connected"]`, then run your automation. ## Auth model Who you authenticate as decides whether you pass `agentId`: | Credential | `agentId` | Behavior | | ---------------------- | ------------ | ----------------------------------------------------------------------------------- | | **Master key** (`mk_`) | **Required** | Selects which agent the extension connects as. | | **Agent key** (`ak_`) | **Omit** | The server resolves the agent from the key. Passing another agent's id is rejected. | Two org policies support headless connect: **`session`** and **`pre_approved`**. The **`prompt_owner`** policy is **not supported** — there is no owner to approve an unattended connection, so the API rejects it: ``` Headless connect is not available under the prompt_owner policy — use session or pre_approved. ``` Under `pre_approved`, the target agent must be on the org's pre-approved allowlist (configure it under **Settings → Extension** in the console), or the request is rejected. ### `ttl` `ttl` is optional and **shorten-only**: `"15m" | "1h" | "session"`. It may only be shorter than — or equal to — the org's configured maximum. A value **longer** than the org policy is rejected rather than silently downgraded, so you learn the org forbids that lifetime instead of quietly getting a shorter one. Omit `ttl` to use the org default. A `"session"` TTL has no wall-clock expiry, so `expiresAt` comes back `null`. The separate `exchangeExpiresAt` is the deadline for redeeming the connect URL itself (a single-use handoff window, \~60 seconds) — distinct from `expiresAt`, the lifetime of the resulting connection. ## Mint a connect URL ```bash cURL theme={null} curl -X POST https://api.useanima.sh/v1/extension/connect \ -H "Authorization: Bearer $ANIMA_API_KEY" \ -H "Content-Type: application/json" \ -d '{"agentId": "agent_123", "ttl": "session"}' # → { # "agentId": "agent_123", # "connectUrl": "https://console.useanima.sh/extension/connect?code=exc_auto_…", # "expiresAt": null, # "exchangeExpiresAt": "2026-01-01T00:01:00.000Z", # "policy": "session" # } ``` ```bash CLI theme={null} # Master key: pass the agent. anima extension connect --agent agent_123 --ttl session # Agent key: omit it — the agent is resolved from the key. anima extension connect # Add --json for the full payload instead of the human-readable summary. anima extension connect --agent agent_123 --json ``` ```typescript Node theme={null} import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: process.env.ANIMA_API_KEY }); const { connectUrl } = await anima.extension.connect({ agentId: "agent_123", // omit when using an agent key ttl: "session", }); ``` ```python Python theme={null} import os from anima import Anima client = Anima(api_key=os.environ["ANIMA_API_KEY"]) result = client.extension.connect(agent_id="agent_123", ttl="session") connect_url = result.connect_url # Async: from anima import AsyncAnima # result = await client.extension.connect(agent_id="agent_123", ttl="session") ``` ```go Go theme={null} import "github.com/anima-labs-ai/go" client := anima.NewClient("ak_...") res, err := client.Extension.Connect(ctx, anima.ConnectExtensionParams{ AgentID: "agent_123", // omit when using an agent key TTL: "session", }) if err != nil { log.Fatal(err) } connectURL := res.ConnectURL ``` ```json MCP theme={null} // Tool: extension_connect { "agentId": "agent_123", "ttl": "session" } ``` ### Response | Field | Type | Description | | ------------------- | ----------------------------- | ------------------------------------------------------------------------------ | | `agentId` | `string` | The agent the extension will connect as. | | `connectUrl` | `string` | URL to open in the headless browser. Carries the single-use `exc_auto_…` code. | | `expiresAt` | `string \| null` | When the resulting connection expires. `null` for a `session` TTL. | | `exchangeExpiresAt` | `string` | Deadline to redeem `connectUrl` (single-use handoff window). | | `policy` | `"session" \| "pre_approved"` | The policy governing the session. | ## Puppeteer worker recipe End to end: mint the URL, launch Chrome with the automation bundle loaded, open the URL, and wait for the bridge. ```javascript theme={null} import puppeteer from "puppeteer"; import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: process.env.ANIMA_API_KEY }); const { connectUrl } = await anima.extension.connect({ agentId: "agent_123" }); // The automation bundle: the extension built with the Chrome Web Store `key` // injected, so an unpacked --load-extension resolves to the SAME extension ID // the connect page targets. Built with `bun run build:automation` → dist-automation/. const EXT = "/path/to/anima-extension/dist-automation"; const browser = await puppeteer.launch({ headless: "new", // old headless can't load extensions args: [ `--disable-extensions-except=${EXT}`, `--load-extension=${EXT}`, ], }); const page = await browser.newPage(); await page.goto(connectUrl); // exc_auto_ code auto-relays, no click await page.waitForSelector('[data-connect-status="connected"]'); // The extension bridge is now live for agent_123 — drive your automation here. ``` **Use the automation bundle, not the plain build.** The console connect page only ever messages the *published* Chrome Web Store extension ID — it never trusts a caller-supplied one. An ordinary unpacked build gets a different, per-path ID, so the relay silently never reaches it. The automation bundle (`dist-automation/`) injects the Chrome Web Store `key` into the manifest, pinning the unpacked extension to the production ID the page targets. A few operational notes: * **`--headless=new` is required.** Chrome's old headless mode cannot load extensions at all. * **A fresh browser profile connects with no prompt.** The `exc_auto_` code auto-relays on page load. * **Re-connecting a profile that already holds a live bridge requires confirmation.** An unattended page must not silently replace an existing connection — the extension asks the owner to approve the re-pair, and denies otherwise. For a clean worker run, start from a fresh profile. ## Related * [Vault — Browser Extension](/vault#browser-extension) — how the extension performs credential autofill without exposing secrets. * [Vault](/vault) — per-agent encrypted credential storage and the secret boundary. * [Security](/security) — key types (`mk_` vs `ak_`) and the master-key model. # Frequently Asked Questions Source: https://docs.useanima.sh/faq Frequently asked questions about Anima. # Frequently Asked Questions Answers to common questions about Anima. ### Can I use Anima with Gmail? Anima does not relay through Gmail or your existing mailbox — and doesn't need to. Every agent gets its own real mailbox on Anima's mail infrastructure: an address on `agents.useanima.sh` out of the box, or on [your own custom domain](/custom-domains) with full SPF/DKIM/DMARC. Your agent can of course email any Gmail user, and replies thread correctly in Gmail. ### Is it free? Anima is a managed cloud platform with a [free tier](/pricing-and-limits): 3 agent identities, 3,000 emails/month, and 10 vault credentials, no credit card required. Paid tiers start at \$19/month and add phone numbers, SMS, and voice. ### How many agents can I create? It depends on your plan: Free includes 3 agent identities, Starter 25, Growth 250, Enterprise unlimited. See [Pricing & Limits](/pricing-and-limits). ### Does it support attachments? Yes, fully supported. You can send and receive attachments. Incoming attachments are parsed and available via the API, and every attachment is scanned for malware and disguised executables in both directions (see [Attachment Scanning](/security#attachment-scanning)). ### What happens if an agent tries to leak my API key? The Outbound Guard will detect the pattern and block the email immediately. You will receive an alert about the attempt. ### Can agents email each other? Yes! Agents can communicate with each other using their unique email addresses, just like normal users. # Getting Started Source: https://docs.useanima.sh/getting-started Install the Anima CLI, provision an agent identity, and send its first email from the terminal — then call the same platform from your code. # Getting Started Give an AI agent a real, owned identity — an email inbox, and optionally a phone number — and send its first message, all from the terminal. Anima is hosted; there is nothing to run locally and no infrastructure to stand up. This page takes you from zero to a working agent that can send email. The `anima` CLI is the fastest way to provision and drive an agent. ```bash theme={null} npm install -g @anima-labs/cli ``` Bun and pnpm work too: `bun add -g @anima-labs/cli` or `pnpm add -g @anima-labs/cli`. Use this on your development machine, and as a dependency of any Node/TypeScript agent project. For containers, servers, and CI runners, download the signed static binary from [GitHub Releases](https://github.com/anima-labs-ai/cli/releases) (`anima-linux-x64` / `anima-linux-arm64`). Every release is signed with Sigstore keyless signing; verify it before installing: ```bash theme={null} VERSION="v0.6.5" ARCH="$(uname -m | sed 's/x86_64/x64/; s/aarch64/arm64/')" BIN="anima-linux-${ARCH}" BASE="https://github.com/anima-labs-ai/cli/releases/download/${VERSION}" curl -fsSL -O "${BASE}/${BIN}" curl -fsSL -O "${BASE}/${BIN}.sigstore.bundle" cosign verify-blob \ --bundle "${BIN}.sigstore.bundle" \ --certificate-identity-regexp 'https://github.com/anima-labs-ai/cli/\.github/workflows/release\.yml@refs/tags/v.+' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ "${BIN}" install -m 0755 "${BIN}" /usr/local/bin/anima ``` macOS and Windows binaries are not shipped — on those platforms use the npm package. See [Install the Anima CLI](/install) for the full verification chain, the `curl | sh` installer, and Dockerfile examples. Confirm it's on your PATH: ```bash theme={null} anima --version ``` The primary binary is `anima`; `am` is a shorter alias for the same tool. Run the guided onboarding: ```bash theme={null} anima onboard ``` If you're not signed in yet, `anima onboard` starts setup for you. Choosing **Create a fresh agent identity** provisions an organization, an agent, and an email inbox in a single flow, and saves your credentials locally. You'll be asked for two things: * **The agent owner's email** — the human who owns this agent. A 6-digit verification code is sent here. * **A username for the agent** — this becomes the agent's address, `@agents.useanima.sh`. Onboarding then shows your identity and plan tier, offers a couple of optional test-mode demos (no real email is sent), and offers to wire Anima into your AI client over MCP. When it finishes you have a working agent identity with an inbox, its credentials stored in `~/.anima`, and the commands to do everything else. Already have an Anima organization and API key? Choose **Configure with an existing API key** instead, or run `anima init --non-interactive --api-key ak_...`. A brand-new agent starts **unverified**, and an unverified agent may only email its own owner. To unlock sending to anyone, get the 6-digit code from the owner's inbox and submit it: ```bash theme={null} anima verify ``` On success, the agent is verified and can send to any recipient. You can confirm your identity and tier at any time: ```bash theme={null} anima auth whoami ``` Send an email from your agent. Until the agent is verified, send it to the owner's address — that always works and proves the inbox is live end to end. You'll need the agent's ID, which onboarding printed and stored; `anima auth whoami` shows your identity. ```bash theme={null} anima email send \ --agent \ --to you@example.com \ --subject "Hello from my Anima agent" \ --body "This email was sent by an AI agent." ``` The command prints the sent message's ID. To watch messages arrive and other events in real time, open the live event stream: ```bash theme={null} anima tail ``` On a Starter tier or above, give the same agent a phone number for SMS and voice: ```bash theme={null} anima phone provision --agent --country US --capabilities sms,voice ``` Then text a number you control: ```bash theme={null} anima phone send-sms --agent --to +15551234567 --body "Texting from my Anima agent." ``` See [Phone & Voice](/phone) for the full SMS-then-call walkthrough. ## Use it from code The same platform is available from the official SDKs. Once onboarding has provisioned an agent, use its ID to send from your application. Set `ANIMA_API_KEY` in your environment (the CLI stored your key under `~/.anima`), or pass the key to the client directly. ```bash theme={null} npm install @anima-labs/sdk ``` ```ts theme={null} import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: process.env.ANIMA_API_KEY }); const message = await anima.messages.sendEmail({ agentId: "agent_...", to: ["you@example.com"], subject: "Hello from my Anima agent", body: "This email was sent by an AI agent.", }); console.log(message.id, message.status); ``` ```bash theme={null} pip install anima-labs ``` ```python theme={null} import os from anima import Anima anima = Anima(api_key=os.environ["ANIMA_API_KEY"]) message = anima.messages.send_email( agent_id="agent_...", to=["you@example.com"], subject="Hello from my Anima agent", body="This email was sent by an AI agent.", ) print(message.id, message.status) ``` ```bash theme={null} go get github.com/anima-labs-ai/go ``` ```go theme={null} package main import ( "context" "fmt" "log" "os" anima "github.com/anima-labs-ai/go" ) func main() { client := anima.NewClient(os.Getenv("ANIMA_API_KEY")) msg, err := client.Messages.SendEmail(context.Background(), anima.SendEmailParams{ AgentID: "agent_...", To: []string{"you@example.com"}, Subject: "Hello from my Anima agent", Body: "This email was sent by an AI agent.", }) if err != nil { log.Fatal(err) } fmt.Println(msg.ID, msg.Status) } ``` The REST API lives at `https://api.useanima.sh/v1` and accepts your key as either a `Bearer` token or an `X-API-Key` header. ## Next steps Let your agent set itself up from `skill.md`, and connect the docs and product MCP servers. Wire Anima's tools into Claude Code, Cursor, Claude Desktop, VS Code, and Windsurf. Store credentials your agent can use without exposing raw secrets to the model. Typed clients for Node.js, Python, and Go. # Agent Cards Source: https://docs.useanima.sh/identity/agent-cards Machine-readable Agent Cards describing an agent's identity, capabilities, and contact endpoints — the discovery layer for A2A. # Agent Cards An Agent Card is a machine-readable JSON document describing an agent's identity, capabilities, and contact endpoints. Cards are what other agents fetch during [A2A discovery](/a2a/overview) to learn who an agent is and what it can do. Anima generates a card automatically for every agent from its live state — its DID, provisioned channels (email, phone, vault, addresses), and contact identities. There is nothing to publish manually. ## Fetching a card ``` GET https://api.useanima.sh/v1/agents/{agentId}/card ``` Readable org-wide with an agent or master key. Cards are also served publicly at `/.well-known/agent.json` on hosts that publish one — the [A2A `discover`](/a2a/overview#discovering-an-agent) helpers fetch that URL from any domain, Anima-hosted or not. ### Example card ```json theme={null} { "name": "Acme Purchasing Agent", "description": "Handles procurement for Acme Corp.", "url": "https://api.useanima.sh/v1/agents/cmb1xa3f50001abcd/card", "did": "did:web:agents.useanima.sh:cmb1x9k2l0000abcd:cmb1xa3f50001abcd", "capabilities": { "email": true, "phone": true, "vault": true, "address": false, "protocols": ["a2a"] }, "verification": { "level": "standard", "credentials": ["AnimaEmailVerified", "AnimaOwnerBound"] }, "trustScore": 20, "contact": { "email": "purchasing@agents.useanima.sh", "phone": "+14155550142" } } ``` ## Field reference | Field | Meaning | | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `did` | The agent's [`did:web` DID](/identity/did-method). Resolve it to get the public key that signs the agent's A2A requests. | | `capabilities` | Which channels this agent actually has provisioned right now — derived from live state, not self-declared. | | `contact` | The agent's primary email address and phone number, when provisioned. | | `verification.level` / `verification.credentials` | Derived from the [verifiable credentials](/identity/verifiable-credentials) the platform has issued to the agent. Credentials are auto-issued on real verification events (email OTP, phone provisioning, paid checkout), so levels move without manual action: `basic` = no verification credentials; `standard` = a channel verified (`AnimaEmailVerified` or `AnimaPhoneVerified`); `premium` = standard + org-level verification (`AnimaPaymentCapable` or `AnimaKYBCompleted`). | | `trustScore` | **Reserved.** A fixed placeholder (`20`), identical for every agent — not computed from any signal. Do not sort, filter, or gate on it. The registry's `trustMin` parameter is accepted but ignored for the same reason. | The strongest trust signal remains cryptographic: resolve the agent's DID document and verify the signature on its [A2A messages](/a2a/overview). `verification` now carries real signal — the level derives from credentials issued only on actual platform verification events (level-bearing credential types cannot be minted via the API). `trustScore` is still a placeholder; ignore it. ## Next Steps * [DID Method](/identity/did-method) -- The identity layer underneath the card * [A2A Protocol](/a2a/overview) -- Discovery + signed task dispatch * [Agent Registry](/registry/overview) -- Org-wide agent search # DID Method (did:web) Source: https://docs.useanima.sh/identity/did-method Decentralized identifiers for AI agents using the standard did:web method. Document structure, resolution, and key rotation. # DID Method (did:web) Anima assigns every agent a globally unique decentralized identifier (DID) using the standard [`did:web`](https://w3c-ccg.github.io/did-method-web/) method — a W3C-registered method that resolves over plain HTTPS, with no bespoke method support required. ## DID Format ``` did:web:agents.useanima.sh:: ``` For example: `did:web:agents.useanima.sh:cmb1x9k2l0000abcd:cmb1xa3f50001abcd` ## Resolution Every agent's DID document is world-readable — no API key required — at: ``` GET https://api.useanima.sh/.well-known/did// ``` The document is served with the `application/did+json` media type, a 60-second public cache, and open CORS, so agents in other orgs (and third-party tooling) can fetch and verify it. The spec-canonical `did:web` HTTPS mapping (`https://agents.useanima.sh///did.json`) is not yet routed — until it is, fetch the document from the API host above. ### Example DID Document ```json theme={null} { "@context": [ "https://www.w3.org/ns/did/v1", "https://w3id.org/security/suites/jws-2020/v1" ], "id": "did:web:agents.useanima.sh:cmb1x9k2l0000abcd:cmb1xa3f50001abcd", "verificationMethod": [ { "id": "did:web:agents.useanima.sh:cmb1x9k2l0000abcd:cmb1xa3f50001abcd#key-1", "type": "JsonWebKey2020", "controller": "did:web:agents.useanima.sh:cmb1x9k2l0000abcd:cmb1xa3f50001abcd", "publicKeyJwk": { "kty": "OKP", "crv": "Ed25519", "x": "…" } } ], "authentication": [ "did:web:agents.useanima.sh:cmb1x9k2l0000abcd:cmb1xa3f50001abcd#key-1" ], "assertionMethod": [ "did:web:agents.useanima.sh:cmb1x9k2l0000abcd:cmb1xa3f50001abcd#key-1" ] } ``` The agent's Ed25519 key signs [A2A requests](/a2a/overview); receivers verify the signature against the public key in this document. ## API Reference DIDs are created automatically when an agent is provisioned — there is no separate "create DID" call. ### Get an agent's DID document ``` GET https://api.useanima.sh/v1/agents/{agentId}/did ``` Returns the same DID document served at the public resolution URL. Readable org-wide with an agent or master key (public discovery works via the `did.json` URL above without any key). ### Rotate keys ``` POST https://api.useanima.sh/v1/agents/{agentId}/did/rotate ``` Generates a new keypair for the agent and republishes the DID document. Master key required. Signatures made with the old key stop verifying once the document no longer lists it — coordinate rotation with any party that pinned the old key. ## Related endpoints | Endpoint | Method | Description | | --------------------------------- | ------ | ------------------------------------------ | | `/v1/agents/{agentId}/did` | GET | DID document for an agent | | `/v1/agents/{agentId}/did/rotate` | POST | Rotate the agent's keypair (master key) | | `/v1/agents/{agentId}/card` | GET | Public [Agent Card](/identity/agent-cards) | | `/v1/identity/verify` | POST | Verify a JWT Verifiable Credential | ## Next Steps * [Verifiable Credentials](/identity/verifiable-credentials) -- Verify and revoke credentials * [Agent Cards](/identity/agent-cards) -- Machine-readable agent metadata * [A2A Protocol](/a2a/overview) -- Agent-to-agent communication using DIDs # Verifiable Credentials Source: https://docs.useanima.sh/identity/verifiable-credentials W3C Verifiable Credentials for AI agents — auto-issued on real verification events, plus list, verify, and revoke APIs. # Verifiable Credentials Anima's identity layer is built around W3C Verifiable Credentials (VCs): signed, revocable attestations about an agent (for example "this agent's email is verified" or "this org passed billing verification"). **Current status.** Issuance, listing, verification, and revocation are all live. Credentials are issued **automatically on real platform events** — you don't call anything to get them — and the [Agent Card](/identity/agent-cards)'s `verification.level` moves as they land. Revocation is published as a [StatusList2021 bitstring](#revocation-status) you can check without calling us. ## How credentials are issued ### Automatically, on platform verification events | Event | Credentials issued | Issued to | | ------------------------------------------------------------------- | ---------------------------------------- | --------------------------------------- | | Email verified (owner completes the OTP on `POST /v1/agent/verify`) | `AnimaEmailVerified` + `AnimaOwnerBound` | Every DID-bearing agent in the org | | Phone verified (a real number is provisioned) | `AnimaPhoneVerified` | The agent the number was provisioned to | | Org verified (paid Stripe checkout completes) | `AnimaPaymentCapable` | Every DID-bearing agent in the org | Issuance is idempotent per (agent, credential type) — retries and repeat verifications don't stack duplicates, and an org that verified before auto-issuance shipped picks its credentials up on its next `/v1/agent/verify` call. These level-bearing credential types are **platform-reserved**: they cannot be minted through the API, so a card's `verification.level` always reflects something that actually happened. ### Via the API (level-neutral attestations) ``` POST https://api.useanima.sh/v1/agents/{agentId}/credentials ``` Master key required. Issues a signed JWT-VC of a **level-neutral** type (for example `AnimaAddressVerified`) to one of your org's agents. Requests for platform-reserved types are rejected — the API cannot change a card's verification level. ### Issuer model Credentials are self-issued in v1: signed server-side with the subject agent's own Ed25519 DID key, only on platform events or via the master-key endpoint. `verifyCredential` resolves issuer keys from the agent's DID, so issued VCs verify out of the box. Each credential's `metadata.source` records whether the platform (`platform-auto`) or your master key (`api`) issued it, so consumers can weight them differently. A platform root issuer DID may layer on later. ## Verification levels The Agent Card's `verification.level` derives from the credential types the agent holds: | Level | Meaning | | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | `basic` | No verification credentials — a fresh, unverified agent. | | `standard` | A channel was verified: `AnimaEmailVerified` (owner completed the email OTP) or `AnimaPhoneVerified` (a real number was provisioned). | | `premium` | Standard **plus** org-level verification: `AnimaPaymentCapable` (paid checkout) or `AnimaKYBCompleted` (reserved for a future KYB flow). | ## Working with credentials ### List an agent's credentials ``` GET https://api.useanima.sh/v1/agents/{agentId}/credentials ``` Returns the agent's credential records — type, issuance metadata, `revoked` status, and the compact JWT for each. Self-scoped: an agent key can list only its own credentials; a master key can list any agent's in the org. ### Verify a credential ``` POST https://api.useanima.sh/v1/identity/verify ``` ```json theme={null} { "jwtVc": "" } ``` Verifies a JWT-encoded VC: signature against the issuer's DID, expiry, and revocation status. Works for externally issued credentials too — the issuer's DID document just has to be resolvable. ### Revoke a credential ``` POST https://api.useanima.sh/v1/agents/{agentId}/credentials/{vcId}/revoke ``` Master key required. Revoked credentials fail verification, stop counting toward the card's verification level, and stay in the list with `revoked: true`. A revoked type can be re-issued. ## Revocation status Revoked credentials are checkable **without asking Anima**. Every credential carries a [StatusList2021](https://www.w3.org/TR/2023/WD-vc-status-list-20230427/) entry in its `credentialStatus`, naming the list to fetch and the bit to read: ```json theme={null} "credentialStatus": { "id": "https://api.useanima.sh/.well-known/status/{orgId}/{agentId}#3", "type": "StatusList2021Entry", "statusPurpose": "revocation", "statusListIndex": "3", "statusListCredential": "https://api.useanima.sh/.well-known/status/{orgId}/{agentId}" } ``` `GET` that `statusListCredential` URL (unauthenticated) and you get the agent's `StatusList2021Credential` as a signed JWT-VC. To resolve a credential's status yourself: 1. Read `credentialStatus.statusListCredential` from the credential and fetch it. 2. Verify the list's signature against the issuer's public key — it is signed by the same key that signed the credential. 3. Base64url-decode and gunzip `credentialSubject.encodedList` to get the bitstring. 4. Read the bit at `statusListIndex`. **`1` means revoked.** The published bits are computed from the same source as `POST /v1/identity/verify`, so the two always agree — you can use either. **Credentials issued before this shipped** carry no `credentialStatus`. They are still revocable, but only through `POST /v1/identity/verify` — a signed JWT cannot be retrofitted with a status entry without changing its signature. Re-issue a credential to get one (issuance is idempotent per type; revoke first if the current one is still live). **Use the `statusListCredential` URL as given.** It resolves on `api.useanima.sh`, not on the `agents.useanima.sh` DID domain. The URL is signed into each credential and never changes for that credential's lifetime. ## API Reference | Endpoint | Method | Description | | ------------------------------------------------ | ------ | ------------------------------------------------------------------------ | | `/v1/agents/{agentId}/credentials` | POST | Issue a level-neutral credential (master key) | | `/v1/agents/{agentId}/credentials` | GET | List an agent's credentials | | `/v1/identity/verify` | POST | Verify a JWT VC (signature, expiry, revocation) | | `/v1/agents/{agentId}/credentials/{vcId}/revoke` | POST | Revoke a credential (master key) | | `/.well-known/status/{orgId}/{agentId}` | GET | The agent's signed StatusList2021 revocation bitstring (public, no auth) | ## Next Steps * [DID Method](/identity/did-method) -- The signing identity underneath credentials * [Agent Cards](/identity/agent-cards) -- Where verification state surfaces * [A2A Protocol](/a2a/overview) -- Signed agent-to-agent requests # Install the Anima CLI Source: https://docs.useanima.sh/install Install anima in an agent runtime — npm for Node/TS agents, signed Linux binary for containers and CI. Every path verifies signatures end-to-end. # Install the Anima CLI The `anima` CLI is built for **agents**, not for human click-through installs. That shapes every decision on this page: * **No macOS or Windows binaries.** Agent workloads run on Linux — in containers, VMs, and CI runners. A developer on a Mac should use the npm package; their agent, when it actually executes, runs on Linux. * **No Homebrew or Winget.** Those exist to make installs ergonomic for humans; agents read release manifests directly. * **Sigstore everywhere.** One uniform trust anchor for both install paths, verifiable offline from any pipeline. ## Pick your channel ```bash theme={null} # npm npm install -g @anima-labs/cli # Bun bun install -g @anima-labs/cli # pnpm pnpm add -g @anima-labs/cli ``` The package is published with [npm provenance](https://docs.npmjs.com/generating-provenance-statements), linking the tarball to the exact commit and GitHub Actions workflow that built it. Verify the attestation: ```bash theme={null} npm view @anima-labs/cli --json | jq .dist.attestations ``` Or enforce verification at install time (npm ≥ 9.5): ```bash theme={null} npm install @anima-labs/cli npm audit signatures ``` Use this path when `anima` is a runtime dependency of a Node/TS agent project. ```bash theme={null} curl -fsSL https://get.useanima.sh | sh ``` The installer: 1. Detects architecture (x64 / arm64) — aborts on non-Linux. 2. Downloads [cosign](https://docs.sigstore.dev/cosign/system_config/installation/) if not already present (hash-pinned to a known-good build). 3. Fetches the signed `SHA256SUMS` manifest from the GitHub release. 4. Verifies the manifest's Sigstore signature against our pinned release-workflow identity. 5. Downloads the platform binary and checks it against the now-trusted hash. 6. Installs to `/usr/local/bin` if writable, otherwise `~/.local/bin`. If *any* verification step fails, the install aborts — there is no silent fallback. A failed install is visible; a degraded one isn't. Pin a specific version (recommended for reproducible container builds): ```bash theme={null} curl -fsSL https://get.useanima.sh | sh -s -- --version v0.5.0 ``` Install into a custom prefix: ```bash theme={null} curl -fsSL https://get.useanima.sh | sh -s -- --prefix /opt/anima/bin ``` ## Inside a Dockerfile For a reproducible agent container, pin the version and verify explicitly: ```dockerfile theme={null} FROM ubuntu:24.04 RUN apt-get update && apt-get install -y curl ca-certificates && rm -rf /var/lib/apt/lists/* # Pin the version. Bumping this is a deliberate, reviewable change. ARG ANIMA_VERSION=v0.5.0 RUN curl -fsSL https://get.useanima.sh | sh -s -- --version "$ANIMA_VERSION" \ && anima --version ``` The installer does the signature verification inline. If the release is ever tampered with or the pinned workflow identity changes, the `RUN` step fails and the image build stops. ## Verify the install ```bash theme={null} anima --version anima vault --help ``` ## Configuration Every setting resolves in priority order — a flag wins, then an environment variable, then the active profile, then your saved defaults: 1. **CLI flag** (e.g. `--org`, `--api-key`, `--api-url`) 2. **Environment variable** (`ANIMA_API_KEY`, `ANIMA_API_URL`, `ANIMA_DEFAULT_ORG`, `ANIMA_DEFAULT_IDENTITY`, `ANIMA_OUTPUT_FORMAT`) 3. **Active profile** (`anima config profile use `) 4. **Saved defaults** (`anima config set ` or `anima init`) This keeps the CLI ergonomic in CI and containers — export your key and default org once, then drop the flags: ```bash theme={null} export ANIMA_API_KEY="ak_..." export ANIMA_DEFAULT_ORG="org_..." anima security scan # resolves the org from the environment ``` ## Verify a binary manually Every release asset has a matching Sigstore bundle. To verify end-to-end without trusting the installer (e.g. in an air-gapped pipeline): ```bash theme={null} VERSION="v0.5.0" ARCH="$(uname -m | sed 's/x86_64/x64/; s/aarch64/arm64/')" BIN="anima-linux-${ARCH}" BASE="https://github.com/anima-labs-ai/cli/releases/download/${VERSION}" curl -fsSL -O "${BASE}/${BIN}" curl -fsSL -O "${BASE}/${BIN}.sigstore.bundle" cosign verify-blob \ --bundle "${BIN}.sigstore.bundle" \ --certificate-identity-regexp 'https://github.com/anima-labs-ai/cli/\.github/workflows/release\.yml@refs/tags/v.+' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ "${BIN}" ``` A successful verification means: 1. The binary was signed by *our* release workflow, at a release tag — not a different repo's and not a non-tagged build. 2. The signing event is recorded in the public [Rekor transparency log](https://rekor.sigstore.dev) — anyone can audit it. 3. The binary bytes haven't been modified since signing. ## GPG (optional, for distro pipelines) Releases ship a GPG-signed `SHA256SUMS.asc` when the publisher key is provisioned. This is redundant with Sigstore and exists only for downstream packagers that prefer a traditional PGP trust chain. Verify with: ```bash theme={null} gpg --verify SHA256SUMS.asc SHA256SUMS sha256sum -c SHA256SUMS --ignore-missing ``` Our publisher key fingerprint is published at [useanima.sh/.well-known/gpg-key.asc](https://useanima.sh/.well-known/gpg-key.asc). ## Troubleshooting **`Unsupported OS`** — You ran the `curl | sh` installer on macOS or Windows. The CLI ships Linux-only binaries; on a developer machine, use `npm install -g @anima-labs/cli` instead. **Signature verification failed** — Do not proceed. Report at [github.com/anima-labs-ai/cli/issues](https://github.com/anima-labs-ai/cli/issues) and include the full installer output. A failed verification is either a transient CDN issue (retry) or a genuine tampering attempt (we want to hear about it immediately). **`~/.local/bin not in PATH`** — The installer prints the exact line to add to your shell rc: ```bash theme={null} export PATH="$HOME/.local/bin:$PATH" ``` ## See also * [Security](/security) — full signing chain and threat model * [Quickstart: Vault](/quickstart-vault) — your first `anima vault` command * [SDKs](/sdks) — Python & TypeScript client libraries # Connect your AI client Source: https://docs.useanima.sh/integrations Wire Anima's tools into Claude Code, Claude Desktop, Cursor, VS Code, Windsurf, and other MCP clients — automatically with the CLI, or by hand. # Connect your AI client Anima's capabilities are exposed as [Model Context Protocol](https://modelcontextprotocol.io) tools, so any MCP-aware client can send email, provision numbers, place calls, and use the vault on your behalf. One server carries the full tool surface, reachable two ways: | | Endpoint / package | Best for | | ------------------------ | ---------------------------------------------------------------------------- | ------------------------------------------------------- | | **Hosted (recommended)** | `https://mcp.useanima.sh/mcp` | Nothing to install or keep updated; native HTTP clients | | **Local (stdio)** | [`@anima-labs/mcp`](https://www.npmjs.com/package/@anima-labs/mcp) via `npx` | Clients without remote-MCP support | The stdio server loads every tool group by default; pass `--tools=email,vault,phone` to register a subset (see [MCP Server](/mcp-servers) for the group list). ## Fastest path: let the CLI configure it The [Anima CLI](/install) detects your installed MCP clients and writes the right config for each one: ```bash theme={null} # Configure every detected client (hosted gateway — the default) anima setup-mcp install --all # Or target a single client, or install the local stdio server instead anima setup-mcp install --client cursor anima setup-mcp install --all --mode stdio ``` `setup-mcp install` auto-configures **Claude Code, Claude Desktop, Cursor, VS Code, and Windsurf**. It backs up any existing config before writing, and stores your API key in the client's config. Check and confirm what it did: ```bash theme={null} anima setup-mcp status # what's configured, where, and in which mode anima setup-mcp verify # validate the config (add --ping to test connectivity) ``` To connect a client the CLI doesn't configure automatically (such as Codex or Zed), add the configuration by hand using the tabs below. ## Per-client setup Each tab shows the CLI command where it applies, plus the manual configuration in that client's native format. Hosted configs authenticate with `Authorization: Bearer ak_...`; the stdio server takes your API key as `ANIMA_API_KEY`. ```bash theme={null} anima setup-mcp install --client claude-code ``` Or add the server manually: ```bash theme={null} # Hosted claude mcp add anima --transport http \ --url https://mcp.useanima.sh/mcp \ --header "Authorization: Bearer ak_..." # Local (stdio) claude mcp add anima --env ANIMA_API_KEY=ak_... -- npx -y @anima-labs/mcp ``` Claude Code stores MCP servers in `~/.claude.json`. ```bash theme={null} anima setup-mcp install --client claude-desktop ``` Or edit the config file directly: * **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json` * **Windows:** `%APPDATA%\Claude\claude_desktop_config.json` * **Linux:** `~/.config/Claude/claude_desktop_config.json` ```json theme={null} { "mcpServers": { "anima": { "command": "npx", "args": ["-y", "@anima-labs/mcp"], "env": { "ANIMA_API_KEY": "ak_..." } } } } ``` ```bash theme={null} anima setup-mcp install --client cursor ``` Or edit `~/.cursor/mcp.json` (hosted, native HTTP): ```json theme={null} { "mcpServers": { "anima": { "url": "https://mcp.useanima.sh/mcp", "headers": { "Authorization": "Bearer ak_..." } } } } ``` ```bash theme={null} anima setup-mcp install --client vscode ``` Or edit your user `mcp.json` (Command Palette → **MCP: Open User Configuration**): ```json theme={null} { "servers": { "anima": { "type": "http", "url": "https://mcp.useanima.sh/mcp", "headers": { "Authorization": "Bearer ${input:anima-key}" } } }, "inputs": [ { "id": "anima-key", "type": "promptString", "description": "Anima API Key", "password": true } ] } ``` ```bash theme={null} anima setup-mcp install --client windsurf ``` Or edit `~/.codeium/windsurf/mcp_config.json`: ```json theme={null} { "mcpServers": { "anima": { "serverUrl": "https://mcp.useanima.sh/mcp", "headers": { "Authorization": "Bearer ak_..." } } } } ``` The CLI does not configure these clients automatically — add the server using each client's MCP configuration. Both support stdio servers launched with `npx`: ``` command: npx args: ["-y", "@anima-labs/mcp"] env: ANIMA_API_KEY=ak_... ``` One entry is enough — the server registers every tool group (pass `--tools=email,vault` in `args` to load a subset). ## Hosted (remote) server If you'd rather not run the servers locally, Anima hosts them at a single endpoint. Point any client with native remote-MCP support at it and send your API key as a bearer token: | | | | ------------- | ------------------------------ | | **Endpoint** | `https://mcp.useanima.sh/mcp` | | **Transport** | Streamable HTTP | | **Auth** | `Authorization: Bearer ak_...` | ```json theme={null} { "mcpServers": { "anima": { "url": "https://mcp.useanima.sh/mcp", "headers": { "Authorization": "Bearer ak_..." } } } } ``` Treat your API key like a password. Prefer your client's secret-input mechanism over hardcoding the key in a file that might be committed. ## Try it Once connected, ask your assistant in natural language: * "Send an email from my agent to [user@example.com](mailto:user@example.com)." * "Text me now from my agent's number." * "Store my CRM login in the vault — don't print the password." * "Make a voice call to +1-555-0123 after confirming consent." ## Related The skill manifest, the docs MCP server, and machine-readable docs. Provision a number, text a human, and place a call. # Data Processing Agreement Source: https://docs.useanima.sh/legal/data-processing-agreement Data Processing Agreement for Anima platform customers — GDPR-compliant data processing terms. # Data Processing Agreement **Effective Date:** March 28, 2026 **Last Updated:** August 5, 2026 This Data Processing Agreement ("DPA") forms part of the Terms of Service (the "Agreement") between the entity identified as the customer in the Agreement ("Controller" or "Customer") and Anima Labs Ltd, including its affiliates ("Processor" or "Anima"), and supplements the Agreement with respect to Anima's processing of Personal Data on behalf of the Customer. This DPA applies where and only to the extent that Anima processes Personal Data on behalf of the Customer in the course of providing the Service, and such Personal Data is subject to the European General Data Protection Regulation (EU 2016/679) ("GDPR"), the UK General Data Protection Regulation ("UK GDPR"), the Swiss Federal Act on Data Protection ("FADP"), or other applicable data protection laws. *** ## 1. Definitions In this DPA, the following terms have the meanings set out below. Capitalized terms not defined in this DPA have the meanings given to them in the Agreement. **"Applicable Data Protection Law"** means all data protection and privacy laws applicable to the processing of Personal Data under this DPA, including the GDPR, UK GDPR, FADP, and the California Consumer Privacy Act ("CCPA"). **"Controller"** means the entity that determines the purposes and means of the processing of Personal Data, as defined in Applicable Data Protection Law. **"Data Subject"** means the identified or identifiable natural person to whom the Personal Data relates. **"EEA"** means the European Economic Area. **"Personal Data"** means any information relating to a Data Subject that is processed by Anima on behalf of the Customer in connection with the Service, as defined in Applicable Data Protection Law. **"Personal Data Breach"** means a breach of security leading to the accidental or unlawful destruction, loss, alteration, unauthorized disclosure of, or access to Personal Data transmitted, stored, or otherwise processed. **"Processing"** means any operation or set of operations performed on Personal Data, whether or not by automated means, including collection, recording, organization, structuring, storage, adaptation, alteration, retrieval, consultation, use, disclosure by transmission, dissemination, alignment, combination, restriction, erasure, or destruction. **"Processor"** means the entity that processes Personal Data on behalf of the Controller, as defined in Applicable Data Protection Law. **"Standard Contractual Clauses" or "SCCs"** means the standard contractual clauses for the transfer of personal data to processors established in third countries, as adopted by the European Commission Decision 2021/914 of 4 June 2021, and as may be amended or replaced from time to time. **"Sub-processor"** means any third party appointed by Anima to process Personal Data on behalf of the Customer. *** ## 2. Scope and Purpose of Processing ### 2.1 Scope This DPA applies to the processing of Personal Data as described in Annex I (Details of Processing) attached hereto. The categories of Data Subjects, types of Personal Data, and purposes of processing are specified in Annex I. ### 2.2 Customer Instructions Anima shall process Personal Data only on documented instructions from the Customer, including with respect to transfers of Personal Data to a third country or international organization, unless required to do so by applicable law. In such a case, Anima shall inform the Customer of that legal requirement before processing, unless that law prohibits such notification on important grounds of public interest. ### 2.3 Purpose Limitation Anima shall process Personal Data solely for the purpose of providing the Service in accordance with the Agreement and this DPA, and shall not process Personal Data for any other purpose unless instructed by the Customer in writing or required by applicable law. *** ## 3. Obligations of the Processor Anima shall: ### 3.1 Compliance Process Personal Data in compliance with Applicable Data Protection Law and the terms of this DPA. ### 3.2 Confidentiality Ensure that all persons authorized to process Personal Data have committed themselves to confidentiality or are under an appropriate statutory obligation of confidentiality. ### 3.3 Security Implement and maintain appropriate technical and organizational measures to ensure a level of security appropriate to the risk, as described in Annex II (Security Measures) attached hereto. These measures include, at a minimum: * Encryption of Personal Data at rest (AES-256) and in transit (TLS 1.2+). * Measures to ensure the ongoing confidentiality, integrity, availability, and resilience of processing systems and services. * The ability to restore the availability and access to Personal Data in a timely manner in the event of a physical or technical incident. * A process for regularly testing, assessing, and evaluating the effectiveness of technical and organizational measures. ### 3.4 Assistance Taking into account the nature of processing, assist the Customer by appropriate technical and organizational measures, insofar as this is possible, for the fulfillment of the Customer's obligation to respond to requests for exercising Data Subject rights under Applicable Data Protection Law. ### 3.5 Deletion and Return At the choice of the Customer, delete or return all Personal Data to the Customer after the end of the provision of the Service, and delete existing copies unless applicable law requires storage of the Personal Data. Anima will provide the Customer with a 30-day period following termination to export or request return of Personal Data. *** ## 4. Sub-processors ### 4.1 Authorization The Customer provides general written authorization for Anima to engage Sub-processors for the processing of Personal Data under this DPA. The current list of approved Sub-processors is set forth in Annex III and is available at [https://useanima.sh/subprocessors](https://useanima.sh/subprocessors). ### 4.2 Notification of Changes Anima shall notify the Customer at least 30 days in advance of any intended addition or replacement of a Sub-processor, providing the Customer with an opportunity to object to the change. ### 4.3 Objection Right If the Customer reasonably objects to a new Sub-processor within 15 days of receiving notice, Anima shall use commercially reasonable efforts to: (a) make available to the Customer a change in the Service that avoids the use of the objected-to Sub-processor; or (b) recommend a commercially reasonable alternative. If Anima is unable to provide an alternative within 30 days, either party may terminate the affected portion of the Service without penalty. ### 4.4 Sub-processor Obligations Anima shall: (a) impose data protection obligations on each Sub-processor that are no less protective than those in this DPA; (b) remain fully liable to the Customer for the performance of each Sub-processor's obligations; and (c) ensure that each Sub-processor agreement provides for termination and data deletion/return provisions consistent with this DPA. *** ## 5. Data Subject Rights ### 5.1 Assistance Anima shall, taking into account the nature of the processing, assist the Customer by appropriate technical and organizational measures for the fulfillment of the Customer's obligation to respond to Data Subject requests under Applicable Data Protection Law, including requests for access, rectification, erasure, restriction, data portability, and objection. ### 5.2 Notification If Anima receives a request from a Data Subject regarding Personal Data processed on behalf of the Customer, Anima shall promptly notify the Customer and shall not respond to the request directly unless instructed by the Customer or required by applicable law. ### 5.3 Cost Anima shall provide reasonable assistance at no additional charge. If a Data Subject request requires significant effort beyond routine assistance, Anima may charge a reasonable fee based on the administrative cost of responding, provided Anima notifies the Customer of the fee in advance. *** ## 6. Security Measures ### 6.1 Technical Measures Anima implements and maintains the technical security measures described in Annex II, including: * **Encryption:** AES-256 encryption at rest for all Personal Data; TLS 1.2+ encryption in transit. * **Access Controls:** Role-based access control (RBAC), multi-factor authentication (MFA) for all administrative access, principle of least privilege. * **Network Security:** Network segmentation, firewalls, intrusion detection and prevention systems, DDoS protection. * **Audit Logging:** Comprehensive logging of all access to Personal Data, with tamper-evident log storage. * **Vulnerability Management:** Regular vulnerability scanning, annual penetration testing by qualified third parties, and a responsible disclosure program. ### 6.2 Organizational Measures * **Personnel Security:** Background checks for employees with access to Personal Data, mandatory security awareness training, and binding confidentiality obligations. * **Incident Response:** Documented incident response plan with defined roles, escalation procedures, and post-incident review processes. * **Business Continuity:** Redundant infrastructure, automated backups, and documented disaster recovery procedures with defined recovery time objectives (RTO) and recovery point objectives (RPO). * **Vendor Management:** Due diligence and ongoing monitoring of Sub-processors' security practices. *** ## 7. Personal Data Breach Notification ### 7.1 Notification to Customer Anima shall notify the Customer without undue delay, and in any event within 72 hours, after becoming aware of a Personal Data Breach affecting Personal Data processed on behalf of the Customer. ### 7.2 Content of Notification The notification shall include, to the extent available: * A description of the nature of the Personal Data Breach, including the categories and approximate number of Data Subjects and Personal Data records concerned. * The name and contact details of Anima's point of contact for further information. * A description of the likely consequences of the Personal Data Breach. * A description of the measures taken or proposed to be taken to address the Personal Data Breach, including measures to mitigate its possible adverse effects. ### 7.3 Ongoing Cooperation Anima shall cooperate with the Customer and take reasonable commercial steps to assist in the investigation, mitigation, and remediation of the Personal Data Breach. Anima shall provide the Customer with timely updates as additional information becomes available. ### 7.4 Documentation Anima shall document all Personal Data Breaches, including the facts relating to the breach, its effects, and the remedial action taken. *** ## 8. Audit Rights ### 8.1 Information and Audit Anima shall make available to the Customer all information necessary to demonstrate compliance with this DPA and shall allow for and contribute to audits, including inspections, conducted by the Customer or an auditor mandated by the Customer. ### 8.2 Audit Procedure Audits shall be conducted subject to the following conditions: * The Customer shall provide at least 30 days' written notice of an audit request. * Audits shall be conducted during normal business hours and shall not unreasonably disrupt Anima's operations. * The Customer and its auditors shall comply with Anima's reasonable security and confidentiality requirements. * Audits shall be limited to once per year unless a Personal Data Breach has occurred or a supervisory authority requires an additional audit. ### 8.3 Third-Party Certifications Anima may satisfy audit requests by providing: (a) relevant third-party audit reports or certifications (e.g., SOC 2 Type II); (b) responses to reasonable written questions; or (c) facilitating an on-site audit if the foregoing are insufficient to demonstrate compliance. ### 8.4 Cost Each party shall bear its own costs in connection with audits, except that if an audit reveals a material breach of this DPA by Anima, Anima shall bear the reasonable costs of the audit. *** ## 9. Data Deletion and Return ### 9.1 Upon Termination Upon termination or expiration of the Agreement, Anima shall, at the Customer's election: * Return all Personal Data to the Customer in a structured, commonly used, and machine-readable format; or * Delete all Personal Data and certify such deletion in writing. ### 9.2 Retention Period Anima will provide the Customer with a 30-day period following termination to export or request return of Personal Data. After this period, Anima shall delete all remaining Personal Data within 30 additional days, unless applicable law requires continued storage. ### 9.3 Sub-processor Data Anima shall ensure that all Sub-processors delete or return Personal Data in accordance with the timelines set forth in this Section 9. *** ## 10. International Data Transfers ### 10.1 Transfer Mechanism To the extent that the performance of the Service involves the transfer of Personal Data from the EEA, the United Kingdom, or Switzerland to a country that has not been recognized as providing an adequate level of data protection, the parties agree that such transfers shall be governed by the Standard Contractual Clauses, which are incorporated into this DPA by reference. ### 10.2 Module Application The SCCs shall apply as follows: * **Module Two (Controller to Processor):** Where the Customer is a Controller and Anima processes Personal Data as a Processor. * **Module Three (Processor to Processor):** Where the Customer is a Processor acting on behalf of its own controller, and Anima processes Personal Data as a Sub-processor. ### 10.3 UK International Data Transfer Addendum For transfers of Personal Data subject to the UK GDPR, the UK International Data Transfer Addendum to the EU SCCs (as issued by the UK Information Commissioner under Section 119A of the Data Protection Act 2018) shall apply and is incorporated into this DPA by reference. ### 10.4 Swiss Addendum For transfers of Personal Data subject to the Swiss FADP, the SCCs shall apply with the modifications necessary to comply with the FADP, including treating the Swiss Federal Data Protection and Information Commissioner (FDPIC) as the competent supervisory authority. ### 10.5 Supplementary Measures In addition to the SCCs, Anima implements the following supplementary measures to protect transferred Personal Data: * Encryption at rest and in transit as described in Annex II. * Strict access controls limiting access to Personal Data to authorized personnel. * Policies and procedures to handle government access requests in accordance with applicable law, including transparency reporting where permitted. *** ## 11. Duration and Termination ### 11.1 Term This DPA shall remain in effect for the duration of the Agreement and shall automatically terminate upon termination or expiration of the Agreement, subject to Section 9 (Data Deletion and Return). ### 11.2 Survival The obligations of Anima under this DPA with respect to the processing of Personal Data shall continue for as long as Anima retains Personal Data processed on behalf of the Customer. *** ## 12. Liability The liability of each party under this DPA is subject to the limitations of liability set forth in the Agreement. For the avoidance of doubt, the aggregate liability of Anima under the Agreement and this DPA combined shall not exceed the limitations set forth in the Agreement. *** ## 13. Governing Law This DPA shall be governed by and construed in accordance with the governing law provisions of the Agreement, except that: (a) where the SCCs apply, the governing law of the SCCs shall be as specified therein; and (b) where required by Applicable Data Protection Law, the relevant data protection law shall govern. *** ## 14. Contact For questions regarding this DPA, please contact: **Anima Labs Ltd** Data Protection Officer Email: [legal@useanima.sh](mailto:legal@useanima.sh) Website: [https://useanima.sh](https://useanima.sh) *** ## Annex I: Details of Processing ### A. List of Parties **Data Exporter (Controller):** The Customer, as identified in the Agreement. **Data Importer (Processor):** Anima Labs Ltd, registered in England and Wales, and its affiliates. ### B. Description of Processing | Element | Description | | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Categories of Data Subjects** | Customer's employees, contractors, end users, and the natural persons whose data is processed by Customer's Agents (e.g., email recipients, phone call participants). | | **Categories of Personal Data** | Account data (name, email, company); email data (addresses, content, metadata); phone/SMS data (numbers, content, metadata); vault entries (encrypted credentials); identity data (DIDs, verifiable credential metadata); usage data (API logs, dashboard activity); technical data (IP addresses, device information). | | **Sensitive Data** | None intentionally processed. If Customer submits sensitive data to the Service, Customer is responsible for ensuring a lawful basis and appropriate safeguards. | | **Frequency of Transfer** | Continuous, for the duration of the Service. | | **Nature and Purpose of Processing** | Processing is necessary to provide the Service, including: email routing and delivery; phone/SMS communication services; encrypted credential storage; cryptographic identity management; API request handling; usage metering and billing. | | **Retention Period** | As specified in Anima's Privacy Policy (Section 5) and subject to Customer configuration, except as required by applicable law. | ### C. Competent Supervisory Authority The competent supervisory authority shall be determined in accordance with Clause 13 of the SCCs. For UK-based Data Exporters, the competent supervisory authority is the UK Information Commissioner's Office (ICO). *** ## Annex II: Security Measures Anima implements and maintains the following technical and organizational security measures: ### Technical Measures | Measure | Description | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Encryption at Rest** | AES-256 encryption for all stored Personal Data, including database fields, vault entries, and backups. | | **Encryption in Transit** | TLS 1.2 or higher for all data in transit. Certificate pinning for critical internal services. | | **Access Control** | Role-based access control (RBAC) with principle of least privilege. Multi-factor authentication (MFA) required for all administrative access. | | **Network Security** | Virtual private cloud (VPC) isolation, network segmentation, Web Application Firewall (WAF), DDoS protection, and intrusion detection/prevention systems (IDS/IPS). | | **Audit Logging** | Immutable, tamper-evident audit logs for all access to Personal Data and administrative actions. Logs retained for a minimum of 2 years. | | **Vulnerability Management** | Automated vulnerability scanning (weekly), annual third-party penetration testing, and a responsible disclosure/bug bounty program. | | **Data Isolation** | Logical separation of Customer data through tenant isolation at the application and database layers. | | **Backup and Recovery** | Automated daily backups with encryption, geo-redundant storage, and tested disaster recovery procedures. RTO: 4 hours. RPO: 1 hour. | ### Organizational Measures | Measure | Description | | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Personnel Security** | Background checks for all employees with access to Personal Data. Mandatory security awareness training upon onboarding and annually thereafter. | | **Confidentiality** | All employees and contractors bound by written confidentiality agreements. | | **Incident Response** | Documented incident response plan with 24/7 on-call rotation, defined escalation procedures, and mandatory post-incident reviews. | | **Vendor Management** | Security assessment and due diligence for all Sub-processors prior to engagement. Ongoing monitoring of Sub-processor compliance. | | **Physical Security** | Infrastructure hosted on SOC 2 Type II and ISO 27001 certified Google Cloud Platform data centers with physical access controls, surveillance, and environmental protections. | | **Change Management** | Formal change management process with peer review, testing, and approval requirements for all changes to production systems. | *** ## Annex III: List of Sub-processors | Sub-processor | Processing Activity | Location | Data Processed | | ------------------------- | --------------------------------------------------------------- | ----------------- | --------------------------------------- | | **Google Cloud Platform** | Cloud infrastructure and hosting | United States, EU | All categories (encrypted) | | **Stripe** | Payment processing | United States | Billing data, payment methods | | **Telnyx** | Telephony and SMS services | United States, EU | Phone numbers, call/SMS data | | **Twilio** | Phone number line-type and reassigned-number lookup | United States | Destination phone numbers, carrier name | | **Deepgram** | Speech-to-text and text-to-speech for voice calls | United States | Call audio, voice transcripts | | **Google (Gemini API)** | Language model for agent conversation and message understanding | United States | Message and conversation content | | **Clerk** | Authentication and identity | United States | Account data, auth events | | **Resend** | Email delivery | United States | Email addresses, email content | This list is current as of the Last Updated date above. Updated lists are maintained at [https://useanima.sh/subprocessors](https://useanima.sh/subprocessors). # Privacy Policy Source: https://docs.useanima.sh/legal/privacy-policy Privacy Policy for the Anima platform — how we collect, use, and protect your data. # Privacy Policy **Effective Date:** March 28, 2026 **Last Updated:** August 5, 2026 Anima Labs Ltd ("Anima," "we," "us," or "our") is committed to protecting the privacy of our customers and their end users. This Privacy Policy explains how we collect, use, disclose, and safeguard personal data in connection with the Anima platform, APIs, and related services (the "Service") available at [https://useanima.sh](https://useanima.sh). This Privacy Policy applies to all users of the Service, including developers, administrators, and operators acting on behalf of their organizations. *** ## 1. Data Controller Information **Anima Labs Ltd** is the data controller for personal data processed in connection with the operation of the Service and your account. For personal data processed on behalf of our customers (e.g., email content, phone records, and vault entries associated with customer Agents), the customer is the data controller and Anima acts as a data processor. The terms of such processing are governed by our [Data Processing Agreement](/legal/data-processing-agreement). **Contact:** Email: [legal@useanima.sh](mailto:legal@useanima.sh) Website: [https://useanima.sh](https://useanima.sh) *** ## 2. Types of Personal Data Collected ### 2.1 Account Data When you register for the Service, we collect: * Full name and job title * Business email address * Company or organization name * Billing address and payment information * Phone number (optional) ### 2.2 Usage Data We automatically collect data about how you interact with the Service: * API call logs (endpoints called, timestamps, response codes, latency) * Dashboard activity (pages visited, features used) * Authentication events (login times, IP addresses, authentication methods) * Error logs and diagnostic data * Feature usage metrics and patterns ### 2.3 Agent Data When your Agents use the Service, we process data on your behalf, which may include: * **Email Data:** Email addresses, message content (headers, body, attachments), delivery status, bounce and complaint data. * **Phone and SMS Data:** Phone numbers, call metadata (duration, timestamps, call direction), SMS message content, delivery receipts. * **Vault Entries:** Encrypted credential data, metadata about stored entries (creation date, last accessed, entry type). Anima does not have access to decrypted vault content in customer-managed encryption configurations. * **Identity Data:** Decentralized Identifiers (DIDs), verifiable credential metadata, Agent Card profiles, A2A protocol interaction logs. ### 2.4 Technical Data We collect technical information from devices and browsers used to access the Service: * IP addresses * Browser type and version * Operating system and device type * Referring URLs * Time zone and language settings *** ## 3. Legal Bases for Processing (GDPR Article 6) We process personal data under the following legal bases: ### 3.1 Contract Performance (Article 6(1)(b)) Processing necessary for the performance of our contract with you, including: * Provisioning and maintaining your account * Providing the Service features you have requested (email, phone, vault, identity) * Processing billing and payments * Providing customer support ### 3.2 Legitimate Interest (Article 6(1)(f)) Processing necessary for our legitimate interests, provided these interests are not overridden by your rights and freedoms, including: * Improving and optimizing the Service * Detecting and preventing fraud, abuse, and security incidents * Analyzing usage patterns to develop new features * Ensuring network and information security * Enforcing our Terms of Service ### 3.3 Consent (Article 6(1)(a)) Where required by applicable law, we process personal data based on your freely given, specific, informed, and unambiguous consent, including: * Sending marketing communications and newsletters * Placing non-essential cookies and tracking technologies * Processing data for purposes beyond those described in this Privacy Policy You may withdraw consent at any time by contacting us at [legal@useanima.sh](mailto:legal@useanima.sh) or by using the relevant opt-out mechanism. Withdrawal of consent does not affect the lawfulness of processing carried out before withdrawal. ### 3.4 Legal Obligation (Article 6(1)(c)) Processing necessary for compliance with legal obligations, including tax reporting, responding to lawful government requests, and maintaining records required by financial regulations. *** ## 4. How We Use Your Data We use the personal data we collect to: * **Provide the Service:** Operate, maintain, and deliver the features and functionality of the Platform, including email routing, phone/SMS services, vault storage, and identity management. * **Process Transactions:** Process payments, generate invoices, and manage your billing account. * **Communicate with You:** Send transactional communications (account notifications, security alerts, service updates), and, where you have opted in, marketing communications. * **Ensure Security:** Monitor for and protect against fraud, abuse, unauthorized access, and other security threats. * **Improve the Service:** Analyze usage data to understand how the Service is used, identify areas for improvement, and develop new features. * **Comply with Law:** Fulfill our legal and regulatory obligations, including tax reporting, anti-money laundering requirements, and responding to lawful requests from authorities. * **Enforce Our Terms:** Investigate and enforce compliance with our Terms of Service and Acceptable Use Policy. *** ## 5. Data Retention We retain personal data only for as long as necessary to fulfill the purposes for which it was collected, unless a longer retention period is required by law. | Data Category | Retention Period | | --------------------- | ---------------------------------------------------------------------- | | Account Data | Duration of the account plus 3 years after termination | | Usage Data (API logs) | 90 days in detailed form; 2 years in aggregated form | | Email Content | 30 days after delivery, unless configured otherwise by the customer | | Phone/SMS Records | 90 days, or as required by telecommunications regulations | | Vault Entries | Duration of the account; deleted within 30 days of account termination | | Identity Data (DIDs) | Duration of the account plus 1 year after termination | | Technical Data | 90 days | | Billing Records | 7 years, as required by tax and accounting regulations | Customers may request earlier deletion of their data subject to applicable legal retention requirements. See Section 8 (Data Subject Rights) for details. *** ## 6. Data Sharing and Subprocessors ### 6.1 No Sale of Personal Data Anima does not sell personal data to third parties. We do not share personal data for third-party advertising purposes. ### 6.2 Subprocessors We share personal data with the following categories of subprocessors, who process data on our behalf and under our instructions: | Subprocessor | Purpose | Data Processed | | ------------------------------- | --------------------------------------------------------------- | -------------------------------------------- | | **Google Cloud Platform (GCP)** | Cloud infrastructure and hosting | All categories (encrypted at rest) | | **Stripe** | Payment processing and billing | Billing data, payment methods | | **Telnyx** | Phone and SMS services | Phone numbers, call/SMS metadata and content | | **Twilio** | Phone number line-type and reassigned-number lookup | Destination phone numbers, carrier name | | **Deepgram** | Speech-to-text and text-to-speech for voice calls | Call audio, voice transcripts | | **Google (Gemini API)** | Language model for agent conversation and message understanding | Message and conversation content | | **Clerk** | Authentication and user management | Account data, authentication events | | **Resend** | Transactional email delivery | Email addresses, email content | ### 6.3 Other Disclosures We may disclose personal data: * **To comply with law:** In response to a subpoena, court order, or other lawful request from a government authority. * **To protect rights:** When we believe in good faith that disclosure is necessary to protect our rights, your safety, or the safety of others, investigate fraud, or respond to a government request. * **In a business transfer:** In connection with a merger, acquisition, bankruptcy, or sale of assets, in which case you will be notified of any change in data controller. * **With your consent:** Where you have provided explicit consent to the disclosure. ### 6.4 Subprocessor Updates We maintain an up-to-date list of subprocessors at [https://useanima.sh/subprocessors](https://useanima.sh/subprocessors). Customers who have entered into a Data Processing Agreement will receive 30 days' prior notice of any new subprocessor additions. *** ## 7. International Data Transfers ### 7.1 Transfer Mechanisms Anima Labs Ltd is based in the United Kingdom. Personal data may be transferred to and processed in the United States and other countries where our subprocessors operate. For transfers of personal data from the European Economic Area (EEA), the United Kingdom, or Switzerland to countries that have not received an adequacy decision, we rely on: * **Standard Contractual Clauses (SCCs):** As adopted by the European Commission (Decision 2021/914) and, where applicable, the UK International Data Transfer Addendum. * **Supplementary Measures:** Including encryption in transit and at rest, access controls, and contractual commitments from subprocessors. ### 7.2 EU-U.S. Data Privacy Framework Where applicable, we rely on relevant data privacy framework certifications maintained by our subprocessors. *** ## 8. Data Subject Rights (GDPR) If you are located in the EEA or the United Kingdom, you have the following rights under the GDPR and UK GDPR: ### 8.1 Right of Access (Article 15) You have the right to obtain confirmation of whether we process your personal data and, if so, to request a copy of that data along with information about the purposes and categories of processing. ### 8.2 Right to Rectification (Article 16) You have the right to request correction of inaccurate personal data and completion of incomplete personal data. ### 8.3 Right to Erasure (Article 17) You have the right to request deletion of your personal data where: the data is no longer necessary for the purposes for which it was collected; you withdraw consent and there is no other legal basis; you object to processing and there are no overriding legitimate grounds; or the data has been unlawfully processed. ### 8.4 Right to Restriction of Processing (Article 18) You have the right to request restriction of processing where: the accuracy of the data is contested; the processing is unlawful and you prefer restriction to erasure; Anima no longer needs the data but you require it for legal claims; or you have objected to processing pending verification. ### 8.5 Right to Data Portability (Article 20) You have the right to receive your personal data in a structured, commonly used, and machine-readable format, and to transmit that data to another controller, where processing is based on consent or contract and is carried out by automated means. ### 8.6 Right to Object (Article 21) You have the right to object to processing based on legitimate interests. We will cease processing unless we demonstrate compelling legitimate grounds that override your interests, rights, and freedoms, or where processing is necessary for the establishment, exercise, or defense of legal claims. ### 8.7 Right Related to Automated Decision-Making (Article 22) Anima does not currently make decisions based solely on automated processing that produce legal effects or similarly significant effects concerning you. If this changes, we will provide meaningful information about the logic involved and the significance and envisaged consequences of such processing, and provide a mechanism to request human review. ### 8.8 Exercising Your Rights To exercise any of these rights, contact us at [legal@useanima.sh](mailto:legal@useanima.sh). We will respond to your request within 30 days. We may request verification of your identity before fulfilling your request. If we are acting as a data processor on behalf of a customer, we will direct your request to the appropriate data controller. *** ## 9. CCPA Rights (California Residents) If you are a California resident, you have the following rights under the California Consumer Privacy Act (CCPA), as amended by the California Privacy Rights Act (CPRA): ### 9.1 Right to Know You have the right to request that we disclose the categories and specific pieces of personal information we have collected about you, the categories of sources, the business or commercial purpose for collecting the information, and the categories of third parties with whom we share it. ### 9.2 Right to Delete You have the right to request deletion of personal information we have collected from you, subject to certain exceptions (e.g., legal obligations, completing a transaction, security purposes). ### 9.3 Right to Opt-Out of Sale or Sharing Anima does not sell personal information and does not share personal information for cross-context behavioral advertising purposes as defined by the CCPA/CPRA. ### 9.4 Right to Correct You have the right to request that we correct inaccurate personal information that we maintain about you. ### 9.5 Non-Discrimination We will not discriminate against you for exercising any of your CCPA rights. We will not deny you the Service, charge you different prices, or provide a different level of quality based on your exercise of privacy rights. ### 9.6 Exercising Your Rights To exercise your CCPA rights, contact us at [legal@useanima.sh](mailto:legal@useanima.sh). We will verify your identity before fulfilling your request and respond within 45 days. *** ## 10. Cookies and Tracking Technologies ### 10.1 Types of Cookies We use the following categories of cookies on our website and dashboard: * **Strictly Necessary Cookies:** Required for the operation of the Service (authentication, session management, security). These cookies cannot be disabled. * **Analytics Cookies:** Used to understand how visitors interact with the Service, measure performance, and identify areas for improvement. Deployed only with your consent where required by law. * **Preference Cookies:** Used to remember your settings and preferences (language, theme, display options). ### 10.2 Managing Cookies You can manage your cookie preferences through: (a) the cookie consent banner displayed on first visit; (b) your browser settings; or (c) contacting us at [legal@useanima.sh](mailto:legal@useanima.sh). ### 10.3 Do Not Track The Service currently does not respond to "Do Not Track" browser signals. However, you can opt out of analytics cookies as described above. *** ## 11. Children's Privacy The Service is not directed to individuals under the age of 18. We do not knowingly collect personal data from children under 18. If we become aware that we have collected personal data from a child under 18, we will take steps to delete that information promptly. If you believe that a child under 18 has provided us with personal data, please contact us at [legal@useanima.sh](mailto:legal@useanima.sh). *** ## 12. Security Measures We implement appropriate technical and organizational measures to protect personal data against unauthorized access, alteration, disclosure, or destruction. These measures include: * **Encryption:** Data is encrypted at rest (AES-256) and in transit (TLS 1.2+). * **Access Controls:** Role-based access control, multi-factor authentication for administrative access, and principle of least privilege. * **Infrastructure Security:** Hosted on SOC 2 Type II certified infrastructure (Google Cloud Platform) with network segmentation, intrusion detection, and DDoS protection. * **Audit Logging:** Comprehensive audit logs of administrative actions and data access events. * **Employee Security:** Background checks, security training, and confidentiality agreements for all employees with access to personal data. * **Vulnerability Management:** Regular security assessments, penetration testing, and a responsible disclosure program. *** ## 13. Data Breach Notification ### 13.1 Supervisory Authorities In the event of a personal data breach that is likely to result in a risk to the rights and freedoms of natural persons, Anima will notify the relevant supervisory authority within 72 hours of becoming aware of the breach, in accordance with GDPR Article 33. ### 13.2 Affected Individuals Where a personal data breach is likely to result in a high risk to the rights and freedoms of natural persons, Anima will communicate the breach to the affected individuals without undue delay, in accordance with GDPR Article 34. ### 13.3 Customer Notification For data breaches affecting Customer Data where Anima acts as a data processor, Anima will notify the affected customer without undue delay and in any event within 72 hours of becoming aware of the breach, providing sufficient information for the customer to fulfill its own notification obligations. *** ## 14. Changes to This Privacy Policy We may update this Privacy Policy from time to time to reflect changes in our practices, technology, legal requirements, or other factors. We will notify you of material changes by: * Posting the updated Privacy Policy on our website with a revised "Last Updated" date. * Sending an email notification to the address associated with your account at least 30 days before the changes take effect. Your continued use of the Service after the effective date of any changes constitutes your acceptance of the updated Privacy Policy. *** ## 15. Contact Us If you have questions, concerns, or requests regarding this Privacy Policy or our data practices, please contact our Data Protection team: **Anima Labs Ltd** Data Protection Officer Email: [legal@useanima.sh](mailto:legal@useanima.sh) Website: [https://useanima.sh](https://useanima.sh) If you are located in the EEA or UK and are unsatisfied with our response to a privacy concern, you have the right to lodge a complaint with your local supervisory authority. For UK residents, this is the Information Commissioner's Office (ICO) at [https://ico.org.uk](https://ico.org.uk). # Terms of Service Source: https://docs.useanima.sh/legal/terms-of-service Terms of Service for the Anima platform — the unified agent identity infrastructure for AI agents. # Terms of Service **Effective Date:** March 28, 2026 **Last Updated:** March 28, 2026 These Terms of Service ("Terms") constitute a legally binding agreement between you ("Customer," "you," or "your") and Anima Labs Ltd, a company incorporated in England and Wales, and its affiliates including Anima Labs Inc. (collectively, "Anima," "we," "us," or "our"). These Terms govern your access to and use of the Anima platform, APIs, SDKs, documentation, and related services (collectively, the "Service") available at [https://useanima.sh](https://useanima.sh). By accessing or using the Service, you agree to be bound by these Terms. If you are entering into these Terms on behalf of an organization, you represent and warrant that you have the authority to bind that organization to these Terms, and references to "you" or "Customer" shall refer to that organization. *** ## 1. Definitions **"Agent"** means an autonomous or semi-autonomous artificial intelligence software program that accesses or uses the Service on behalf of a Customer or Organization. **"Agent Identity"** means the set of credentials, identifiers, and capabilities provisioned to an Agent through the Service, including but not limited to email addresses, phone numbers, cryptographic identifiers (DIDs), and vault entries. **"API"** means the application programming interfaces provided by Anima for programmatic access to the Service. **"API Key"** means the unique authentication credential issued to a Customer for accessing the API. **"Organization"** means a legal entity that has entered into these Terms and under which one or more Users and Agents operate. **"Platform"** means the Anima infrastructure, including email services, phone/SMS services, credential vault, cryptographic identity (DIDs), verifiable credentials, and the Agent-to-Agent (A2A) protocol. **"Service"** means the Anima platform, APIs, SDKs, documentation, dashboard, and all related services. **"User"** means any individual who accesses the Service on behalf of an Organization, including developers, administrators, and operators. **"Vault"** means the encrypted credential storage system provided as part of the Service. *** ## 2. Account Registration and API Keys ### 2.1 Account Creation To use the Service, you must create an account by providing accurate, complete, and current information. You must maintain the accuracy of this information throughout the term of your use. ### 2.2 API Keys Upon registration, you will be issued API Keys for programmatic access to the Service. You are responsible for: * Maintaining the confidentiality and security of all API Keys. * All activity that occurs under your API Keys, whether authorized or unauthorized. * Immediately notifying Anima at [legal@useanima.sh](mailto:legal@useanima.sh) if you become aware of any unauthorized use or compromise of your API Keys. ### 2.3 Account Security You shall implement reasonable security measures to protect your account credentials and API Keys. Anima reserves the right to suspend access if we reasonably believe your credentials have been compromised. *** ## 3. Acceptable Use Policy ### 3.1 General Requirements You agree to use the Service only for lawful purposes and in accordance with these Terms. You shall ensure that all Agents operating under your account comply with this Acceptable Use Policy. ### 3.2 Prohibited Activities You shall not, and shall not permit any Agent to: * **Fraud and Deception:** Engage in fraudulent, deceptive, or misleading activities, including impersonating humans or other entities without proper disclosure, or using Agent Identities to deceive third parties. * **Spam and Unsolicited Communications:** Send unsolicited bulk email, SMS, or other communications; engage in email harvesting; or distribute malware or phishing content. * **Illegal Activity:** Use the Service in violation of any applicable law, regulation, or third-party right, including laws governing telecommunications and data protection. * **System Interference:** Interfere with or disrupt the integrity or performance of the Service, attempt to gain unauthorized access to other accounts or systems, or circumvent rate limits or security measures. * **Harmful Content:** Use the Service to generate, store, or transmit content that is unlawful, defamatory, threatening, or that infringes intellectual property rights. * **Reverse Engineering:** Reverse engineer, decompile, disassemble, or otherwise attempt to derive the source code of the Service. * **Unauthorized Resale:** Resell, sublicense, or redistribute the Service without Anima's prior written consent. ### 3.3 Agent Conduct Requirements Customers are solely responsible for the conduct of their Agents. All Agents must: * Clearly identify themselves as AI agents when interacting with third parties, unless otherwise permitted by applicable law. * Operate within the scope of permissions and capabilities granted by the Customer. * Comply with all applicable laws and regulations in the jurisdictions in which they operate. * Respect rate limits, usage quotas, and fair usage policies. ### 3.4 Enforcement Anima reserves the right to investigate and take appropriate action against violations of this Acceptable Use Policy, including suspension or termination of access, removal of content, and reporting to law enforcement authorities. *** ## 4. Service Level Commitments ### 4.1 Uptime Target Anima targets 99.9% monthly uptime for the core Service, measured as the percentage of total minutes in a calendar month during which the Service is available. Scheduled maintenance windows, which will be communicated at least 48 hours in advance, are excluded from uptime calculations. ### 4.2 Service Credits If the Service falls below the 99.9% uptime target in any calendar month, eligible Customers on paid plans may request service credits in accordance with the Service Level Agreement published at [https://useanima.sh/sla](https://useanima.sh/sla). ### 4.3 Exclusions Uptime commitments do not apply to: (a) features identified as alpha, beta, or preview; (b) downtime caused by factors outside Anima's reasonable control, including force majeure events; (c) downtime resulting from Customer's equipment, software, or network connections; or (d) downtime caused by Customer's breach of these Terms. *** ## 5. Rate Limiting and Fair Usage ### 5.1 Rate Limits The Service enforces rate limits on API calls, email sending, SMS sending, and other operations. Current rate limits are published in the API documentation and may vary by plan tier. ### 5.2 Fair Usage Even within published rate limits, Anima reserves the right to throttle or restrict access if usage patterns are inconsistent with normal business operations, place disproportionate load on the infrastructure, or negatively impact other customers. ### 5.3 Notification Anima will make reasonable efforts to notify you before imposing restrictions under this section, except where immediate action is necessary to protect the Service or other customers. *** ## 6. Email Sending Policies ### 6.1 Anti-Spam Compliance All email sent through the Service must comply with applicable anti-spam legislation, including but not limited to: * **CAN-SPAM Act (United States):** All commercial email must include a valid physical postal address, a clear and conspicuous unsubscribe mechanism, and accurate header information. Unsubscribe requests must be honored within 10 business days. * **GDPR (European Union):** Email communications to EU residents must comply with the General Data Protection Regulation, including obtaining valid consent where required under Article 6 and Article 7. * **PECR (United Kingdom):** Email to UK recipients must comply with the Privacy and Electronic Communications Regulations. * **CASL (Canada):** Email to Canadian recipients must comply with Canada's Anti-Spam Legislation, including obtaining express or implied consent. ### 6.2 Agent Email Requirements Agents sending email through the Service must: * Use only email addresses provisioned through the Platform or verified custom domains. * Include clear identification that the sender is an AI agent, where required by law or recipient policy. * Not forge, spoof, or misrepresent email headers or sender identity. * Maintain bounce rates below 5% and spam complaint rates below 0.1%. ### 6.3 Email Suspension Anima reserves the right to immediately suspend email sending capabilities if: (a) bounce rates or spam complaint rates exceed acceptable thresholds; (b) the Customer's sending practices violate applicable law; or (c) the Customer's email activity negatively impacts the Platform's sending reputation. *** ## 7. Phone and SMS Usage Policies ### 7.1 Regulatory Compliance All phone and SMS usage through the Service must comply with applicable telecommunications regulations, including: * **TCPA (United States):** You must obtain prior express consent (or prior express written consent for marketing messages) before sending SMS or making automated calls to US numbers. You must honor opt-out requests immediately. * **10DLC Registration:** Customers sending SMS to US numbers must complete 10DLC (10-Digit Long Code) registration through the Platform before sending application-to-person (A2P) messages. * **GDPR and ePrivacy:** SMS to EU recipients must comply with applicable EU data protection and electronic communications laws. ### 7.2 Prohibited SMS Content The following content is prohibited in SMS messages sent through the Service: SHAFT content (sex, hate, alcohol, firearms, tobacco), illegal substances, deceptive or misleading offers, phishing or social engineering, and content that violates carrier acceptable use policies. ### 7.3 Call Recording and Monitoring If you use the Service to record or monitor phone calls, you are solely responsible for complying with all applicable consent and notification requirements under federal and state law. *** ## 8. Vault and Credential Storage ### 9.1 Encryption All data stored in the Vault is encrypted at rest using AES-256 encryption and in transit using TLS 1.2 or higher. Anima employs industry-standard key management practices. ### 9.2 Customer Responsibility You are solely responsible for: * The content stored in your Vault, including ensuring you have all necessary rights to store such content. * Managing access controls and permissions for Vault entries. * Maintaining backup copies of critical credentials and secrets, as Anima's liability for data loss is limited as set forth in Section 14. ### 9.3 Prohibited Vault Content You shall not store in the Vault: content that violates applicable law, malware or malicious code, or content that infringes third-party intellectual property rights. *** ## 9. Data Processing and Privacy ### 10.1 Privacy Policy Our collection and use of personal data in connection with the Service is described in our [Privacy Policy](/legal/privacy-policy). The Privacy Policy is incorporated into these Terms by reference. ### 10.2 Data Processing Agreement For Customers who require a Data Processing Agreement under GDPR or other applicable data protection laws, our standard [Data Processing Agreement](/legal/data-processing-agreement) is available and is incorporated into these Terms where applicable. ### 10.3 Customer Data As between you and Anima, you retain all rights in and to the data you submit to the Service ("Customer Data"). You grant Anima a limited, non-exclusive license to process Customer Data solely as necessary to provide and improve the Service. *** ## 10. Intellectual Property ### 11.1 Anima IP The Service, including all software, APIs, documentation, designs, trademarks, and other intellectual property, is and remains the exclusive property of Anima. Nothing in these Terms grants you any right, title, or interest in the Service except the limited right to use the Service in accordance with these Terms. ### 11.2 Customer IP As between you and Anima, you retain all intellectual property rights in your Customer Data, Agent configurations, and any software you develop using the Service. These Terms do not transfer any Customer intellectual property to Anima. ### 11.3 Feedback If you provide Anima with feedback, suggestions, or ideas regarding the Service ("Feedback"), you grant Anima a perpetual, irrevocable, worldwide, royalty-free license to use, modify, and incorporate such Feedback into the Service without obligation to you. *** ## 11. API Terms ### 12.1 API Access Access to the API is granted subject to these Terms and the API documentation. You shall use the API only in accordance with the published documentation. ### 12.2 Versioning Anima uses semantic versioning for the API. Major version changes that include breaking changes will be communicated at least 90 days in advance. ### 12.3 Deprecation Policy When an API version or feature is deprecated: * Anima will provide at least 90 days' notice before removing a major API version. * Deprecated features will be clearly marked in the API documentation and response headers. * Anima will provide migration guides for significant version changes. ### 12.4 Backward Compatibility Within a major API version, Anima will maintain backward compatibility. Additive changes (new endpoints, new optional fields) are not considered breaking changes. *** ## 12. Billing and Payments ### 13.1 Pricing The Service is offered on a usage-based pricing model. Current pricing is published at [https://useanima.sh/pricing](https://useanima.sh/pricing) and may be updated from time to time with 30 days' prior notice. ### 13.2 Usage-Based Billing You will be billed based on your actual usage of the Service, including but not limited to: API calls, emails sent and received, SMS messages, phone minutes and outbound call count, phone numbers provisioned, vault storage, and agent identities provisioned. ### 13.3 Invoicing Invoices are generated monthly and are due within 30 days of the invoice date unless otherwise agreed in a separate order form. All fees are stated in US Dollars unless otherwise specified. ### 13.4 Taxes All fees are exclusive of applicable taxes, duties, and levies. You are responsible for all taxes associated with your use of the Service, except for taxes based on Anima's net income. ### 13.5 Late Payments Late payments accrue interest at the lesser of 1.5% per month or the maximum rate permitted by applicable law. Anima reserves the right to suspend the Service for accounts with payments overdue by more than 30 days. *** ## 13. Disclaimer of Warranties THE SERVICE IS PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTIES OF ANY KIND, WHETHER EXPRESS, IMPLIED, OR STATUTORY, INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND NON-INFRINGEMENT. ANIMA DOES NOT WARRANT THAT THE SERVICE WILL BE UNINTERRUPTED, ERROR-FREE, OR SECURE, OR THAT ANY DEFECTS WILL BE CORRECTED. *** ## 14. Limitation of Liability ### 15.1 Exclusion of Consequential Damages TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL ANIMA BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, OR PUNITIVE DAMAGES, OR ANY LOSS OF PROFITS, REVENUE, DATA, OR BUSINESS OPPORTUNITY ARISING OUT OF OR RELATED TO THESE TERMS OR THE SERVICE, REGARDLESS OF THE THEORY OF LIABILITY. ### 15.2 Cap on Liability ANIMA'S TOTAL AGGREGATE LIABILITY ARISING OUT OF OR RELATED TO THESE TERMS OR THE SERVICE SHALL NOT EXCEED THE GREATER OF (A) THE AMOUNTS PAID BY YOU TO ANIMA IN THE TWELVE (12) MONTHS PRECEDING THE EVENT GIVING RISE TO THE CLAIM, OR (B) ONE HUNDRED US DOLLARS (\$100). ### 15.3 Exceptions The limitations in this Section 14 do not apply to: (a) either party's indemnification obligations; (b) either party's breach of confidentiality obligations; (c) your breach of Section 3 (Acceptable Use Policy); or (d) liability that cannot be limited under applicable law. *** ## 15. Indemnification ### 16.1 Customer Indemnification You agree to indemnify, defend, and hold harmless Anima and its officers, directors, employees, and agents from and against any claims, damages, losses, liabilities, costs, and expenses (including reasonable attorneys' fees) arising out of or related to: * Your use of the Service or any Agent's use of the Service under your account. * Your breach of these Terms or any applicable law or regulation. * Your Agent's interactions with third parties, including any claims arising from email, phone, or SMS communications initiated by your Agents. * Any content or data you submit to or transmit through the Service. ### 16.2 Anima Indemnification Anima agrees to indemnify, defend, and hold harmless Customer from and against any third-party claims that the Service, as provided by Anima, infringes such third party's intellectual property rights, provided that Customer promptly notifies Anima of the claim, gives Anima sole control of the defense, and cooperates with Anima's defense. *** ## 16. Termination and Suspension ### 17.1 Termination by Customer You may terminate your account at any time by providing written notice to Anima. Termination does not relieve you of any obligation to pay fees incurred prior to termination. ### 17.2 Termination by Anima Anima may terminate or suspend your access to the Service immediately, without prior notice, if: * You breach any material provision of these Terms. * You fail to pay any fees when due after a 15-day cure period. * Your use of the Service poses a security risk to the Service or other customers. * Required by applicable law, regulation, or court order. ### 17.3 Suspension Anima may suspend your access to all or part of the Service if we reasonably believe suspension is necessary to prevent harm to the Service, other customers, or third parties. We will provide notice of the suspension and the reasons therefor as soon as practicable. ### 17.4 Effect of Termination Upon termination: (a) all rights granted to you under these Terms will immediately cease; (b) you must cease all use of the Service and delete all API Keys; (c) Anima will make your Customer Data available for export for 30 days following termination, after which Anima may delete it; and (d) provisions that by their nature should survive termination will survive, including Sections 11, 14, 15, 16, and 19. *** ## 17. Dispute Resolution ### 18.1 Informal Resolution Before filing any formal dispute, the parties agree to attempt to resolve any dispute informally by sending written notice to the other party describing the dispute and proposed resolution. The parties shall negotiate in good faith for at least 30 days before initiating formal proceedings. ### 18.2 Arbitration Any dispute arising out of or relating to these Terms that cannot be resolved informally shall be settled by binding arbitration administered by the American Arbitration Association (AAA) under its Commercial Arbitration Rules. The arbitration shall take place in Wilmington, Delaware, and be conducted in English. ### 18.3 Exceptions Either party may seek injunctive or equitable relief in any court of competent jurisdiction to protect its intellectual property rights or confidential information without first engaging in the dispute resolution procedures above. ### 18.4 Class Action Waiver TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, YOU AND ANIMA EACH WAIVE THE RIGHT TO PARTICIPATE IN A CLASS ACTION, COLLECTIVE ACTION, OR REPRESENTATIVE ACTION WITH RESPECT TO ANY DISPUTE ARISING UNDER THESE TERMS. *** ## 18. Governing Law These Terms are governed by and construed in accordance with the laws of the State of Delaware, United States of America, without regard to its conflict of laws principles. The parties consent to the exclusive jurisdiction of the state and federal courts located in Delaware for any dispute not subject to arbitration. *** ## 19. Changes to These Terms Anima may modify these Terms from time to time. We will provide at least 30 days' prior written notice of material changes by email to the address associated with your account and by posting the updated Terms on our website. Your continued use of the Service after the effective date of any modification constitutes your acceptance of the modified Terms. If you do not agree with any modification, you must cease using the Service before the modification takes effect. *** ## 20. General Provisions ### 21.1 Entire Agreement These Terms, together with the Privacy Policy, Data Processing Agreement (where applicable), and any applicable order forms, constitute the entire agreement between you and Anima regarding the Service and supersede all prior agreements and understandings. ### 21.2 Severability If any provision of these Terms is held to be invalid or unenforceable, the remaining provisions will continue in full force and effect. ### 21.3 Waiver The failure of either party to enforce any right or provision of these Terms shall not constitute a waiver of such right or provision. ### 21.4 Assignment You may not assign or transfer these Terms, or any rights or obligations hereunder, without Anima's prior written consent. Anima may assign these Terms in connection with a merger, acquisition, or sale of all or substantially all of its assets. ### 21.5 Force Majeure Neither party shall be liable for any failure or delay in performance due to circumstances beyond its reasonable control, including acts of God, natural disasters, pandemics, war, terrorism, government actions, or failures of third-party infrastructure providers. ### 21.6 Notices All notices under these Terms shall be in writing and sent to: (a) Anima: [legal@useanima.sh](mailto:legal@useanima.sh); (b) Customer: the email address associated with your account. *** ## 22. Contact Information If you have questions about these Terms, please contact us at: **Anima Labs Ltd** Email: [legal@useanima.sh](mailto:legal@useanima.sh) Website: [https://useanima.sh](https://useanima.sh) # MCP Server Source: https://docs.useanima.sh/mcp-servers Connect Anima to Claude Desktop, Cursor, VS Code, and other MCP clients via Model Context Protocol. # MCP Server Anima exposes its platform via the [Model Context Protocol](https://modelcontextprotocol.io) (MCP), giving AI assistants direct access to agent email, inboxes, vault, phone, voice calls, and more. There are two ways to connect: | | Endpoint / package | Tools | Best for | | ------------------------ | ---------------------------------------------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------- | | **Hosted (recommended)** | `https://mcp.useanima.sh/mcp` | 65 — the full surface, always current (includes inbox management) | Nothing to install or keep updated; native HTTP clients | | **Local (stdio)** | [`@anima-labs/mcp`](https://www.npmjs.com/package/@anima-labs/mcp) via `npx` | 53 core tools | Clients without remote-MCP support; pinned/air-gapped configs | ## Quick Start ### Option A: Hosted (remote) Point any client with remote-MCP support at the hosted endpoint and authenticate with your API key as a Bearer token: ``` https://mcp.useanima.sh/mcp Authorization: Bearer ak_... ``` ### Option B: Local (stdio) Run the server locally via `npx`: ```bash theme={null} ANIMA_API_KEY=ak_... npx -y @anima-labs/mcp ``` By default every tool group is registered. Use `--tools` to load a subset — fewer tools can improve the model's tool selection and reduce token usage: ```bash theme={null} npx -y @anima-labs/mcp --tools=email,vault,phone ``` ## Configuration ### CLI Setup (all clients) The [Anima CLI](/install) auto-configures every detected MCP client (Claude Desktop, Claude Code, Cursor, Windsurf, VS Code): ```bash theme={null} # Configure all detected clients (hosted gateway — the default) anima setup-mcp install --all # Use the local stdio server instead anima setup-mcp install --all --mode stdio # Check what got written, then validate it anima setup-mcp status anima setup-mcp verify --ping ``` ### Claude Desktop Add to `~/Library/Application Support/Claude/claude_desktop_config.json`: **Hosted (remote via mcp-remote bridge):** ```json theme={null} { "mcpServers": { "anima": { "command": "npx", "args": [ "-y", "mcp-remote", "https://mcp.useanima.sh/mcp", "--header", "Authorization:${ANIMA_TOKEN}" ], "env": { "ANIMA_TOKEN": "Bearer ak_..." } } } } ``` **Local (stdio):** ```json theme={null} { "mcpServers": { "anima": { "command": "npx", "args": ["-y", "@anima-labs/mcp"], "env": { "ANIMA_API_KEY": "ak_..." } } } } ``` ### Cursor **Hosted (native HTTP):** ```json theme={null} { "mcpServers": { "anima": { "url": "https://mcp.useanima.sh/mcp", "headers": { "Authorization": "Bearer ak_..." } } } } ``` ### Claude Code ```bash theme={null} # Hosted claude mcp add anima --transport http \ --url https://mcp.useanima.sh/mcp \ --header "Authorization: Bearer ak_..." # Local claude mcp add anima --env ANIMA_API_KEY=ak_... -- npx -y @anima-labs/mcp ``` ### VS Code Add to your user `mcp.json` (Command Palette → **MCP: Open User Configuration**): ```json theme={null} { "servers": { "anima": { "type": "http", "url": "https://mcp.useanima.sh/mcp", "headers": { "Authorization": "Bearer ${input:anima-key}" } } }, "inputs": [ { "id": "anima-key", "type": "promptString", "description": "Anima API Key", "password": true } ] } ``` ## Environment Variables (stdio) | Variable | Required | Description | | ------------------ | -------- | ---------------------------------------------------- | | `ANIMA_API_KEY` | Yes | Agent API key (`ak_...`) | | `ANIMA_MASTER_KEY` | No | Master key (`mk_...`) — unlocks admin tools | | `ANIMA_API_URL` | No | API base URL (defaults to `https://api.useanima.sh`) | ## Tool Groups All groups load by default; pass `--tools=` to the stdio server to load a subset. | Group | Covers | | ------------ | ----------------------------------------------------- | | `workspace` | Account overview and usage rollups | | `agent` | Agent CRUD and address/identity management | | `email` | Email send/receive, threads, drafts, attachments | | `domain` | Custom sending domains: DNS, verification, zone files | | `phone` | Phone number provisioning and release | | `phone_call` | Outbound calls, transcripts, recordings, voices | | `sms` | SMS/MMS send and conversation history | | `vault` | Credential vault management and TOTP | | `webhook` | Webhook subscription management and testing | ## Example Usage Once connected, ask your AI assistant natural language questions: * "Send an email from my agent to [user@example.com](mailto:user@example.com)" * "Store my CRM login credentials in the vault" * "Text me now from my agent's number" * "Make a voice call to +1-555-0123" For editor-specific setup, see [Connect your AI client](/integrations). # Phone & Voice Source: https://docs.useanima.sh/phone Provision phone numbers, send SMS, receive replies, and place voice calls from an AI agent identity. # Phone & Voice Give your AI agents real phone numbers for SMS and voice. The phone number belongs to the same agent identity as its email inbox, vault, addresses, and DID, so cross-channel workflows stay tied to one actor and one audit trail. ## Overview Anima Phone lets you: * Search for available US numbers by area code and capability * Provision a dedicated number for an agent * Send a real "text me now" SMS from that agent * Receive inbound SMS through webhooks * Place outbound voice calls behind a server-side TCPA consent gate and per-plan call caps * Read call records and transcripts from the same identity surface Use this page when you already have an `agent_id`. If you are starting from zero, create an agent first in the console or through the agent API, then come back here with the agent ID. ## 1. Search for a number Find available numbers by area code and requested capability: ```python theme={null} from anima import Anima anima = Anima(api_key="ak_...") numbers = anima.phones.search( country_code="US", area_code="415", capabilities=["sms", "voice"], limit=3, ) for number in numbers["items"]: print(number["phoneNumber"], number.get("region")) ``` ```ts theme={null} import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: "ak_..." }); const numbers = await anima.phones.search({ countryCode: "US", areaCode: "415", capabilities: ["sms", "voice"], limit: 3, }); for (const number of numbers.items) { console.log(number.phoneNumber, number.region); } ``` ## 2. Provision a phone identity Provisioning assigns one number to one agent. Request both `sms` and `voice` when you want the number to support the full demo path. ```python theme={null} agent_id = "AGENT_ID" phone = anima.phones.provision( agent_id=agent_id, country_code="US", area_code="415", capabilities=["sms", "voice"], ) print(f"Provisioned: {phone.phone_number}") ``` ```ts theme={null} const agentId = "AGENT_ID"; const phone = await anima.phones.provision({ agentId, countryCode: "US", areaCode: "415", capabilities: ["sms", "voice"], }); console.log(`Provisioned: ${phone.phoneNumber}`); ``` ## 3. Text your human now Send the first SMS to a number you control. This is the fastest way to prove the agent has a real, reachable phone path. ```python theme={null} message = anima.messages.send_sms( agent_id=agent_id, to="+15551234567", # your phone number body="Hi - this is my Anima agent texting from its own number.", ) print(f"SMS sent: {message.id} ({message.status})") ``` ```ts theme={null} const message = await anima.messages.sendSms({ agentId, to: "+15551234567", // your phone number body: "Hi - this is my Anima agent texting from its own number.", }); console.log(`SMS sent: ${message.id} (${message.status})`); ``` ## 4. Receive replies Inbound SMS is delivered as a message event. Subscribe to `message.received`, then inspect the message channel/payload to distinguish SMS from email. ```bash theme={null} curl -X POST https://api.useanima.sh/v1/webhooks \ -H "Authorization: Bearer ak_..." \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-app.example.com/webhooks/anima", "events": ["message.received", "phone.provisioned", "call.ended"] }' ``` See [Webhooks](/webhooks) for signature verification and delivery retries. ## 5. Call your human now Outbound voice calls run through the voice gate before dialing. Use a number you control for the first call, and only call recipients where you have the required consent. ```python theme={null} call = anima.calls.create( agent_id=agent_id, to="+15551234567", # your phone number greeting="Hi, this is my Anima agent. I am calling from my own phone number.", ) print(f"Call started: {call.call_id} ({call.state})") ``` ```ts theme={null} const call = await anima.calls.create({ agentId, to: "+15551234567", // your phone number greeting: "Hi, this is my Anima agent. I am calling from my own phone number.", }); console.log(`Call started: ${call.callId} (${call.state})`); ``` Voice behavior comes from the configured agent. Use `greeting` for the first spoken line; use the agent configuration and voice pipeline for deeper call behavior. ## 6. Read the transcript After the call ends, fetch the transcript: ```python theme={null} transcript = anima.calls.get_transcript(call.call_id) for segment in transcript.segments: print(f"{segment.speaker}: {segment.text}") ``` ```ts theme={null} const transcript = await anima.calls.getTranscript(call.callId); for (const segment of transcript.segments) { console.log(`${segment.speaker}: ${segment.text}`); } ``` For live call events and bidirectional voice control, see the [Voice WebSocket Protocol](/protocols/voice-websocket). ## Phone, mail, and vault together The useful Anima pattern is not "a phone API in isolation." It is one agent identity using the right channel at each step: 1. Email the human or customer with context. 2. Text them if the workflow needs an immediate response. 3. Place a voice call when the situation needs synchronous confirmation. 4. Store credentials and tokens in the vault so the agent can act without exposing secrets to the LLM. 5. Tie the full workflow together in audit logs and webhooks through the same agent ID. ## Compliance guardrails For US outbound calls, Anima enforces some guardrails server-side before the dial reaches the telephony provider, and leaves the rest to you. Knowing which is which matters — TCPA damages are per call. **Anima enforces:** your organization's TCPA consent attestation (a missing or incomplete attestation returns a `451` and the call is not placed), per-plan call caps and per-second rate limits, and the voice spend ceiling. On SMS, recipients who reply STOP are suppressed automatically. **You are responsible for:** scrubbing against the Reassigned Numbers Database, scrubbing against federal and state Do-Not-Call registries, and confining calls to lawful hours in the recipient's local time. Anima checks none of these and will place the call — a call placed at 6am local goes through. That is the CPaaS-standard posture: scrub before the list reaches us and keep the evidence, because the TCPA safe harbor is earned by the caller, not by the platform. See [What is the TCPA?](https://useanima.sh/tcpa) for the full split. ### Enable outbound calling and SMS Placing calls and sending SMS is gated on a one-time **consent attestation** that you complete yourself in the console — no manual review or support ticket. In **Settings → Outbound Calling & SMS**: 1. Choose the **basis for contact** — for example, the recipient contacted you first, gave prior express consent, or the message is transactional. 2. Confirm that your organization **scrubs recipients against Do-Not-Call (DNC) registries** and honors opt-out and STOP requests. 3. Save. Outbound is enabled for your organization on the **Starter plan and above**, and the panel shows the attested basis and date. By attesting, you confirm you have a lawful basis to contact these recipients and accept responsibility for TCPA and related compliance. Receiving calls needs no attestation — inbound works as soon as an agent has a number. For SMS, keep the same standard: do not send spam, honor opt-outs, and only contact recipients where you have a lawful basis to do so. ## List or release numbers List numbers assigned to an agent: ```python theme={null} numbers = anima.phones.list(agent_id=agent_id) for phone in numbers: print(phone.phone_number, phone.ten_dlc_status) ``` ```ts theme={null} const numbers = await anima.phones.list({ agentId }); for (const phone of numbers.items) { console.log(phone.phoneNumber, phone.tenDlcStatus); } ``` Release a number when the agent no longer needs it: ```python theme={null} anima.phones.release( agent_id=agent_id, phone_number="+14155551234", ) ``` ```ts theme={null} await anima.phones.release({ agentId, phoneNumber: "+14155551234", }); ``` ## Next steps * [Quickstart: Voice Calls](/quickstart-voice) - Run the SMS-then-call demo end to end * [Conversational Calls](/conversational-calls) - Choose REST hosted or WebSocket-controlled calls * [Voice Catalog](/voice-catalog) - Browse available voices * [Call Intelligence](/call-intelligence) - Summaries, scoring, transcripts, and recordings * [Pricing & Limits](/pricing-and-limits) - Check included phone, SMS, and voice quotas * [Vault](/vault) - Store credentials without exposing raw secrets to the model # Pricing & Limits Source: https://docs.useanima.sh/pricing-and-limits Plan limits, enforced email/SMS quotas, phone/voice rates, MCP pricing, and call caps for Anima. # Pricing & Limits The canonical pricing page is [useanima.sh/pricing](https://useanima.sh/pricing). Use this page as the docs summary for plan limits and phone/voice meters. ## Plans | Plan | Price | Agent identities | Emails / mo | Phone numbers | SMS / mo | Voice | Vault credentials | Custom domains | | ---------- | ------------ | ---------------- | ----------- | ----------------------------- | ---------------- | ------------------------- | ----------------- | -------------- | | Free | `$0/forever` | 3 | 3,000 | — | — | — | 10 | — | | Starter | `$19/mo` | 25 | 25,000 | 1 included, then `$3/mo` each | 50 out + 50 in | 50 voice min · 50 calls | 500 | 10 | | Growth | `$199/mo` | 250 | 250,000 | 10 included | 500 out + 500 in | 600 voice min · 600 calls | 5,000 | 100 | | Enterprise | Custom | Unlimited | Negotiated | Pooled numbers | Negotiated | Negotiated | Negotiated | Negotiated | Notes: * **One agent is one identity.** The "agent identities" column is the agent count. An agent's email address, phone number and vault are attributes of that one identity, not identities of their own — an agent using all three still counts once. Each agent has exactly one inbox; adding a verified sending address to an agent does not use another identity. * **Free is hard-capped and email-only.** Phone numbers, SMS, and voice start on Starter, and Free includes no custom domain — agents use the platform default. Free also caps storage (5 GB) and vault credentials (10). * **Email and SMS quotas are enforced server-side** — sends beyond the monthly quota are rejected with a clear quota error, not silently billed. Email and SMS draw from separate pools. * **Phone-number caps are enforced**: Free 0, Starter 1 included, Growth 10 included. * **Vault credential caps are enforced on every tier.** * Paid tiers use the unit rates below when usage exceeds the included quota. ## Usage rate card US domestic rates. [useanima.sh/pricing](https://useanima.sh/pricing) renders the same numbers straight from the billing rate card and is authoritative if this table ever disagrees; [international rates](https://useanima.sh/pricing/international) are priced per destination country. | Meter | Rate | Notes | | -------------------------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | Email sent | `$0.50 / 1,000` | Above the included monthly quota. Inbound email is included. | | SMS sent, US 10DLC | `$0.02 / message` | Carrier and registration handling included in the published US rate. | | SMS received, US 10DLC | `$0.012 / message` | Applies to inbound replies. | | SMS sent, US toll-free | `$0.025 / message` | Toll-free numbers have separate carrier treatment. | | Voice | `$0.13 / min` | One voice tier — every agent uses the same high-quality pipeline. Calls to a mobile abroad add a per-country surcharge. | | Phone number, US local | `$3 / mo each` | Above included quota. | | Phone number, US toll-free | `$4 / mo each` | Above included quota. | | Outbound voice calls | `$0.03 / call above included` | An anti-abuse charge on call count, alongside the per-minute rate. | | Vault stored credentials | `$0.001 / credential / mo` | Above tier cap. | | MCP tool calls | Included | No per-tool-call charge. | | Webhook deliveries | Included | Includes retry budget. | ## Voice call caps Outbound voice calls are limited by both minutes and call count, and these are two different numbers. Included **minutes** are the allowance your plan pays for. The **call cap** is a separate anti-abuse ceiling that sits well above it, because a loop of very short calls can rack up per-call exposure long before a minute cap notices. | Plan | Included voice minutes | Monthly outbound-call cap | | ---------- | --------------------------- | ------------------------- | | Free | — (voice starts on Starter) | 0 | | Starter | 50 | 250 | | Growth | 600 | 1,000 | | Enterprise | Unmetered | 20,000 default | Anima also refuses more than **5 outbound calls per second** per organization. In practice that is the limit which stops a runaway loop — a `429` inside the first second, long before a monthly number matters. ## Compliance gates Outbound calls run through these server-side gates before dialing: * TCPA consent attestation (a one-time attestation your org completes in the console — there is no per-call consent field); fails closed with a `451` until your organization records a consent basis * Per-plan and per-second call caps * The voice spend ceiling: calls stop at your included minutes unless you opt in to metered overage and name a dollar limit If a gate blocks the call, the API rejects it before the telephony provider dials. Three things Anima does **not** do, which are commonly assumed: * **Reassigned Numbers Database scrubbing.** The lookup exists in the codebase but is disabled in production and has never run on a real call. * **Do-Not-Call scrubbing.** We do not check federal or state DNC registries. The `telemarketing_with_dnc_scrub` consent basis records that *you* scrubbed; it does not run a scrub. * **Calling-hour windows.** We do not compute the destination's local time. A call placed at 6am goes through. All three stay with you — see [What is the TCPA?](https://useanima.sh/tcpa). ## Metered overage By default your plan is a hard wall. Outbound voice stops when the included minutes are used up and the API returns a 402. Nothing is billed above your plan unless you ask for it. Asking for it is one setting in the console, under **Billing → Metered overage**. Turn it on, name a dollar limit, and voice keeps working above the included allowance and is billed at the rates above — up to that limit and no further. What to know before you enable it: * **A payment method is required.** Enabling without a card on file is refused, because authorizing spend we cannot collect is not a control. * **Your plan bounds the limit.** Free $100, Starter $5,000, Growth \$25,000. Enterprise is uncapped. * **It is a ceiling on new spend, not a wallet.** The limit is checked before each call against what you have already accrued this period. Lowering it stops the next call immediately, and you are still billed for what you already used. * **A failed invoice suspends it.** If a payment fails, the ceiling drops to zero and you are back to the hard wall until the payment clears. Stripe retries automatically and emails you. * **The check runs before a call, not during one.** A call already connected is not cut off mid-sentence — it finishes, and the next one is refused. The ceiling binds within about one call's length, not to the cent. * **Voice only, today.** Email, SMS, storage and the rest are bounded by their own per-tier quotas, which return a 402 at the limit. ## MCP pricing MCP access is included. You do not pay per MCP tool call. The underlying action can still consume a metered resource, such as SMS, voice minutes, a phone number, or stored vault credentials. ## Related docs * [Phone & Voice](/phone) * [Conversational Calls](/conversational-calls) * [MCP Servers](/mcp-servers) # Voice WebSocket Protocol Source: https://docs.useanima.sh/protocols/voice-websocket Full protocol reference for real-time voice call events over WebSocket, including message types, schemas, authentication, and error handling. # Voice WebSocket Protocol Stream real-time voice call events over a persistent WebSocket connection. Use this protocol for live dashboards, call monitoring, real-time transcription displays, and custom call control interfaces. ## Connection Setup ### Endpoint ``` wss://api.useanima.sh/v1/events/ws ``` ### Authentication Include your API key as a query parameter or in the first message after connection: ``` wss://api.useanima.sh/v1/events/ws?apiKey=ak_... ``` Or authenticate with an `auth` message after connecting: ```json theme={null} { "type": "auth", "apiKey": "ak_..." } ``` The server responds with an `auth.success` or `auth.error` message: ```json theme={null} { "type": "auth.success", "connectionId": "conn_abc123", "serverTime": "2025-01-15T10:30:00.000Z" } ``` ### SDK Connection The SDKs handle connection, authentication, and reconnection automatically. ```python theme={null} from anima import Anima anima = Anima(api_key="ak_...") stream = anima.events.connect() for event in stream: print(f"{event['type']}: {event['data']}") ``` ```ts theme={null} import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: "ak_..." }); const stream = anima.events.connect(); stream.on("event", (event) => { console.log(`${event.type}:`, event.data); }); stream.on("error", (err) => { console.error("Stream error:", err); }); ``` ## Message Format All messages follow a consistent envelope format: ```json theme={null} { "type": "string", "timestamp": "ISO 8601 string", "data": { } } ``` | Field | Type | Description | | ----------- | ------ | ------------------------------- | | `type` | string | Message type identifier | | `timestamp` | string | ISO 8601 timestamp of the event | | `data` | object | Event-specific payload | ## Message Types ### call.started Emitted when an outbound or inbound call is connected. ```json theme={null} { "type": "call.started", "timestamp": "2025-01-15T10:30:00.000Z", "data": { "callId": "call_abc123", "agentId": "ag_xyz789", "direction": "outbound", "from": "+14155551234", "to": "+14155555678", "voiceId": "voice_aria", "recordingEnabled": true } } ``` | Field | Type | Description | | ------------------ | ------- | ----------------------------- | | `callId` | string | Unique call identifier | | `agentId` | string | Agent handling the call | | `direction` | string | `"outbound"` or `"inbound"` | | `from` | string | Caller phone number (E.164) | | `to` | string | Callee phone number (E.164) | | `voiceId` | string | Voice model used for the call | | `recordingEnabled` | boolean | Whether recording is active | ### call.ringing Emitted when the outbound call is ringing on the recipient's end. ```json theme={null} { "type": "call.ringing", "timestamp": "2025-01-15T10:30:01.000Z", "data": { "callId": "call_abc123" } } ``` ### call.answered Emitted when the recipient picks up. ```json theme={null} { "type": "call.answered", "timestamp": "2025-01-15T10:30:05.000Z", "data": { "callId": "call_abc123", "answeredAt": "2025-01-15T10:30:05.000Z" } } ``` ### call.transcription Emitted in real-time as speech is transcribed. Segments may be partial (streaming) or final. ```json theme={null} { "type": "call.transcription", "timestamp": "2025-01-15T10:30:10.000Z", "data": { "callId": "call_abc123", "segmentId": "seg_001", "speaker": "caller", "text": "Hi, I'd like to schedule an appointment.", "isFinal": true, "confidence": 0.97, "startTime": 5.2, "endTime": 8.1, "language": "en" } } ``` | Field | Type | Description | | ------------ | ------- | ----------------------------------------- | | `segmentId` | string | Unique segment identifier | | `speaker` | string | `"agent"` or `"caller"` | | `text` | string | Transcribed text | | `isFinal` | boolean | `false` for partial, `true` for finalized | | `confidence` | number | Transcription confidence (0.0 - 1.0) | | `startTime` | number | Seconds from call start | | `endTime` | number | Seconds from call start | | `language` | string | Detected language code | ### call.agent\_response Emitted when the agent generates a response that will be spoken. ```json theme={null} { "type": "call.agent_response", "timestamp": "2025-01-15T10:30:11.000Z", "data": { "callId": "call_abc123", "text": "Of course! I can help you with that. What day works best for you?", "tokensUsed": 42, "latencyMs": 320 } } ``` ### call.dtmf Emitted when the caller presses a key on their phone keypad (DTMF tone). ```json theme={null} { "type": "call.dtmf", "timestamp": "2025-01-15T10:31:00.000Z", "data": { "callId": "call_abc123", "digit": "1", "durationMs": 120 } } ``` | Field | Type | Description | | ------------ | ------ | ---------------------------------------- | | `digit` | string | Key pressed: `"0"`-`"9"`, `"*"`, `"#"` | | `durationMs` | number | How long the key was held (milliseconds) | ### call.hold Emitted when the call is placed on or taken off hold. ```json theme={null} { "type": "call.hold", "timestamp": "2025-01-15T10:32:00.000Z", "data": { "callId": "call_abc123", "status": "on_hold" } } ``` | Field | Type | Description | | -------- | ------ | -------------------------- | | `status` | string | `"on_hold"` or `"resumed"` | ### call.transfer Emitted when the call is transferred to another number or agent. ```json theme={null} { "type": "call.transfer", "timestamp": "2025-01-15T10:33:00.000Z", "data": { "callId": "call_abc123", "transferTo": "+14155559999", "reason": "Customer requested human agent" } } ``` ### call.sentiment Emitted periodically with rolling sentiment analysis. ```json theme={null} { "type": "call.sentiment", "timestamp": "2025-01-15T10:33:30.000Z", "data": { "callId": "call_abc123", "current": "positive", "score": 0.72, "trend": "improving" } } ``` | Field | Type | Description | | --------- | ------ | ------------------------------------------- | | `current` | string | `"positive"`, `"neutral"`, or `"negative"` | | `score` | number | Sentiment score (-1.0 to 1.0) | | `trend` | string | `"improving"`, `"stable"`, or `"declining"` | ### call.ended Emitted when the call terminates for any reason. ```json theme={null} { "type": "call.ended", "timestamp": "2025-01-15T10:35:00.000Z", "data": { "callId": "call_abc123", "reason": "completed", "durationSeconds": 300, "recordingUrl": "https://recordings.useanima.sh/call_abc123.wav", "transcriptAvailable": true, "summary": "Caller scheduled an appointment for January 20th at 2 PM." } } ``` | Field | Type | Description | | --------------------- | ------- | ---------------------------------------------------------------------------------------------------- | | `reason` | string | `"completed"`, `"caller_hangup"`, `"agent_hangup"`, `"no_answer"`, `"busy"`, `"failed"`, `"timeout"` | | `durationSeconds` | number | Total call duration in seconds | | `recordingUrl` | string | Signed URL to the recording (if enabled) | | `transcriptAvailable` | boolean | Whether the full transcript is ready | | `summary` | string | AI-generated call summary | ### call.error Emitted when an error occurs during the call. ```json theme={null} { "type": "call.error", "timestamp": "2025-01-15T10:35:01.000Z", "data": { "callId": "call_abc123", "code": "voice_synthesis_timeout", "message": "Voice synthesis did not respond within 5 seconds", "recoverable": true } } ``` ## Subscribing to Specific Calls Filter events to a specific call or agent by sending a `subscribe` message: ```json theme={null} { "type": "subscribe", "filters": { "callId": "call_abc123" } } ``` ```json theme={null} { "type": "subscribe", "filters": { "agentId": "ag_xyz789", "eventTypes": ["call.transcription", "call.ended"] } } ``` The server confirms with: ```json theme={null} { "type": "subscribe.success", "subscriptionId": "sub_abc123", "filters": { "agentId": "ag_xyz789", "eventTypes": ["call.transcription", "call.ended"] } } ``` ## Heartbeat / Ping-Pong The server sends a `ping` message every 30 seconds. The client must respond with a `pong` within 10 seconds or the connection will be closed. ```json theme={null} // Server sends { "type": "ping", "timestamp": "2025-01-15T10:30:30.000Z" } // Client responds { "type": "pong" } ``` The SDKs handle ping-pong automatically. If you are implementing a raw WebSocket client, ensure you respond to every `ping`. ## Error Handling ### Connection Errors | Code | Reason | Action | | ------ | ------------------------ | ------------------------------------------------- | | `4001` | Invalid API key | Check your API key and reconnect | | `4002` | Rate limit exceeded | Back off and retry after the `Retry-After` header | | `4003` | Connection limit reached | Close unused connections before opening new ones | | `4008` | Ping timeout | Client did not respond to ping in time; reconnect | | `4500` | Internal server error | Retry with exponential backoff | ### Reconnection Strategy Use exponential backoff with jitter for automatic reconnection: 1. First retry: 1 second 2. Second retry: 2 seconds 3. Third retry: 4 seconds 4. Max backoff: 30 seconds 5. Add random jitter of 0-1 seconds to each delay ```ts theme={null} // The SDK handles reconnection automatically const stream = anima.events.connect({ reconnect: true, // default: true maxReconnectAttempts: 10, // default: 10 maxReconnectDelay: 30000, // default: 30s }); stream.on("reconnecting", (attempt) => { console.log(`Reconnecting (attempt ${attempt})...`); }); stream.on("reconnected", () => { console.log("Reconnected successfully"); }); ``` ```python theme={null} stream = anima.events.connect( reconnect=True, max_reconnect_attempts=10, max_reconnect_delay=30000, ) ``` ## Next Steps * [Quickstart: Voice Calls](/quickstart-voice) -- Make your first AI-powered phone call * [Voice Catalog](/voice-catalog) -- Browse available voice models * [Call Intelligence](/call-intelligence) -- Recording, transcription, and scoring * [Webhooks](/webhooks) -- HTTP-based event delivery as an alternative to WebSocket # Quickstart: Email Source: https://docs.useanima.sh/quickstart-email Send your first email from an AI agent in under 5 minutes. # Quickstart: Email Give your AI agent a real email address and send its first message. ## Prerequisites * Your org's **master key** (`mk_...`) from [console.useanima.sh](https://console.useanima.sh) — creating agents is a master-key operation * Your organization ID (`org_...`), shown in the console * Python 3.10+ or Node.js 18+ ## Python ```bash theme={null} pip install anima-labs ``` ```python theme={null} from anima import Anima anima = Anima(api_key="mk_...") # master key — agent creation is admin-gated # Create an agent with an email inbox agent = anima.agents.create( org_id="org_...", # your organization ID, from the console name="My First Agent", slug="my-first-agent", ) print(f"Agent: {agent.id}") # Send an email anima.messages.send_email( agent_id=agent.id, to=["recipient@example.com"], subject="Hello from my AI agent", body="This email was sent by an AI agent powered by Anima.", ) print("Email sent!") ``` ## Node.js / TypeScript ```bash theme={null} npm install @anima-labs/sdk ``` ```ts theme={null} import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: "mk_..." }); // master key — agent creation is admin-gated // Create an agent with an email inbox const agent = await anima.agents.create({ orgId: "org_...", // your organization ID, from the console name: "My First Agent", slug: "my-first-agent", }); console.log(`Agent: ${agent.id}`); // Send an email await anima.messages.sendEmail({ agentId: agent.id, to: ["recipient@example.com"], subject: "Hello from my AI agent", body: "This email was sent by an AI agent powered by Anima.", }); console.log("Email sent!"); ``` ## What's Next * [Quickstart: Vault](/quickstart-vault) — Store credentials securely * [Webhooks](/webhooks) — Get notified when emails arrive * [Custom Domains](/custom-domains) — Use your own domain # Quickstart: Vault Source: https://docs.useanima.sh/quickstart-vault Store and retrieve encrypted credentials for your AI agent. # Quickstart: Vault Give your AI agent secure access to credentials — logins, API keys, and secrets. ## Prerequisites * An Anima API key from [console.useanima.sh](https://console.useanima.sh) * Python 3.10+ or Node.js 18+ ## Python ```bash theme={null} pip install anima-labs ``` ```python theme={null} from anima import Anima anima = Anima(api_key="mk_...") # master key — agent creation is admin-gated # Create an agent agent = anima.agents.create( org_id="org_...", # your organization ID, from the console name="Web Agent", slug="web-agent", ) # Provision a vault for the agent anima.vault.provision(agent_id=agent.id) # Store a login credential credential = anima.vault.create_credential( agent_id=agent.id, type="login", name="CRM Login", login={ "username": "bot@company.com", "password": "s3cur3-p4ssw0rd", "uris": [{"uri": "https://crm.company.com"}], }, ) print(f"Stored: {credential.name}") # Or let the vault generate the password server-side — it is stored with # the credential and never returned; you get back only the credential ref. generated = anima.vault.create_credential( agent_id=agent.id, type="login", name="Acme Portal", login={"username": "bot@company.com"}, generate_password={}, # defaults: 24 chars, all character classes ) print(f"Credential ref: {generated.id}") # Retrieve it later creds = anima.vault.get_credential( agent_id=agent.id, credential_id=credential.id, ) print(f"Username: {creds.username}") # Clean up anima.vault.delete_credential( agent_id=agent.id, credential_id=credential.id, ) anima.vault.deprovision(agent_id=agent.id) ``` ## Node.js / TypeScript ```bash theme={null} npm install @anima-labs/sdk ``` ```ts theme={null} import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: "mk_..." }); // master key — agent creation is admin-gated // Create an agent const agent = await anima.agents.create({ orgId: "org_...", // your organization ID, from the console name: "Web Agent", slug: "web-agent", }); // Provision a vault await anima.vault.provision({ agentId: agent.id }); // Store a login credential const credential = await anima.vault.createCredential({ agentId: agent.id, type: "login", name: "CRM Login", login: { username: "bot@company.com", password: "s3cur3-p4ssw0rd", uris: [{ uri: "https://crm.company.com" }], }, }); console.log(`Stored: ${credential.name}`); // Or let the vault generate the password server-side — it is stored with // the credential and never returned; you get back only the credential ref. const generated = await anima.vault.createCredential({ agentId: agent.id, type: "login", name: "Acme Portal", login: { username: "bot@company.com" }, generatePassword: {}, // defaults: 24 chars, all character classes }); console.log(`Credential ref: ${generated.id}`); // Retrieve it later const creds = await anima.vault.getCredential({ agentId: agent.id, credentialId: credential.id, }); console.log(`Username: ${creds.username}`); ``` ## Credential Types The vault supports four credential types: | Type | Use Case | | ------------- | ------------------------------------------- | | `login` | Website logins (username + password + URIs) | | `secure_note` | Free-form encrypted text (API keys, tokens) | | `card` | Payment card details | | `identity` | Personal/business identity information | ## What's Next * [Quickstart: Email](/quickstart-email) — Send emails from agents * [Security](/security) — Understand Anima's security model * [Encryption](/encryption) — How vault data is encrypted # Quickstart: Voice Calls Source: https://docs.useanima.sh/quickstart-voice Text yourself, then place your first AI-powered phone call from an Anima agent. # Quickstart: Voice Calls Give your AI agent a real phone number, send yourself an SMS, then place the first voice call to a number you control. ## Prerequisites * An Anima API key from [console.useanima.sh](https://console.useanima.sh) * Python 3.10+ or Node.js 18+ * An existing `agent_id` * Phone access for SMS * Voice access for outbound calls * Consent to contact the destination number If you are testing for the first time, use your own phone number as the destination. ## Python ```bash theme={null} pip install anima-labs ``` ```python theme={null} from anima import Anima anima = Anima(api_key="ak_...") agent_id = "AGENT_ID" your_phone = "+15551234567" # 1. Provision a phone number for SMS and voice. phone = anima.phones.provision( agent_id=agent_id, country_code="US", capabilities=["voice", "sms"], ) print(f"Phone: {phone.phone_number}") # 2. Text yourself first. This proves the number is real and reachable. sms = anima.messages.send_sms( agent_id=agent_id, to=your_phone, body="Hi - this is my Anima agent texting before it calls.", ) print(f"SMS sent: {sms.id} ({sms.status})") # 3. Confirm the voice catalog is available. voices = anima.voices.list() print(f"Available voices: {len(voices['voices'])}") # 4. Place the first outbound call. call = anima.calls.create( agent_id=agent_id, to=your_phone, greeting="Hi, this is my Anima agent. I am calling from my own phone number.", ) print(f"Call started: {call.call_id}, state: {call.state}") # 5. After the call ends, fetch the transcript. transcript = anima.calls.get_transcript(call.call_id) for segment in transcript.segments: print(f"{segment.speaker}: {segment.text}") ``` ## Node.js / TypeScript ```bash theme={null} npm install @anima-labs/sdk ``` ```ts theme={null} import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: "ak_..." }); const agentId = "AGENT_ID"; const yourPhone = "+15551234567"; // 1. Provision a phone number for SMS and voice. const phone = await anima.phones.provision({ agentId, countryCode: "US", capabilities: ["voice", "sms"], }); console.log(`Phone: ${phone.phoneNumber}`); // 2. Text yourself first. This proves the number is real and reachable. const sms = await anima.messages.sendSms({ agentId, to: yourPhone, body: "Hi - this is my Anima agent texting before it calls.", }); console.log(`SMS sent: ${sms.id} (${sms.status})`); // 3. Confirm the voice catalog is available. const voices = await anima.voices.list(); console.log(`Available voices: ${voices.voices.length}`); // 4. Place the first outbound call. const call = await anima.calls.create({ agentId, to: yourPhone, greeting: "Hi, this is my Anima agent. I am calling from my own phone number.", }); console.log(`Call started: ${call.callId}, state: ${call.state}`); // 5. After the call ends, fetch the transcript. const transcript = await anima.calls.getTranscript(call.callId); for (const segment of transcript.segments) { console.log(`${segment.speaker}: ${segment.text}`); } ``` ## Environment Variables You can also configure via environment variables instead of passing options directly: ```bash theme={null} export ANIMA_API_KEY="ak_..." export ANIMA_API_URL="https://api.useanima.sh" # optional, defaults to production export ANIMA_LOG="debug" # optional, enables debug logging ``` ```python theme={null} # No need to pass api_key when ANIMA_API_KEY is set from anima import Anima anima = Anima() ``` ```ts theme={null} // No need to pass apiKey when ANIMA_API_KEY is set const anima = new Anima(); ``` ## Handle Inbound Calls To react after calls complete, set up a webhook endpoint and subscribe to `call.ended`. Inbound real-time call control happens over the voice WebSocket. ```python theme={null} from anima import Anima, fastapi_webhook_dependency from fastapi import FastAPI, Depends app = FastAPI() webhook_dep = fastapi_webhook_dependency("whsec_...") @app.post("/webhooks") async def handle(event=Depends(webhook_dep)): if event.type == "call.ended": print(f"Call ended: {event.data['callId']}") ``` ```ts theme={null} import express from "express"; import { Anima, webhookMiddleware } from "@anima-labs/sdk"; const app = express(); app.use(express.json()); app.post("/webhooks", webhookMiddleware("whsec_..."), (req, res) => { const event = req.webhookEvent; if (event.type === "call.ended") { console.log(`Call ended: ${event.data.callId}`); } res.sendStatus(200); }); ``` ## Real-Time Call Events (WebSocket) Monitor and control calls in real time over the voice WebSocket: ```ts theme={null} const anima = new Anima({ apiKey: "ak_..." }); const conn = anima.calls.connect({ agentId }); conn.on("message", (message) => { switch (message.type) { case "call.started": console.log("Call started:", message.data?.callId); break; case "call.transcription": console.log(`[${message.data?.speaker}]: ${message.data?.text}`); break; case "call.ended": console.log("Call ended:", message.data?.callId); break; } }); conn.createCall(yourPhone); ``` ## What's Next? * [Voice Catalog](/voice-catalog) — Browse all available voices with audio samples * [Call Intelligence](/call-intelligence) — Recording, transcription, RAG, and scoring * [Conversational Calls](/conversational-calls) — REST hosted, realtime, and WebSocket call modes * [Voice WebSocket Protocol](/protocols/voice-websocket) — Full protocol reference for real-time events * [MCP Setup](/mcp-servers) — Use voice calls from Claude Desktop and other AI tools # Agent Registry Source: https://docs.useanima.sh/registry/overview Discover, search, and register AI agents in the Anima Agent Registry. Public and private registry modes. # Agent Registry The Anima Agent Registry is a searchable directory of agents and their capabilities. It enables agent discovery, facilitates A2A communication, and provides a trust layer through verified identity records. ## How It Works 1. **Register** -- Agents publish their identity, capabilities, and endpoints to the registry 2. **Discover** -- Other agents or services search the registry by capability, domain, or name 3. **Verify** -- Registry entries are linked to DIDs and Agent Cards for cryptographic verification 4. **Connect** -- Use discovered endpoints to initiate A2A communication ## Registering an Agent ```ts theme={null} import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: "ak_..." }); const entry = await anima.registry.register({ agentId: "ag_8f3k2m9x1n4p7q6r", visibility: "public", tags: ["procurement", "invoicing", "payments"], description: "Handles procurement workflows for Acme Corp", }); console.log(`Registered: ${entry.id}`); ``` ```python theme={null} from anima import Anima anima = Anima(api_key="ak_...") entry = anima.registry.register( agent_id="ag_8f3k2m9x1n4p7q6r", visibility="public", tags=["procurement", "invoicing", "payments"], description="Handles procurement workflows for Acme Corp", ) print(f"Registered: {entry.id}") ``` ```go theme={null} import "github.com/anima-labs-ai/go" client := anima.NewClient("ak_...") entry, err := client.Registry.Register(ctx, &anima.RegisterAgentParams{ AgentID: "ag_8f3k2m9x1n4p7q6r", Visibility: "public", Tags: []string{"procurement", "invoicing", "payments"}, Description: "Handles procurement workflows for Acme Corp", }) ``` ## Searching the Registry ```ts theme={null} // Search by capability const results = await anima.registry.search({ query: "invoice processing", tags: ["payments"], limit: 10, }); for (const agent of results.data) { console.log(`${agent.name} -- ${agent.did}`); console.log(` Capabilities: ${agent.capabilities.map(c => c.name).join(", ")}`); console.log(` Endpoint: ${agent.endpoints.a2a}`); } ``` ```python theme={null} results = anima.registry.search( query="invoice processing", tags=["payments"], limit=10, ) for agent in results.data: print(f"{agent.name} -- {agent.did}") print(f" Capabilities: {', '.join(c.name for c in agent.capabilities)}") ``` ## Visibility Modes | Mode | Description | | ---------- | ---------------------------------------------------------- | | `public` | Visible to all registry users. Searchable by anyone. | | `private` | Only visible within your organization's pod. | | `unlisted` | Not searchable, but accessible via direct DID or agent ID. | ## API Reference | Endpoint | Method | Description | | ------------------------------------------------- | ------ | ----------------------- | | `https://api.useanima.sh/api/registry/agents` | POST | Register an agent | | `https://api.useanima.sh/api/registry/agents/:id` | GET | Get a registry entry | | `https://api.useanima.sh/api/registry/agents/:id` | PUT | Update a registry entry | | `https://api.useanima.sh/api/registry/agents/:id` | DELETE | Deregister an agent | | `https://api.useanima.sh/api/registry/search` | GET | Search the registry | ### Search Parameters | Parameter | Type | Description | | ------------ | --------- | -------------------------------------------- | | `query` | string | Free-text search across name and description | | `tags` | string\[] | Filter by capability tags | | `visibility` | string | Filter by visibility mode | | `limit` | number | Max results (default 20, max 100) | | `cursor` | string | Pagination cursor | ## Configuration | Variable | Default | Description | | ----------------------------- | --------- | ---------------------------------------- | | `ANIMA_REGISTRY_DEFAULT_VIS` | `private` | Default visibility for new registrations | | `ANIMA_REGISTRY_SEARCH_LIMIT` | `20` | Default search result limit | ## Next Steps * [Agent Cards](/identity/agent-cards) -- Publish detailed agent metadata * [A2A Protocol](/a2a/overview) -- Communicate with discovered agents * [Verifiable Credentials](/identity/verifiable-credentials) -- Prove agent capabilities # Official SDKs Source: https://docs.useanima.sh/sdks Official TypeScript and Python SDKs for Anima. # Official SDKs Seamlessly integrate Anima into your Python and TypeScript applications. Both SDKs provide access to the full unified platform: agents, email, vault, phone, and webhooks. Using Go? See the [Go SDK reference](/sdks/go). ## TypeScript / Node.js The TypeScript SDK provides full type safety and works with Node.js, Bun, and Deno. ### Installation ```bash theme={null} npm install @anima-labs/sdk ``` ### Usage ```ts theme={null} import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: "mk_..." }); // agent creation needs the master key // Create an agent const agent = await anima.agents.create({ orgId: "org_...", name: "Researcher", slug: "researcher", }); // Send email await anima.messages.sendEmail({ agentId: agent.id, to: ["user@example.com"], subject: "Hello", body: "Sent by an AI agent", }); ``` ## Python The Python SDK is fully typed and supports both sync and async operations. ### Installation ```bash theme={null} pip install anima-labs ``` ### Usage ```python theme={null} from anima import Anima anima = Anima(api_key="mk_...") # agent creation needs the master key # Create an agent agent = anima.agents.create( org_id="org_...", name="Researcher", slug="researcher", ) # Send email anima.messages.send_email( agent_id=agent.id, to=["user@example.com"], subject="Hello", body="Sent by an AI agent", ) ``` ### Async Usage ```python theme={null} from anima import AsyncAnima anima = AsyncAnima(api_key="mk_...") agent = await anima.agents.create( org_id="org_...", name="Async Agent", slug="async-agent", ) ``` ## Available Resources Both SDKs expose the same set of resources: | Resource | Description | | --------------- | ------------------------------------------- | | `agents` | Create and manage AI agents | | `organizations` | Organization settings and profile | | `messages` | Send email and SMS as an agent | | `emails` | List an agent's mail and handle attachments | | `vault` | Store and retrieve encrypted credentials | | `phones` | Provision and manage phone numbers | | `calls` | Place and manage voice calls | | `webhooks` | Subscribe to real-time events | | `domains` | Configure custom email domains | | `security` | Content scanning and security events | …plus `identity`, `registry`, `pods`, `a2a`, `audit`, `compliance`, `anomaly`, `voices`, `events`, and `addresses` — the same names in both SDKs (snake\_case arguments in Python, camelCase in TypeScript). # Go SDK Source: https://docs.useanima.sh/sdks/go Quickstart and reference for the Anima Go SDK. Manage agents, email, vault, identity, and A2A from Go applications. # Go SDK The official Anima Go SDK provides idiomatic Go access to the full Anima platform: agents, email, vault, identity, registry, and A2A. ## Installation ```bash theme={null} go get github.com/anima-labs-ai/go ``` Requires Go 1.21 or later. ## Quickstart ```go theme={null} package main import ( "context" "fmt" "log" "github.com/anima-labs-ai/go" ) func main() { ctx := context.Background() client := anima.NewClient("ak_...") // Create an agent agent, err := client.Agents.Create(ctx, &anima.CreateAgentParams{ Name: "Go Agent", }) if err != nil { log.Fatal(err) } fmt.Printf("Agent: %s\n", agent.ID) // Send an email _, err = client.Messages.SendEmail(ctx, &anima.SendEmailParams{ AgentID: agent.ID, To: "user@example.com", Subject: "Hello from Go", Body: "This email was sent by an AI agent using the Go SDK.", }) if err != nil { log.Fatal(err) } fmt.Println("Email sent!") } ``` ## Client Configuration ```go theme={null} // Basic client client := anima.NewClient("ak_...") // Client with options client := anima.NewClient("ak_...", anima.WithBaseURL("https://api.useanima.sh"), anima.WithTimeout(30 * time.Second), anima.WithRetries(3), anima.WithPodID("pod_prod"), ) ``` ### Configuration Options | Option | Default | Description | | ---------------- | ------------------------- | ----------------------------- | | `WithBaseURL` | `https://api.useanima.sh` | API base URL | | `WithTimeout` | `30s` | HTTP request timeout | | `WithRetries` | `2` | Max retry attempts on failure | | `WithPodID` | none | Scope all requests to a pod | | `WithHTTPClient` | `http.DefaultClient` | Custom HTTP client | | `WithUserAgent` | `anima-go/` | Custom User-Agent header | ## Resource Reference ### Agents ```go theme={null} // Create agent, err := client.Agents.Create(ctx, &anima.CreateAgentParams{ Name: "Research Bot", }) // Get agent, err := client.Agents.Get(ctx, "ag_abc123") // List agents, err := client.Agents.List(ctx, &anima.ListAgentsParams{ Limit: 20, }) // Update agent, err := client.Agents.Update(ctx, "ag_abc123", &anima.UpdateAgentParams{ Name: "Updated Bot", }) // Delete err := client.Agents.Delete(ctx, "ag_abc123") ``` ### Email ```go theme={null} // Send email msg, err := client.Messages.SendEmail(ctx, &anima.SendEmailParams{ AgentID: "ag_abc123", To: "user@example.com", Subject: "Hello", Body: "Sent from Go", }) // List messages messages, err := client.Messages.List(ctx, &anima.ListMessagesParams{ AgentID: "ag_abc123", Limit: 50, }) // Get a message msg, err := client.Messages.Get(ctx, "msg_xyz789") ``` ### Vault ```go theme={null} // Store a secret secret, err := client.Vault.Store(ctx, &anima.StoreSecretParams{ AgentID: "ag_abc123", Key: "openai_key", Value: "sk-...", Tags: []string{"llm", "production"}, }) // Retrieve a secret secret, err := client.Vault.Get(ctx, "ag_abc123", "openai_key") fmt.Println(secret.Value) // List secrets (metadata only) secrets, err := client.Vault.List(ctx, &anima.ListSecretsParams{ AgentID: "ag_abc123", }) ``` ### Identity ```go theme={null} // Resolve DID doc, err := client.Identity.ResolveDID(ctx, "did:web:agents.useanima.sh:org_abc123:ag_abc123") // Issue a credential cred, err := client.Identity.Credentials.Issue(ctx, &anima.IssueCredentialParams{ AgentID: "ag_abc123", Type: "AgentAuthorization", Claims: map[string]any{"role": "purchasing-agent"}, }) // Publish Agent Card card, err := client.Identity.AgentCards.Publish(ctx, &anima.PublishAgentCardParams{ AgentID: "ag_abc123", Domain: "agent.acme.com", }) ``` ### A2A ```go theme={null} // Submit a task to one of your own agents task, err := client.A2A.SubmitTask(ctx, "ag_receiver", anima.SubmitA2ATaskParams{ Input: map[string]any{"dataset": "q1-sales"}, }) // Get task result task, err = client.A2A.GetTask(ctx, "ag_receiver", task.ID) fmt.Printf("Status: %s\n", task.Status) // Dispatch a signed task to another agent by DID task, err = client.A2A.Dispatch(ctx, "ag_sender", anima.DispatchA2ATaskParams{ ToDID: "did:web:agents.useanima.sh:org_xyz:ag_receiver", Type: "purchase-order", Input: map[string]any{"budget": 200}, }) // Discover another agent's public Agent Card card, err := client.A2A.Discover(ctx, "https://receiver.useanima.sh") ``` See [A2A Protocol](/a2a/overview) for the full task lifecycle and CLI equivalents. ### Wallet ```go theme={null} // Create a wallet wallet, err := client.Wallet.Create(ctx, &anima.CreateWalletParams{ AgentID: "ag_abc123", Currency: "usd", BudgetGuards: &anima.BudgetGuards{ DailyLimitCents: 50000, }, }) // Check balance balance, err := client.Wallet.GetBalance(ctx, wallet.ID) fmt.Printf("Available: %d cents\n", balance.AvailableCents) ``` ### Webhooks ```go theme={null} // Create a webhook, with the auth Anima presents to your endpoint // (on top of the X-Anima-Signature HMAC) plus delivery throttling. rateLimit, maxAttempts := 120, 5 wh, err := client.Webhooks.Create(ctx, anima.CreateWebhookParams{ URL: "https://example.com/hooks/anima", Events: []anima.WebhookEventType{anima.WebhookEventMessageReceived}, // Also: NewBasicAuth(user, pass), NewCustomHeaderAuth(name, value), NewNoAuth(). AuthConfig: anima.NewBearerAuth("your-endpoint-token"), RateLimitPerMinute: &rateLimit, // omit for unlimited MaxAttempts: &maxAttempts, // 1-10, default 3 }) // The credential is write-only — only the scheme (wh.AuthType) is returned. fmt.Println(wh.ID, wh.AuthType) ``` ## Error Handling The SDK uses typed errors for common failure cases: ```go theme={null} agent, err := client.Agents.Get(ctx, "ag_nonexistent") if err != nil { var apiErr *anima.APIError if errors.As(err, &apiErr) { fmt.Printf("API error: %d %s\n", apiErr.StatusCode, apiErr.Message) fmt.Printf("Request ID: %s\n", apiErr.RequestID) } log.Fatal(err) } ``` | Error Type | Description | | ------------------ | ------------------------------------------ | | `*APIError` | API returned an error response | | `*AuthError` | Invalid or expired API key | | `*RateLimitError` | Rate limit exceeded (includes retry-after) | | `*ValidationError` | Invalid request parameters | | `*NotFoundError` | Resource not found | ## Pagination All list endpoints support cursor-based pagination: ```go theme={null} var allAgents []anima.Agent cursor := "" for { result, err := client.Agents.List(ctx, &anima.ListAgentsParams{ Limit: 100, Cursor: cursor, }) if err != nil { log.Fatal(err) } allAgents = append(allAgents, result.Data...) if !result.HasMore { break } cursor = result.NextCursor } ``` ## Next Steps * [Official SDKs](/sdks) -- TypeScript and Python SDK documentation * [Getting Started](/getting-started) -- Platform quickstart guide * [Examples](/examples) -- Complete runnable examples # SDK Migration Guide Source: https://docs.useanima.sh/sdks/migration Upgrade to the latest Anima SDKs with auto-pagination, env var fallback, debug logging, per-request options, and more. # SDK Migration Guide This guide covers the new features in the latest Anima SDKs and how to adopt them. The upgrade is non-breaking -- your existing code will continue to work. ## What's New | Feature | Description | | -------------------------------- | --------------------------------------------------- | | Auto-pagination (`PageIterator`) | Automatically iterate through all pages of results | | Environment variable fallback | SDK reads `ANIMA_API_KEY` if no key is passed | | Debug logging | Built-in structured logging with `ANIMA_LOG` | | Per-request options | Override timeout, headers, and idempotency per call | | Raw response access | Get the full HTTP response alongside typed data | | Request/response events | Hook into the request lifecycle for observability | | Webhook middleware | Framework-native webhook verification | ## Auto-Pagination (PageIterator) No more manual cursor management. The new `PageIterator` handles pagination automatically. ### Before ```python theme={null} # Python -- manual pagination all_agents = [] cursor = None while True: result = anima.agents.list(limit=100, cursor=cursor) all_agents.extend(result["items"]) if not result.get("hasMore"): break cursor = result["nextCursor"] ``` ```ts theme={null} // TypeScript -- manual pagination const allAgents = []; let cursor: string | undefined; do { const result = await anima.agents.list({ limit: 100, cursor }); allAgents.push(...result.items); cursor = result.hasMore ? result.nextCursor : undefined; } while (cursor); ``` ### After ```python theme={null} # Python -- auto-pagination for agent in anima.agents.list_auto_paging(limit=100): print(agent["name"]) # Or collect all at once all_agents = list(anima.agents.list_auto_paging()) ``` ```ts theme={null} // TypeScript -- auto-pagination for await (const agent of anima.agents.listAutoPaging({ limit: 100 })) { console.log(agent.name); } // Or collect all at once const allAgents = await anima.agents.listAutoPaging().toArray(); ``` ## Environment Variable Fallback The SDK now reads configuration from environment variables automatically. The `apiKey` parameter is now optional -- if omitted, the SDK looks for `ANIMA_API_KEY`. ### Before ```python theme={null} # Python -- api_key was required anima = Anima(api_key="ak_...") ``` ```ts theme={null} // TypeScript -- apiKey was required const anima = new Anima({ apiKey: "ak_..." }); ``` ### After ```bash theme={null} # Set environment variables export ANIMA_API_KEY="ak_..." export ANIMA_API_URL="https://api.useanima.sh" # optional ``` ```python theme={null} # Python -- no arguments needed from anima import Anima anima = Anima() # Explicit key still works and takes precedence anima = Anima(api_key="ak_override") ``` ```ts theme={null} // TypeScript -- no arguments needed import { Anima } from "@anima-labs/sdk"; const anima = new Anima(); // Explicit key still works and takes precedence const anima2 = new Anima({ apiKey: "ak_override" }); ``` ### Supported Environment Variables | Variable | Description | Default | | --------------- | -------------------------------------------- | ------------------------- | | `ANIMA_API_KEY` | API key for authentication | none | | `ANIMA_API_URL` | Base URL for the API | `https://api.useanima.sh` | | `ANIMA_LOG` | Log level (`debug`, `info`, `warn`, `error`) | `warn` | ## Debug Logging Enable structured debug logs to inspect HTTP requests, retries, and timing. ### Before ```python theme={null} # Python -- no built-in logging import logging logging.basicConfig(level=logging.DEBUG) # ...but SDK didn't emit structured logs ``` ### After ```bash theme={null} export ANIMA_LOG=debug ``` ```python theme={null} # Python -- structured logging built in anima = Anima(log="debug") # Output: # [anima] POST /v1/agents 201 (142ms) # [anima] GET /v1/agents/ag_abc123 200 (89ms) # [anima] GET /v1/agents/ag_abc123 429 -- retrying in 1s (attempt 1/3) ``` ```ts theme={null} // TypeScript -- structured logging built in const anima = new Anima({ log: "debug" }); // Output: // [anima] POST /v1/agents 201 (142ms) // [anima] GET /v1/agents/ag_abc123 200 (89ms) ``` ## Per-Request Options Override client-level settings on a per-request basis. Useful for setting custom timeouts, idempotency keys, or extra headers. ### Before ```python theme={null} # Python -- no per-request overrides # Had to create separate client instances for different timeouts anima_fast = Anima(api_key="ak_...", timeout=5) anima_slow = Anima(api_key="ak_...", timeout=60) ``` ### After ```python theme={null} # Python -- per-request options from anima import RequestOptions agent = anima.agents.create( org_id="org_...", name="My Agent", slug="my-agent", options=RequestOptions( timeout=60, idempotency_key="create-agent-abc", ), ) ``` ```ts theme={null} // TypeScript -- per-request options const agent = await anima.agents.create( { orgId: "org_...", name: "My Agent", slug: "my-agent" }, { timeout: 60_000, idempotencyKey: "create-agent-abc", }, ); ``` ## Raw Response Access Access the full HTTP response (status, headers) alongside the parsed body. ### Before ```python theme={null} # Python -- only parsed data was available agent = anima.agents.get("ag_abc123") # No access to status code, headers, or request ID ``` ### After ```python theme={null} # Python -- raw response access response = anima.agents.with_raw_response.get("ag_abc123") print(f"Status: {response.status_code}") print(f"Request ID: {response.headers['x-request-id']}") print(f"Rate limit remaining: {response.headers['x-ratelimit-remaining']}") agent = response.parsed # typed data, same as before print(f"Agent: {agent['name']}") ``` ```ts theme={null} // TypeScript -- raw response access const response = await anima.agents.withRawResponse.get("ag_abc123"); console.log(`Status: ${response.status}`); console.log(`Request ID: ${response.headers.get("x-request-id")}`); console.log(`Rate limit remaining: ${response.headers.get("x-ratelimit-remaining")}`); const agent = response.data; // typed data, same as before console.log(`Agent: ${agent.name}`); ``` ## Request/Response Events Hook into the SDK's request lifecycle for logging, metrics, or tracing. ### Before ```python theme={null} # Python -- no event hooks # Had to wrap the client or monkey-patch methods ``` ### After ```python theme={null} # Python -- request/response events def on_request(event): print(f"--> {event['method']} {event['url']}") def on_response(event): print(f"<-- {event['status']} ({event['durationMs']}ms)") anima = Anima( on_request=on_request, on_response=on_response, ) # Every SDK call now emits events: # --> POST /v1/agents # <-- 201 (142ms) ``` ```ts theme={null} // TypeScript -- request/response events const anima = new Anima({ onRequest: (event) => { console.log(`--> ${event.method} ${event.url}`); }, onResponse: (event) => { console.log(`<-- ${event.status} (${event.durationMs}ms)`); }, }); ``` ## Webhook Middleware Framework-native webhook verification replaces manual signature checking. ### Before ```python theme={null} # Python -- manual verification import hmac import hashlib def verify_webhook(payload: bytes, signature: str, secret: str) -> bool: expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest() return hmac.compare_digest(signature, expected) @app.post("/webhooks") async def handle(request: Request): body = await request.body() sig = request.headers.get("x-anima-signature", "") if not verify_webhook(body, sig, "whsec_..."): raise HTTPException(status_code=401) event = json.loads(body) # handle event... ``` ### After ```python theme={null} # Python -- FastAPI dependency from anima import fastapi_webhook_dependency from fastapi import FastAPI, Depends app = FastAPI() webhook_dep = fastapi_webhook_dependency("whsec_...") @app.post("/webhooks") async def handle(event=Depends(webhook_dep)): if event.type == "message.received": print(f"New email from {event.data['from']}") ``` ```ts theme={null} // TypeScript -- Express middleware import express from "express"; import { webhookMiddleware } from "@anima-labs/sdk"; const app = express(); app.use(express.json()); app.post("/webhooks", webhookMiddleware("whsec_..."), (req, res) => { const event = req.webhookEvent; console.log(`Event: ${event.type}`); res.sendStatus(200); }); ``` ## Breaking Changes There are **no breaking changes** in this release. All new features are additive: * `apiKey` / `api_key` is now optional (falls back to `ANIMA_API_KEY`), but passing it explicitly still works * All existing method signatures remain unchanged * New methods (`listAutoPaging`, `withRawResponse`) are additions, not replacements ## Upgrade Steps 1. Update your SDK to the latest version: ```bash theme={null} # Python pip install --upgrade anima-labs # Node.js npm install @anima-labs/sdk@latest ``` 2. (Optional) Set `ANIMA_API_KEY` in your environment and remove hardcoded keys 3. (Optional) Replace manual pagination loops with `listAutoPaging` 4. (Optional) Add `ANIMA_LOG=debug` during development for visibility ## Next Steps * [Official SDKs](/sdks) -- Full SDK reference for TypeScript and Python * [Go SDK](/sdks/go) -- Go SDK documentation * [Webhooks](/webhooks) -- Webhook event reference * [Examples](/examples) -- Complete runnable examples # SecretRef & anima.json Source: https://docs.useanima.sh/secret-ref A declarative spec for pointing at secrets without hardcoding them — used by anima vault exec, anima vault proxy, and the zero-knowledge MCP tools. # SecretRef & `anima.json` A **SecretRef** is a declarative pointer to a secret. Instead of writing `API_KEY=sk-...` in a `.env` file and praying it doesn't end up in a commit or an LLM's context window, you write: ```json theme={null} { "source": "anima", "credentialId": "cred_abc123", "field": "apiKey.key" } ``` The Anima CLI resolves these refs at execution time — the agent composing the command, and the LLM reviewing its output, never see the actual value. ## The three sources Every SecretRef has a `source` field that determines where the value comes from. Only these three are supported, and that's deliberate — every additional source is a new place a secret can be misused. ### `anima` — Anima Vault The default. Points at a credential in your vault. ```json theme={null} { "source": "anima", "credentialId": "cred_01HXYZ...", "field": "apiKey.key", "agentId": "agent_optional" } ``` | Field | Required | Notes | | -------------- | -------- | ------------------------------------------------------------------ | | `credentialId` | yes | The `cred_...` id from the Anima console or `anima vault list` | | `field` | yes | Dot-path into the credential (e.g. `apiKey.key`, `login.password`) | | `agentId` | no | Explicit agent scope; defaults to the CLI's authenticated agent | Under the hood the CLI mints a single-use `vtk_` token, exchanges it, and the resolved value never touches disk. Every resolution is written to the audit log with the actor's key type recorded — reveals via a master key (`mk_`) show up distinctly from agent-scoped accesses. ### `env` — Environment variable Escape hatch for secrets that aren't yet migrated into Anima (rotating dev tokens, one-off sandbox keys). ```json theme={null} { "source": "env", "name": "MY_DEV_TOKEN" } ``` | Field | Required | Notes | | ------ | -------- | -------------------------------------------------- | | `name` | yes | Must be present in the environment at resolve time | If the variable is unset, the CLI refuses to run. It does not fall back to an empty string — that pattern causes too many "it worked locally but 401'd in prod" bugs. ### `exec` — Trusted binary For dynamic secrets that come from a local tool (AWS STS, `op read`, `gcloud auth print-access-token`, short-lived GitHub App tokens). ```json theme={null} { "source": "exec", "command": "aws", "args": ["sts", "get-session-token", "--output", "text"] } ``` | Field | Required | Notes | | --------- | -------- | --------------------------------------------------------------------------------------------------- | | `command` | yes | Bare binary name or absolute path; shell metacharacters (`;`, `\|`, `` ` ``, `$`, `&`) are rejected | | `args` | no | String array. Spawned with `shell: false` so each element is a literal argv slot | | `passEnv` | no | Env var names passed through to the subprocess — everything else is stripped | | `cwd` | no | Working directory for the subprocess | Output is captured with a 15-second timeout and a 1 MB buffer cap. If the binary exits non-zero, the CLI refuses to run. The child process starts with a minimal environment: only the variables listed in `passEnv`, plus `PATH` so the binary can be found. Be explicit about what your provider needs — e.g. `"passEnv": ["HOME", "AWS_PROFILE"]` for the AWS CLI. ## `anima.json` — putting it together Drop an `anima.json` at the root of any project (or any ancestor directory — the CLI walks up from `cwd`). The shape: ```json theme={null} { "$schema": "https://docs.useanima.sh/schemas/anima.json", "secrets": { "GH_TOKEN": { "source": "anima", "credentialId": "cred_github", "field": "apiKey.key" }, "DATABASE_URL":{ "source": "env", "name": "DATABASE_URL" }, "AWS_KEY": { "source": "exec", "command": "aws", "args": ["sts", "get-session-token", "--output", "text"] } } } ``` The keys (`GH_TOKEN`, `DATABASE_URL`, `AWS_KEY`) are the variable names that will be exposed to whatever command consumes the refs — typically environment variables passed to a child process. The `$schema` line is optional, but with it any JSON-Schema-aware editor (VS Code out of the box) validates refs and autocompletes fields as you type. The schema is published at [docs.useanima.sh/schemas/anima.json](https://docs.useanima.sh/schemas/anima.json). ### Using it ```bash theme={null} # Resolve all refs and exec a subprocess with them in-env anima vault exec -- gh api /user # Proxy a single ref through an HTTPS injector anima vault proxy --cred GH_TOKEN --allow-host api.github.com --port 19840 # Quick lookup — masked metadata only (secret fields are never printed) anima vault get GH_TOKEN ``` If the CLI can't resolve a ref (missing env var, failing exec, deleted vault credential) it refuses to run and emits a structured error — no partial execution, no silent fallback. ## Why not just `.env`? Three reasons: 1. **`.env` files are static.** `exec` sources pull fresh values on every resolve — matters for STS tokens, SSO-minted GitHub App tokens, and anything else with a lifetime under an hour. 2. **`.env` files are untyped.** A SecretRef is a JSON schema — you get IDE completion, and the CLI can refuse to run if the shape is wrong before the subprocess starts. 3. **`.env` files don't participate in the audit log.** Every `anima`-source resolution is attributed to an agent or user and shows up in the console Access Log. Agents that ingest a `.env` file by accident are a common leak vector; an `anima.json` commits only *references* — the values live exclusively in the vault. ## Security rules the resolver enforces These are non-negotiable — the resolver refuses to run if any are violated: * `exec.command` may not contain whitespace or `;`, `|`, `&`, `<`, `>`, `$`, `` ` ``, `\`. If you need a pipeline, write a wrapper script and point `command` at it. * `exec` child processes get a minimal environment: `PATH`, plus only the variables listed in `passEnv`. Everything else is dropped. * `anima` refs require an active CLI auth context — the resolver prompts for login rather than falling through silently. * Resolved values never reach disk. They live in process memory for the duration of the child, then are overwritten. See also: [Vault overview](/vault), [Security](/security). # Security Source: https://docs.useanima.sh/security Learn about Anima security controls, dual-layer scanning, policy engine rules, and operational best practices. # Security Built-in safeguards to protect your agents, users, and outbound communications. ### API Keys Two types: Master Keys (full admin access) and Agent Keys (scoped to a single mailbox). ### Dual-Layer Content Scanning Scans with both deterministic regex heuristics and AI classification. ## API Key Types | Type | Prefix | Permissions | Best Practice | | ---------- | ------ | ------------------------------------------------------ | ------------------------------------------------------------------ | | Master Key | `mk_` | Full access. Create agents, manage billing, view logs. | Store in server-only environment variables and rotate on schedule. | | Agent Key | `ak_` | Scoped access. Send/receive for one specific agent. | Issue least-privilege keys per service and revoke unused keys. | ## Reaching master authority A `MASTER_KEY_REQUIRED` (403) means the operation needs org-admin authority and the credential you used does not carry it — around a hundred endpoints sit behind that gate, including agent creation, key management, billing, and the event stream. Which route out is open depends on how you authenticated: | Route | How | Notes | | --------------- | ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | | Master key | Copy your `mk_` key from the [console](https://console.useanima.sh) and send it as the bearer token. | Server-side only. Never ship it to a browser, and never hand it to an agent. | | Console session | Sign in as an organization owner or admin. | Vault reveals additionally require a recent first-factor check — see [Vault reveal policy](/vault-reveal-policy). | | OAuth | Request the `admin:full` scope during `/oauth/authorize`. | Anima Connect applications. | | CLI | Just run the command — it steps up on its own. | Prompts for your OS login password. Enrolment is macOS-only and needs an interactive terminal once — see below. | ### Admin access from the CLI There is no command to run first. A command that needs admin rights — `anima agent create`, key rotation, `anima tail` — asks for it itself, and your OS prompts for your login password. The first time on a given machine, the CLI emails a code to the organization owner and enrols that machine inline, at the moment you need it. Enrolment stores a grant in the OS keychain behind a human-presence gate, so later commands need only the password prompt rather than another email. Admin access lasts for the single command that asked for it and is never written to disk, so each admin command prompts. An agent driving the CLI holds your API key and can run every command, but it cannot answer a system password dialog. That is the boundary, and it is deliberate. The gate is local: it governs release of the grant on this machine, and the server cannot verify that a human was present — so treat it as narrowing who can obtain admin authority here, not as a guarantee about what that authority can do once obtained. Enrolment is macOS-only. On other platforms the CLI refuses to store a grant rather than pretend it is protected, so every step-up uses the emailed code. Enrolment also needs an interactive terminal once, because the code has to be typed back in — a CI job or an agent driving the CLI fails with that explanation instead of blocking on a prompt nothing will answer. ## Content Scanning Overview (Regex + AI) * **Regex Layer:** Fast checks for known injection markers, secret patterns, and risky payload signatures. * **AI Layer:** GPT-4o-mini classification into `SAFE`, `SUSPICIOUS`, or `BLOCKED`. * **Fallback Safety:** If AI credentials are missing, scanning gracefully continues in regex-only mode. > **Note:** Use `low`, `medium` (default), or `high` sensitivity depending on risk tolerance and expected message variability. ## Content Policy Configuration The policy engine applies layered rules to outbound content and can combine deterministic and model-based checks. * **Regex rules:** Match known dangerous patterns. * **AI rules:** Enforce decisions based on model classification. * **Domain rules:** Whitelist or blacklist domains in message content. * **Keyword rules:** Detect finance, attachment, and social engineering signals. ```ts theme={null} const policies = [ { id: "block-injection", name: "Block Injection", action: "block", priority: 100, rules: [ { type: "regex", value: "ignore\\s+previous\\s+instructions", description: "Prompt injection" }, { type: "ai", value: ["BLOCKED"], description: "AI high-risk verdict" } ] } ]; ``` ## Agent Capability Policies Separate from content scanning: each agent carries an optional **capability policy** — hard limits an org admin sets on what the agent may do, regardless of message content. Read it with `GET /v1/agents/{agentId}/policy` (any key in the org). Write it with `PUT /v1/agents/{agentId}/policy` (master key only): ```bash theme={null} curl -X PUT https://api.useanima.sh/v1/agents/agt_.../policy \ -H "Authorization: Bearer mk_..." \ -H "Content-Type: application/json" \ -d '{ "policy": { "email": { "allowedRecipientDomains": ["example.com"], "blockedRecipientDomains": [], "maxPerHour": 20 }, "vault": { "readOnly": true, "blocked": false }, "phone": { "allowedCountries": ["US", "DE"], "maxSmsPerHour": 10, "blocked": false } } }' ``` | Field | Type | Meaning | | ------------------------------- | ---------- | ------------------------------------------------------------------------------------- | | `email.allowedRecipientDomains` | `string[]` | If non-empty, the agent may **only** send to these domains. Empty = no restriction. | | `email.blockedRecipientDomains` | `string[]` | The agent may **never** send to these domains. | | `email.maxPerHour` | `number` | Per-agent override of the emails-per-hour rate limit. | | `vault.readOnly` | `boolean` | Agent can read vault items but not create, update, or delete them. | | `vault.blocked` | `boolean` | Disable vault access for this agent entirely. | | `phone.allowedCountries` | `string[]` | ISO 3166-1 alpha-2 codes. If non-empty, the agent may only call/text these countries. | | `phone.maxSmsPerHour` | `number` | Per-agent override of the SMS-per-hour rate limit. | | `phone.blocked` | `boolean` | Disable phone/SMS for this agent entirely. | Every section is optional — omit one to leave that channel unrestricted. Validation is strict: unknown fields are rejected with a `400` rather than silently ignored, so typos can't create a policy that looks stricter than it is. An email send that violates the policy is refused and logged as a security event, so blocked attempts show up in the audit trail. > **Note:** Email constraints are enforced on every send today. Vault and phone constraints are validated and stored, but not yet enforced at request time. ## Rate Limiting for AI Scanning AI scanning is rate limited to 100 req/min, uses an LRU cache with 1000 entries and a 5min TTL, and falls back to regex scanning when needed. ## Attachment Scanning Email attachments are scanned both when an agent receives them and before an agent sends them, so malicious files are caught in either direction. * **Magic-byte detection:** the actual leading bytes are inspected, not the declared type or the file extension (both are trivially spoofed). An executable or script disguised as `invoice.pdf` is unmasked and blocked. * **Content scanning:** text attachments run through the same content scanner as message bodies, catching leaked credentials, PII, and risky file types. * **Verdict:** each attachment gets a `scanStatus` of `CLEAN`, `FLAGGED`, or `BLOCKED`, surfaced in the message's attachment metadata so an agent can decide before fetching the bytes. A `BLOCKED` attachment cannot be downloaded — the download endpoint returns `403`. Download URLs are short-lived (15 minutes) and scoped to the requesting agent. ## Webhook Security (HMAC Verification) Webhook payloads are protected with HMAC verification and freshness checks to prevent tampering and replay attacks. * Use a dedicated webhook secret. * Verify the HMAC signature on every request. * Compare signatures using constant-time comparison. * Reject requests that fail timestamp freshness checks. > **Warning:** If content is blocked by policy, the API returns a denial response with a reason code so you can audit and tune policy behavior safely. ## Operational Best Practices * Rotate keys on a regular schedule. * Store master keys only in server-side environments. * Use least-privilege agent keys per service. * Review blocked events and adjust policies carefully. # Anomaly Detection Source: https://docs.useanima.sh/security/anomaly-detection Behavioral baselines, detection rules, and automated quarantine for AI agent activity. Protect against compromised or misbehaving agents. # Anomaly Detection Anima continuously monitors agent behavior and compares it against learned baselines to detect unusual activity. When anomalies are detected, agents can be automatically quarantined to prevent damage while alerts are sent for human review. ## How It Works 1. **Baseline Learning** -- Anima observes agent behavior over a configurable window to establish normal patterns 2. **Real-Time Monitoring** -- Every action is compared against the baseline in real time 3. **Detection Rules** -- Built-in and custom rules flag deviations 4. **Response Actions** -- Anomalies trigger alerts, require approval, or quarantine the agent ## Behavioral Baselines Baselines are computed per-agent across multiple dimensions: | Dimension | What It Tracks | | ---------------------- | -------------------------------------------- | | **Email volume** | Emails sent/received per hour, day | | **Recipient patterns** | Typical recipients and new-contact frequency | | **Spending velocity** | Transaction frequency and amount patterns | | **API call rate** | Requests per minute by endpoint | | **Active hours** | Typical hours of activity | | **Data access** | Vault secret access patterns | | **A2A communication** | Task delegation frequency and partner agents | ```ts theme={null} import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: "ak_..." }); // View an agent's current behavioral baseline const baseline = await anima.anomalyDetection.getBaseline("ag_8f3k2m9x1n4p7q6r"); console.log(`Baseline period: ${baseline.windowDays} days`); console.log(`Avg emails/day: ${baseline.emailVolume.avgPerDay}`); console.log(`Avg spend/day: $${(baseline.spending.avgDailyCents / 100).toFixed(2)}`); console.log(`Active hours: ${baseline.activeHours.start}-${baseline.activeHours.end}`); ``` ```python theme={null} from anima import Anima anima = Anima(api_key="ak_...") baseline = anima.anomaly_detection.get_baseline("ag_8f3k2m9x1n4p7q6r") print(f"Baseline period: {baseline.window_days} days") print(f"Avg emails/day: {baseline.email_volume.avg_per_day}") print(f"Avg spend/day: ${baseline.spending.avg_daily_cents / 100:.2f}") ``` ## Detection Rules ### Built-In Rules | Rule | Trigger | Default Action | | --------------------- | ----------------------------------------- | -------------- | | `email_volume_spike` | Emails sent exceed 3x baseline in 1 hour | Alert | | `new_recipient_burst` | 10+ new recipients in 1 hour | Alert | | `spending_spike` | Spend exceeds 5x daily baseline | Quarantine | | `off_hours_activity` | Activity outside established active hours | Alert | | `vault_mass_access` | 5+ vault secrets accessed in 1 minute | Quarantine | | `api_rate_anomaly` | API calls exceed 10x per-minute baseline | Throttle | | `a2a_unknown_agent` | Task sent to never-before-seen agent | Approval | ### Custom Rules Define custom detection rules for your specific use case: ```ts theme={null} await anima.anomalyDetection.rules.create({ name: "high-volume-email-new-recipient", description: "Flag high email volume to recipients never sent to before", conditions: [ { field: "category", operator: "eq", value: "email.send" }, { field: "metadata.batchSize", operator: "gt", value: 100 }, { field: "metadata.to", operator: "not_in_baseline", value: true }, ], action: "require_approval", severity: "high", enabled: true, }); ``` ```python theme={null} anima.anomaly_detection.rules.create( name="high-volume-email-new-recipient", description="Flag high email volume to recipients never sent to before", conditions=[ {"field": "category", "operator": "eq", "value": "email.send"}, {"field": "metadata.batchSize", "operator": "gt", "value": 100}, {"field": "metadata.to", "operator": "not_in_baseline", "value": True}, ], action="require_approval", severity="high", enabled=True, ) ``` ## Quarantine When an agent is quarantined, all its outbound actions are suspended until a human reviews the flagged activity. ```ts theme={null} // Manually quarantine an agent await anima.anomalyDetection.quarantine("ag_8f3k2m9x1n4p7q6r", { reason: "Unusual spending pattern detected", }); // Release an agent from quarantine await anima.anomalyDetection.release("ag_8f3k2m9x1n4p7q6r", { reviewedBy: "admin@acme.com", notes: "Confirmed legitimate activity -- budget increase approved", }); // List quarantined agents const quarantined = await anima.anomalyDetection.listQuarantined(); ``` ## Anomaly Alerts Configure how anomaly alerts are delivered: ```ts theme={null} await anima.anomalyDetection.configureAlerts({ channels: [ { type: "webhook", url: "https://acme.com/webhooks/anomaly" }, { type: "email", address: "security@acme.com" }, { type: "slack", webhookUrl: "https://hooks.slack.com/..." }, ], minSeverity: "medium", // "low" | "medium" | "high" | "critical" }); ``` ## API Reference | Endpoint | Method | Description | | ------------------------------------------------------------------ | ------ | ----------------------------- | | `https://api.useanima.sh/api/anomaly-detection/baselines/:agentId` | GET | Get agent behavioral baseline | | `https://api.useanima.sh/api/anomaly-detection/rules` | POST | Create a detection rule | | `https://api.useanima.sh/api/anomaly-detection/rules` | GET | List detection rules | | `https://api.useanima.sh/api/anomaly-detection/rules/:id` | PUT | Update a detection rule | | `https://api.useanima.sh/api/anomaly-detection/rules/:id` | DELETE | Delete a detection rule | | `https://api.useanima.sh/api/anomaly-detection/alerts` | GET | List recent anomaly alerts | | `https://api.useanima.sh/api/anomaly-detection/quarantine` | POST | Quarantine an agent | | `https://api.useanima.sh/api/anomaly-detection/quarantine` | GET | List quarantined agents | | `https://api.useanima.sh/api/anomaly-detection/release` | POST | Release from quarantine | | `https://api.useanima.sh/api/anomaly-detection/alerts/config` | PUT | Configure alert channels | ## Configuration | Variable | Default | Description | | ------------------------------------ | -------- | --------------------------------------- | | `ANIMA_ANOMALY_BASELINE_WINDOW_DAYS` | `14` | Days of data used to compute baselines | | `ANIMA_ANOMALY_SENSITIVITY` | `medium` | Detection sensitivity (low/medium/high) | | `ANIMA_ANOMALY_AUTO_QUARANTINE` | `true` | Auto-quarantine on critical anomalies | | `ANIMA_ANOMALY_COOLDOWN_MINUTES` | `30` | Cooldown between repeated alerts | ## Next Steps * [Audit Log](/security/audit-log) -- Review the events that triggered anomalies * [Compliance Reporting](/compliance/reporting) -- Include anomaly metrics in reports # Audit Log Source: https://docs.useanima.sh/security/audit-log Immutable audit log for all agent actions. Query, filter, and export for compliance review. # Audit Log Anima maintains an append-only audit log of every authenticated API action performed by or on behalf of agents. The audit log is critical for compliance, incident response, and operational visibility. ## What Gets Logged Every authenticated API call produces an audit entry with a semantic action name: | Category | Example actions | | --------------- | -------------------------------------------------------------------------------- | | **Agent** | `agent.create`, `agent.update`, `agent.delete`, `agent.rotate_key` | | **Email** | `email.send`, `email.list`, `email.get`, `email.unsuppress` | | **Messages** | `message.send_email`, `message.send_sms`, `message.search` | | **Domains** | `domain.add`, `domain.verify`, `domain.delete` | | **Phone/Voice** | `phone.provision`, `phone.send_sms`, `voice.create_call`, `voice.get_transcript` | | **Vault** | `vault.create_credential`, `vault.get_totp`, `vault.share_credential` | | **Identity** | `identity.get_did`, `identity.rotate_keys`, `identity.verify_credential` | | **A2A** | `a2a.submit_task`, `a2a.get_task`, `a2a.cancel_task` | | **Webhooks** | `webhook.create`, `webhook.update`, `webhook.test` | | **Org/Auth** | `org.update`, `org.rotate_key`, API-key lifecycle | Each entry records the actor (API key / user / agent / system), action, resource, result (`SUCCESS` / `FAILURE` / `DENIED`), IP address, user agent, and timestamp. ## Querying the Audit Log Audit access is org-admin surface: use a **master key** (`mk_...`). ``` GET https://api.useanima.sh/v1/orgs/{orgId}/audit-logs ``` ```ts theme={null} import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: "mk_..." }); // Query audit events (paginated) for await (const event of anima.audit.list(orgId, { action: "email.send", startDate: "2026-07-01T00:00:00Z", endDate: "2026-07-16T23:59:59Z", limit: 50, })) { console.log(`[${event.createdAt}] ${event.action}`); console.log(` Actor: ${event.actorId} (${event.actorType})`); console.log(` Resource: ${event.resourceType}/${event.resourceId} → ${event.result}`); } ``` ```bash theme={null} curl "https://api.useanima.sh/v1/orgs/$ORG_ID/audit-logs?action=email.send&limit=50" \ -H "Authorization: Bearer mk_..." ``` ### Query Parameters | Parameter | Type | Description | | -------------- | ------ | --------------------------------------------- | | `actorId` | string | Filter by actor identifier | | `actorType` | string | `API_KEY`, `USER`, `SYSTEM`, or `AGENT` | | `action` | string | Filter by semantic action (e.g. `email.send`) | | `resourceType` | string | Filter by resource type | | `resourceId` | string | Filter by resource identifier | | `result` | string | `SUCCESS`, `FAILURE`, or `DENIED` | | `startDate` | string | ISO 8601 start time | | `endDate` | string | ISO 8601 end time | | `limit` | number | Max results per page (default 20, max 100) | | `cursor` | string | Pagination cursor | ## Audit Event Structure ```json theme={null} { "id": "cmb2xk1a90042abcd", "orgId": "cmb1x9k2l0000abcd", "actorType": "AGENT", "actorId": "cmb1xa3f50001abcd", "action": "email.send", "resourceType": "message", "resourceId": "cmb2xk0zz0041abcd", "result": "SUCCESS", "ipAddress": "10.0.1.42", "userAgent": "anima-node/0.5.1", "metadata": { "to": "ops@example.com", "subject": "Deploy complete" }, "createdAt": "2026-07-15T14:32:01.234Z" } ``` ## Exporting Audit Logs Export logs for compliance review as CSV or JSON: ``` POST https://api.useanima.sh/v1/orgs/{orgId}/audit-logs/export ``` ```ts theme={null} const exportResult = await anima.audit.export(orgId, { format: "csv", // "csv" | "json" startDate: "2026-04-01T00:00:00Z", endDate: "2026-06-30T23:59:59Z", }); console.log(`${exportResult.count} records`); await Bun.write("audit-q2-2026.csv", exportResult.data); ``` ```bash theme={null} curl -X POST "https://api.useanima.sh/v1/orgs/$ORG_ID/audit-logs/export" \ -H "Authorization: Bearer mk_..." \ -H "Content-Type: application/json" \ -d '{"format": "csv", "startDate": "2026-04-01T00:00:00Z"}' ``` The export returns the data inline in the response (`data`, `format`, `count`). Push it to your SIEM or archive from there — Anima does not currently stream directly to SIEM providers. ## Access Reviews For SOC 2-style periodic access reviews, the audit API includes review tracking: | Endpoint | Method | Description | | ----------------------------------------------------- | ------ | ------------------------------------------------------------- | | `/v1/orgs/{orgId}/access-reviews` | POST | Start an access review (`QUARTERLY`, `AD_HOC`, `OFFBOARDING`) | | `/v1/orgs/{orgId}/access-reviews` | GET | List access reviews | | `/v1/orgs/{orgId}/access-reviews/{reviewId}/complete` | POST | Record findings + complete a review | ## API Reference | Endpoint | Method | Description | | ------------------------------------- | ------ | ---------------------------------- | | `/v1/orgs/{orgId}/audit-logs` | GET | Query audit events (filters above) | | `/v1/orgs/{orgId}/audit-logs/{logId}` | GET | Get a single audit event | | `/v1/orgs/{orgId}/audit-logs/export` | POST | Export as CSV/JSON | ## Next Steps * [Anomaly Detection](/security/anomaly-detection) -- Detect unusual agent behavior * [Compliance Reporting](/compliance/reporting) -- Generate compliance reports # LangChain Source: https://docs.useanima.sh/toolkits/langchain Give your LangChain.js agent a real email identity — send, receive, and read email with @anima-labs/toolkit-langchain. # LangChain `@anima-labs/toolkit-langchain` wraps the [Anima SDK](/sdks) as LangChain.js tools, so any LangChain agent can send and read real email from its own inbox. ## Install ```bash theme={null} npm install @anima-labs/toolkit-langchain @langchain/core # plus `langchain` if you use createAgent as below ``` Requires LangChain.js 1.x (`@langchain/core >= 1.0`) and Node 20+. ## Prerequisites * An Anima API key and an agent — create both in [console.useanima.sh](https://console.useanima.sh) or follow the [email quickstart](/quickstart-email) ```bash theme={null} export ANIMA_API_KEY=ak_... # your agent (or org) API key export ANIMA_AGENT_ID=agent_... # the agent to act as ``` ## Bind the tools to an agent ```ts theme={null} import { createAgent } from "langchain"; import { createAnimaTools } from "@anima-labs/toolkit-langchain"; const agent = createAgent({ model: "openai:gpt-5", tools: createAnimaTools(), // reads ANIMA_API_KEY / ANIMA_AGENT_ID }); const result = await agent.invoke({ messages: [ { role: "user", content: "Check my inbox for new mail and summarize anything important." }, ], }); ``` Or configure explicitly: ```ts theme={null} const tools = createAnimaTools({ apiKey: "ak_...", agentId: "agent_..." }); ``` ## Tools | Tool | What it does | | ------------- | -------------------------------------------------------------------------------- | | `get_agent` | The agent's identity: name, status, and its email addresses | | `send_email` | Send an email from the agent's inbox (`to`/`cc`/`bcc`, subject, body) | | `list_emails` | List received/sent email, newest first, with `direction`/`since`/`limit` filters | | `get_email` | Read one full email (body + attachment metadata) by message id | Every tool is backed by a live Anima API route and throws on API errors so your agent executor can react. ## Runnable example: send a real email and read it back The tools work standalone too — no model key needed. This is the full [`examples/quickstart.ts`](https://github.com/anima-labs-ai/toolkit/blob/main/node/langchain/examples/quickstart.ts) flow, which also runs against a mock API in the toolkit repo's CI on every release: ```bash theme={null} export ANIMA_TO=you@example.com # where to send — watch it arrive in your inbox ``` ```ts theme={null} import { createAnimaTools } from "@anima-labs/toolkit-langchain"; const tools = createAnimaTools(); const byName = Object.fromEntries(tools.map((t) => [t.name, t])); // Send a real email through the send_email tool const sent = JSON.parse( (await byName.send_email.invoke({ to: [process.env.ANIMA_TO!], subject: "Hello from my LangChain agent", body: "This email was sent through the Anima LangChain toolkit.", })) as string, ); console.log(`Sent ${sent.id} (${sent.status})`); // Read it back from the agent's outbox const outbox = JSON.parse( (await byName.list_emails.invoke({ direction: "OUTBOUND", limit: 5 })) as string, ); const copy = outbox.emails.find((m: { id: string }) => m.id === sent.id); const full = JSON.parse((await byName.get_email.invoke({ messageId: copy.id })) as string); console.log(`Read back: "${full.subject}" -> ${full.to}`); ``` Anima blocks sending to the agent's own addresses server-side (anti-loop guard), so point `ANIMA_TO` at an external inbox — for example your personal one, and watch the email land there. ## Reading received mail Incoming email to the agent's address is ingested automatically. List it with `direction: "INBOUND"`: ```ts theme={null} const inbox = JSON.parse( (await byName.list_emails.invoke({ direction: "INBOUND", since: "2026-07-16T00:00:00Z", limit: 20, })) as string, ); ``` ## Next steps * [Email quickstart](/quickstart-email) — create agents and inboxes * [Webhooks](/webhooks) — get pushed `message.received` events instead of polling * [SDK reference](/sdks) — the full Anima API surface beyond email # Vault Source: https://docs.useanima.sh/vault Store, share, and inject encrypted credentials for your AI agents — with a hard guarantee that the LLM never sees raw secrets. # Vault The Anima Vault provides encrypted credential storage for AI agents, with a critical security guarantee: **the LLM never sees raw secrets**. Each agent gets its own isolated, per-agent-encrypted vault for logins, API keys, payment cards, and identity data. Agents reference credentials through opaque tokens, and the CLI or browser extension performs the last-mile substitution at execution time. ## Overview Agents often need to authenticate with external services — CRMs, booking platforms, merchant sites. The vault stores these credentials securely and makes them available to the agent at runtime, without exposing secrets in code, environment variables, or the model's context. The core idea: ```text theme={null} Agent (LLM) → composes a command containing a vtk_ token CLI / extension → detects the token, exchanges it for the credential, substitutes Execution → the real secret is used in the request Output → matching secrets are redacted before returning to the LLM ``` ## Use, never see Every agent surface reads credentials **masked**; there is no reveal path an agent can call. Agents exercise secrets through three mechanisms, all of which keep the plaintext out of the model: * **[Server-side use](/vault-server-side-use)** — Anima makes the outbound call and injects the credential on the server; the secret never reaches the agent's host. * **Browser autofill** — the [extension](/extension-connect) fills logins directly into web forms. * **Local injection** — `anima vault exec` / `anima vault proxy` substitute secrets into a local process at the last moment (see below). Whether a *human* can ever read the plaintext back is governed per credential by its **[reveal policy](/vault-reveal-policy)** — `brokered` credentials are use-only for everyone, forever. And when an agent needs a secret it doesn't have, it asks a human through **[credential requests](/vault-credential-requests)** instead of chat. ## Provisioning Before an agent can store credentials, its vault must be provisioned: ```python theme={null} # Python anima.vault.provision(agent_id=agent.id) ``` ```ts theme={null} // TypeScript await anima.vault.provision({ agentId: agent.id }); ``` From the CLI: ```bash theme={null} anima vault provision --agent anima vault status --agent ``` ## Credential Types | Type | Primary secret | Use case | | ------------- | -------------- | ---------------------------------------------------------------- | | `login` | password | Website logins, SSH credentials (username, password, URIs, TOTP) | | `api_key` | key | API keys (e.g. provider keys) | | `oauth_token` | accessToken | OAuth integrations | | `certificate` | privateKey | TLS / mTLS certificates | | `secure_note` | notes | Free-form secret text | | `card` | number | Payment card details | | `identity` | — | Personal / business identity data (not auto-injectable) | ## CRUD Operations ### Create a credential ```python theme={null} # Python credential = anima.vault.create_credential( agent_id=agent.id, type="login", name="CRM Login", login={ "username": "bot@company.com", "password": "s3cur3-p4ssw0rd", "uris": [{"uri": "https://crm.company.com"}], }, ) ``` ```ts theme={null} // TypeScript const credential = await anima.vault.createCredential({ agentId: agent.id, type: "login", name: "CRM Login", login: { username: "bot@company.com", password: "s3cur3-p4ssw0rd", uris: [{ uri: "https://crm.company.com" }], }, }); ``` ### Create a login with a generated password For account provisioning, don't supply a password at all — ask the vault to generate one server-side in the same call. The password is created inside the vault, stored with the credential, and **never returned**: the response carries only the credential ref with masked fields. Your code (and, for MCP agents, the model's context) never sees the secret. ```python theme={null} # Python credential = anima.vault.create_credential( agent_id=agent.id, type="login", name="Acme Portal", login={"username": "bot@company.com", "uris": [{"uri": "https://acme.io/login"}]}, generate_password={}, # server defaults: 24 chars, all character classes ) print(credential.id) # store this ref print(credential.login.password) # "****" — the plaintext stays in the vault ``` ```ts theme={null} // TypeScript const credential = await anima.vault.createCredential({ agentId: agent.id, type: "login", name: "Acme Portal", login: { username: "bot@company.com", uris: [{ uri: "https://acme.io/login" }] }, generatePassword: { length: 32, special: false }, // tune per site policy }); ``` Generation options: `length` (8–128, default 24) and the character-class toggles `uppercase` / `lowercase` / `number` / `special` (all default `true`). `generatePassword` is only valid for `login` credentials and is mutually exclusive with `login.password`. At fill time — for example when the Anima Chrome extension signs in to provision an account — the agent mints a single-use, audit-logged vault token (`POST /vault/token`, scope `autofill`) and exchanges it for the credential (see [Ephemeral Tokens](#ephemeral-tokens) below). The plaintext is revealed exactly once, to the component that needs it. ### Retrieve a credential ```python theme={null} creds = anima.vault.get_credential( agent_id=agent.id, credential_id=credential.id, ) print(creds.username, creds.password) ``` ### List and search ```python theme={null} all_creds = anima.vault.list(agent_id=agent.id) results = anima.vault.search(agent_id=agent.id, query="crm") ``` ### Update and delete ```python theme={null} anima.vault.update_credential( agent_id=agent.id, credential_id=credential.id, password="new-p4ssw0rd", ) anima.vault.delete_credential( agent_id=agent.id, credential_id=credential.id, ) ``` ### REST endpoints | Method | Path | Description | | -------- | -------------------------------------- | ------------------------------------------------ | | `GET` | `/vault/credentials?agentId=` | List all credentials | | `GET` | `/vault/credentials/{id}?agentId=` | Get a single credential | | `POST` | `/vault/credentials` | Create a credential | | `PUT` | `/vault/credentials/{id}` | Update a credential | | `DELETE` | `/vault/credentials/{id}` | Delete a credential (`agentId` in the JSON body) | | `GET` | `/vault/search?agentId=&search=&type=` | Search credentials | | `POST` | `/vault/generate-password` | Generate a random password | | `GET` | `/vault/totp/{id}?agentId=` | Get the current TOTP code | | `GET` | `/vault/status?agentId=` | Vault connection status | | `POST` | `/vault/sync` | Trigger a vault sync | ## TOTP Support For credentials with TOTP (Time-based One-Time Password) configured: ```python theme={null} totp = anima.vault.get_totp( agent_id=agent.id, credential_id=credential.id, ) print(totp.code) # Current 6-digit code ``` ## Password Generation For agent flows, prefer the atomic [create-with-generated-password](#create-a-login-with-a-generated-password) above — it generates and stores the password in one call and never returns the plaintext. The standalone generator remains available for interactive use. Note that it **returns the generated password to the caller**, so it should not be used in flows where the secret must stay inside the vault: ```ts theme={null} // TypeScript const { password } = await anima.vault.generatePassword({ length: 24, uppercase: true, lowercase: true, number: true, special: true, }); ``` ## Credential Sharing Share a credential from one agent to another, with a scoped permission and optional expiry. Sharing is one credential to one target agent at a time. | Method | Path | Description | | ------ | ----------------------------------- | ------------------------------------- | | `POST` | `/vault/share` | Share a credential with another agent | | `GET` | `/vault/shares?agentId=&direction=` | List shares (granted or received) | | `POST` | `/vault/share/revoke` | Revoke a share | `POST /vault/share` takes `credentialId`, `sourceAgentId`, `targetAgentId`, a `permission`, and either `expiresAt` or `expiresInSeconds`. Revoke with a `shareId`. **Permissions:** | Permission | Grants | | ---------- | ------------------------------------------------------------------------ | | `READ` | View credential metadata only | | `USE` | Auto-fill the credential in the CLI or browser, without seeing its value | | `MANAGE` | Full read/update/delete access to the shared credential | ## Ephemeral Tokens An ephemeral token is a short-lived, single-use handle to a credential. The agent puts the token in a command; the CLI or extension exchanges it for the real value at the moment of execution. | Method | Path | Description | | ------ | ----------------------- | ----------------------------------- | | `POST` | `/vault/token` | Create an ephemeral token | | `POST` | `/vault/token/exchange` | Exchange a token for its credential | | `POST` | `/vault/token/revoke` | Revoke all tokens for a credential | `POST /vault/token` takes `credentialId`, a `scope`, and an optional `ttlSeconds` (and `agentId` / `taskId`). The token value (`vtk_`) is returned **only at creation time**. **Scopes:** | Scope | Purpose | | ---------- | ---------------------------------------- | | `autofill` | CLI and browser-extension auto-fill | | `proxy` | Delegated access through the local proxy | | `export` | One-time credential reveal | **Token security:** * Tokens use the `vtk_` prefix with 32 bytes of entropy. * Only the token's hash is stored — the raw token is never persisted. * Tokens are single-use: consumed on first exchange. * Configurable TTL (10–3600 seconds). * Scope-bound: a token scoped to `autofill` cannot be used for `export`. ## Secret Redaction Output from any command that used injected credentials is scanned and redacted before it returns to the LLM, so a secret that appears in a response body or error message never lands in the model's context. ```bash theme={null} some-command | anima vault redact --agent # any value matching a stored credential becomes [REDACTED] ``` `anima vault redact` fetches the agent's credentials, replaces any matching secret value in stdin with `[REDACTED]`, and can take extra literal strings via `--pattern`. ## Template Substitution For structured references inside a block of text, use the template syntax and let the CLI resolve it at execution time: ```text theme={null} {{vault:credentialId:login.password}} {{vault:credentialId:apiKey.key}} {{vault:credentialId:oauthToken.accessToken}} ``` The CLI detects `{{vault:...}}` templates (and `vtk_` tokens) in input and exchanges them for real credentials before the command runs. ## Zero-Knowledge Execution Ephemeral tokens, template substitution, and redaction protect against *accidental* plaintext exposure. For a tighter boundary — where the agent authors the call but can never read the secret — the CLI adds a set of execution primitives. All resolve credentials through [SecretRef & `anima.json`](/secret-ref). ### `anima vault exec` Run a subprocess with resolved secrets injected as environment variables. The agent writes the command; the CLI resolves the `anima.json` references and spawns the child — the model's context never sees the values. Output is scrubbed by the redaction engine. ```bash theme={null} anima vault exec -- gh api /user anima vault exec -- psql -c "select count(*) from users" ``` ### `anima vault proxy` A loopback-only HTTPS proxy that injects an `Authorization` header into outbound requests. The agent holds only a short-lived proxy token (`pxt_`); the credential lives in the CLI process and is never reachable from the network. Requests to any host not on `--allow-host` are rejected. ```bash theme={null} anima vault proxy --cred cred_github --allow-host api.github.com --port 19840 & # proxy_token=pxt_... port=19840 curl -H "X-Anima-Proxy: pxt_..." \ http://127.0.0.1:19840/https://api.github.com/user ``` ### `anima vault agent` (keystroke injection) A local daemon that binds to a Unix socket (mode `0600`) and — on a user-confirmed hotkey — types a credential into the focused text field. Useful for apps that can't take a proxy, such as desktop SSH clients or native database UIs. ```bash theme={null} anima vault agent start anima vault type --cred GH_TOKEN # press the hotkey to confirm; the daemon types the value ``` ### Revealing plaintext The CLI never prints secret plaintext. `anima vault get` always masks secret fields — there is no `--unmask` flag and no `unlock` command. To actually view a secret, a human uses the Anima console, where a reveal is audited and step-up gated. Agents and automation should *use* secrets, never read them: autofill via the browser extension, local injection via `anima vault exec` / `anima vault proxy`, or the server-side broker via `anima vault use`. ### `anima vault audit` Scan the filesystem for leaked secrets, cross-referenced against the vault inventory. High-confidence key patterns are flagged by heuristic; literal values that match a stored credential are flagged by exact comparison. ```bash theme={null} anima vault audit . # scan the current directory anima vault audit --check # CI mode: exit non-zero on findings ``` MCP vault tools never return plaintext to the model. They hand back a plan that a trusted local process (the CLI, the extension, or the keystroke daemon) executes — the LLM composes intent, and the local process enforces the secret boundary. ## Browser Extension The Anima browser extension performs vault credential autofill for login forms, so an agent can log in without the password ever entering its context: 1. The agent stores credential data ephemerally. 2. The agent asks the extension to fill the detected login form. 3. The extension detects the username, password, and TOTP fields. 4. Credentials are injected and immediately zeroized from memory. To bind the extension to an agent from a headless browser — a Puppeteer worker, a scheduled job, no human to click — use [Headless Extension Connect](/extension-connect). ## Access Log Every credential access — token mints, `exec` invocations, and masked reads — is recorded in the vault audit log. ```bash theme={null} anima vault audit # (GET /vault/audit) — the vault access stream ``` In the console, the **Vault → Access Log** page filters this stream and highlights plaintext reveals (via a master key) with a warning banner, so compliance teams can confirm they were intentional. Masked accesses from agent keys are the common path and don't need review. ## Deprovision Remove an agent's vault and all stored credentials: ```python theme={null} anima.vault.deprovision(agent_id=agent.id) ``` ## Security * All credentials are encrypted at rest with AES-256-GCM. * Each agent's vault is isolated, with its own per-agent encryption key — agents cannot access each other's credentials. * Vault access is scoped to the agent's API key. * Credential reads are masked by default; plaintext access requires a master-key `reveal` (audit-logged) or a single-use vault token exchange. * Vault-generated passwords never appear in any API response, audit log, or webhook payload — only the credential ref leaves the vault. * See [Encryption](/encryption) for the per-agent key-derivation model, and [SecretRef & `anima.json`](/secret-ref) for the reference schema used by `exec` and `proxy`. # Credential Requests Source: https://docs.useanima.sh/vault-credential-requests Agents ask a human for a secret they must never see — the human fills it out-of-band, the agent gets a reference. # Credential Requests When an agent needs a credential it doesn't have — a 401 on a service, a missing API key — it should never ask the human to paste the secret into chat. Instead it creates a **credential request**: the human receives a token-gated fill URL, enters the secret on Anima's page, and the secret goes straight into the vault. The agent polls the request and receives only a **reference** (plus a masked preview like `****1234` to confirm which secret arrived), then uses it via the [broker](/vault-server-side-use) or autofill. ```text theme={null} Agent: request("Stripe key", reason) ──> fillUrl ──> human opens page, pastes secret Agent: poll status … PENDING … FULFILLED { credentialId, maskedPreview } Agent: vault use credentialId (never sees the value) ``` ## Create a request ```bash CLI theme={null} anima vault request create --type api_key \ --name "Stripe production key" \ --reason "Deploy needs to verify billing" \ --ttl 900 --wait ``` ```ts TypeScript theme={null} const req = await anima.vault.credentialRequestCreate({ type: "api_key", name: "Stripe production key", reason: "Deploy needs to verify billing", ttlSeconds: 900, }); // share req.fillUrl with the human, then poll ``` ```python Python theme={null} req = anima.vault.credential_request_create( type="api_key", name="Stripe production key", reason="Deploy needs to verify billing", ttl_seconds=900, ) ``` ```json MCP theme={null} // tool: vault_credential_request_create — MCP clients with elicitation // support surface the fill UI inline; otherwise the fill URL is returned // (and optionally emailed to the org owner with notifyOwner). { "type": "api_key", "name": "Stripe production key", "reason": "Deploy needs to verify billing" } ``` Requests expire (60–3600 s TTL, default 15 minutes). `--wait` on the CLI polls until the request leaves `PENDING`. ## Track and manage * **Status** — `credentialRequestStatus(requestId)` returns `PENDING | FULFILLED | EXPIRED | DECLINED | CANCELLED`, the `credentialId` once fulfilled, and the masked preview. Never the secret. * **List** — `credentialRequestList` enumerates the org's requests (agents see only their own). In the console, **Vault → Requests** shows every ask with its reason, lets you copy a pending fill URL to forward to the right person, and cancel stale requests. * **Cancel** — `credentialRequestCancel(requestId)` invalidates the fill URL immediately. Secrets submitted through a fill URL default to the `brokered` [reveal policy](/vault-reveal-policy): the human who typed the secret is the last person who ever sees it. # Reveal Policy Source: https://docs.useanima.sh/vault-reveal-policy Control whether a credential's plaintext can ever be read back — 'brokered' means use-only, for everyone, forever. # Reveal Policy Anima separates **using** a secret from **seeing** it. Agents can always *use* credentials — through the [server-side broker](/vault-server-side-use), browser autofill, or local injection — but reading the plaintext back is governed per credential by its reveal policy: | Policy | Plaintext readable? | Recovery | | ---------- | --------------------------------------------------- | ---------------- | | `standard` | Only by org admins (console / master key), audited | Reveal or rotate | | `brokered` | **Never — by anyone, including the org master key** | Rotation only | Defaults when you don't specify one: `oauth_token` credentials are always `brokered`; logins created by an agent key default to `brokered`; everything else follows the org default (`standard` unless changed). ```bash theme={null} anima vault store --type api_key --name "Stripe" \ --provider stripe --key sk_live_... \ --allowed-host api.stripe.com \ --reveal-policy brokered ``` Upgrading `standard → brokered` needs update access; downgrading `brokered → standard` re-opens the reveal path, so it is master-only and writes a `reveal_policy_downgraded` audit entry. ## What agents can never do On every agent surface — MCP tools, SDKs with agent keys, the API — reads return **masked** data (`sk_****1234`), and there is no reveal parameter an agent can pass. The one plaintext endpoint, `exchangeTokenForInjection`, exists for trusted *injectors* (the CLI's local-injection commands) and is hard-gated: the caller must present a master key or a key carrying the `vault:inject` scope. A plain agent key gets `403`. This is what makes "the agent can use it but can never see it" a property of the platform rather than a convention. ## Editing without seeing Because reads are masked, updates use **patch semantics** for secret-bearing blocks: fields you omit keep their stored values, and a masked echo (`sk_****1234`) of a secret field counts as unchanged. That means you can edit `allowedHosts` on an API key — from the console, SDKs, or CLI — without ever resending or clobbering the key itself. Sending a genuinely new value rotates it. ```ts theme={null} // Adds a host; the stored key is untouched. await anima.vault.updateCredential("cred_abc", { apiKey: { allowedHosts: ["api.stripe.com", "files.stripe.com"] }, }); ``` ## Human reveal (standard policy only) Org admins can reveal a `standard` credential in the console (the Copy action) or via the API with a master key (`reveal: true`). Every reveal is written to the access log as `access_reveal` with the actor — the [access log](/vault) flags plaintext reveals so compliance can review who looked at what. For `brokered` credentials the console offers rotation instead; there is nothing to reveal. **Step-up freshness.** Console reveals additionally require the session to have verified its first factor recently (default: within 10 minutes; `VAULT_REVEAL_FRESHNESS_MINUTES`, `0` disables). A stale browser session — the thing session theft steals — gets a `403` and the console pops Clerk's re-verification modal, then retries automatically; denied attempts are logged as `access_reveal_denied`. Downgrading a credential from `brokered` to `standard` demands the same freshness, since it re-opens the reveal path. Raw master API keys are bearer secrets, not sessions, so they are unaffected — they remain the automation escape hatch. # Server-Side Credential Use Source: https://docs.useanima.sh/vault-server-side-use Let agents make authenticated API calls with vault credentials that never leave Anima — the secret is injected server-side and never returned. # Server-Side Credential Use The strongest way for an agent to use a secret is to never hold it at all. With server-side use, the agent asks Anima to make the outbound HTTPS call; Anima decrypts the credential, injects it into the request **on the server**, and returns only the upstream response. The plaintext never reaches the agent's host, its context, or its logs. ```text theme={null} Agent Anima API Upstream (api.stripe.com) │ use(cred, req) │ │ ├─────────────────────>│ decrypt + inject key │ │ ├─────────────────────────────>│ │ │ response │ │ status/body only │<─────────────────────────────┤ │<─────────────────────┤ (secrets scrubbed) │ ``` Compare this with `anima vault exec` / `anima vault proxy`, which inject locally: there the secret reaches your machine's process (but still never the LLM). Server-side use removes even that. ## Configure the credential A credential is only broker-usable for hosts you bind it to. **Fail-closed:** with no allowed hosts, every call is refused. ```bash theme={null} anima vault store --type api_key --name "Stripe production" \ --provider stripe --key sk_live_... \ --allowed-host api.stripe.com \ --reveal-policy brokered ``` For `api_key` credentials you can also set `authHeader` (default `Authorization`) and `authScheme` (default `Bearer `; empty for raw-key headers like `x-api-key`). `login` credentials use their URIs as the allowlist; `oauth_token` credentials have their own `allowedHosts` and are always created with the `brokered` reveal policy. Changing the allowlist later requires org-admin (master) access — an agent that could broaden it could redirect the secret to a host it controls. ## Make a brokered call ```bash CLI theme={null} anima vault use --credential cred_abc \ --method POST --url https://api.stripe.com/v1/charges \ -H "X-Idempotency: order-42" --body '{"amount":1000}' ``` ```ts TypeScript theme={null} const res = await anima.vault.useCredential("cred_abc", { method: "POST", url: "https://api.stripe.com/v1/charges", headers: { "X-Idempotency": "order-42" }, body: JSON.stringify({ amount: 1000 }), }); console.log(res.status, res.body); ``` ```python Python theme={null} res = anima.vault.use_credential( "cred_abc", method="POST", url="https://api.stripe.com/v1/charges", headers={"X-Idempotency": "order-42"}, body='{"amount": 1000}', ) print(res["status"], res["body"]) ``` ```json MCP theme={null} // tool: vault_credential_use { "id": "cred_abc", "method": "POST", "url": "https://api.stripe.com/v1/charges", "headers": { "X-Idempotency": "order-42" }, "body": "{\"amount\":1000}" } ``` The response carries `status`, `headers`, `body` (size-capped, `truncated` flag), with any occurrence of the credential scrubbed. Any `Authorization` header the caller supplies is discarded and replaced with the real credential. ## Guardrails * **Host allowlist + SSRF guard** — https-only, exact host match, DNS-resolved and pinned, private/link-local/metadata ranges blocked, no redirect following. * **Access control** — the caller needs to own the credential or hold a `USE`-level share. Scoped API keys need the `vault:use` scope. Brokered `POST`/`PUT`/`PATCH`/`DELETE` calls run under the agent's *write* vault policy. * **Rate limits** — per-credential limits from the stored `rateLimit` config, with a conservative default so no credential can be fired unbounded. * **Audit** — every call is recorded as `broker_use` (method, host, status — never the secret); refused calls are recorded as `broker_use_denied` with the reason, so a probing agent is visible in the [access log](/vault). ## When to use which mechanism | Mechanism | Secret reaches | Best for | | ----------------------------- | ------------------------- | ------------------------------ | | Server-side use (`vault use`) | Nothing outside Anima | HTTP APIs, strongest isolation | | Browser autofill (extension) | The web page's form | Logins on websites | | `vault exec` / `vault proxy` | A local process you spawn | CLIs, SDKs, non-HTTP tools | All three keep the secret out of the model's context. See [Vault](/vault) for the full model. # Voice Catalog Source: https://docs.useanima.sh/voice-catalog Browse available voices and set an agent's voice for AI-powered phone calls. # Voice Catalog Every Anima agent speaks with a single, high-quality voice. The catalog is **multilingual** — voices span English, Spanish, French, German, Italian, Japanese, and Dutch, each with its own accent and tone. Use the voice catalog to browse the available voices, then set one on your agent — it is used for all of that agent's calls. ## List voices ```python theme={null} from anima import Anima anima = Anima(api_key="ak_...") voices = anima.voices.list() for voice in voices["voices"]: print(f"{voice.id}: {voice.name} ({voice.gender}, {voice.accent})") ``` ```ts theme={null} import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: "ak_..." }); const voices = await anima.voices.list(); for (const voice of voices.voices) { console.log(`${voice.id}: ${voice.name} (${voice.gender}, ${voice.accent})`); } ``` Each voice has an `id` (e.g. `thalia`), a display `name`, a `gender`, an `accent`, an optional `age`, a set of `descriptors` (tone words such as "warm" or "smooth"), suggested `useCases`, a `language`, and a `sampleUrl` — a short audio preview you can play to hear the voice. ## Filters The catalog supports these optional filters: | Filter | Values | | ---------- | --------------------------------------------------------------- | | `gender` | `male`, `female`, `neutral` | | `language` | language code — one of `en`, `es`, `fr`, `de`, `it`, `ja`, `nl` | ```python theme={null} female_voices = anima.voices.list(gender="female") english_voices = anima.voices.list(language="en") ``` ```ts theme={null} const femaleVoices = await anima.voices.list({ gender: "female" }); const englishVoices = await anima.voices.list({ language: "en" }); ``` ## Preview a voice Each voice carries a `sampleUrl` — a short audio clip you can play to hear how it sounds before assigning it. The preview is served from the Anima API, so you can embed or fetch it directly. ```python theme={null} voices = anima.voices.list(language="es") for voice in voices["voices"]: if voice.sample_url: print(f"{voice.name}: {voice.sample_url}") ``` ```ts theme={null} const { voices } = await anima.voices.list({ language: "es" }); for (const voice of voices) { if (voice.sampleUrl) console.log(`${voice.name}: ${voice.sampleUrl}`); } ``` `sampleUrl` appears once a preview clip has been generated for a voice; a voice without a generated clip omits the field. ## Set an agent's voice A voice is a property of the agent, not the call. Set `voiceId` when you create or update an agent, and every call that agent places or receives uses it. Omit it to use the system default voice. ```python theme={null} agent = anima.agents.update(id="AGENT_ID", voice_id="thalia") ``` ```ts theme={null} const agent = await anima.agents.update({ id: "AGENT_ID", voiceId: "thalia" }); ``` ## Place a call Call creation takes `to`, an optional `agentId`, an optional `greeting`, and an optional `fromNumber` — the voice comes from the agent. ```python theme={null} call = anima.calls.create( agent_id="AGENT_ID", to="+15551234567", greeting="Hi, this is my Anima agent calling from its own number.", ) print(call.call_id, call.state) ``` ```ts theme={null} const call = await anima.calls.create({ agentId: "AGENT_ID", to: "+15551234567", greeting: "Hi, this is my Anima agent calling from its own number.", }); console.log(call.callId, call.state); ``` ## REST endpoint ```bash theme={null} curl "https://api.useanima.sh/v1/voice/catalog" \ -H "Authorization: Bearer ak_..." ``` ## Next steps * [Quickstart: Voice Calls](/quickstart-voice) - Text yourself, then place the first call * [Call Intelligence](/call-intelligence) - Transcripts, summaries, scores, and recordings * [Voice WebSocket Protocol](/protocols/voice-websocket) - Real-time call event streaming # Webhooks Source: https://docs.useanima.sh/webhooks Configure webhooks to receive real-time updates for email, SMS, and call events — with signed, replay-protected deliveries. # Webhooks Subscribe to real-time events like incoming emails, delivery failures, and completed calls. ## Configuration Configure webhooks in the dashboard, via the API, the CLI, the `webhook_set` MCP tool, or any SDK. Each delivery is a JSON POST to your endpoint: ```json theme={null} { "event": "message.received", "occurredAt": "2026-07-28T12:00:00.000Z", "messageId": "cme9x2k1p0001s601abcdefgh", "agentId": "cme9x2k1p0000s601ijklmnop", "channel": "email", "direction": "INBOUND", "fromAddress": "user@example.com", "toAddress": "support-agent@agents.useanima.sh", "threadId": "cme9x2k1p0002s601qrstuvwx", "subject": "Hello", "spam": false } ``` ## Event Types Subscribing to a name that isn't on this list is accepted but never fires, so copy them exactly. `GET /webhooks/event-types` returns the same list from the live API. | Event Name | Fires when | | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | `message.received` | Email or SMS arrives for one of your agents. | | `message.received.auto` | Inbound mail detected as automated (auto-reply, out-of-office). Fired *instead of* `message.received`, so subscribe to it explicitly if you want it. | | `message.sent` | An outbound email or SMS is accepted for delivery. | | `message.failed` | An outbound message failed to send, or the recipient complained. | | `message.bounced` | An outbound email bounced. | | `message.loop_detected` | Repeated sends to the same address tripped the velocity breaker; the message is held for approval. | | `agent.created` | An agent is created. | | `agent.updated` | An agent is updated. | | `agent.deleted` | An agent is deleted. | | `phone.provisioned` | A number is attached to an agent. | | `phone.released` | A number is released from an agent. | | `call.started` | A voice call begins. | | `call.ended` | A voice call completes. | | `call.summary.ready` | The post-call summary finishes processing. | | `call.score.ready` | The post-call quality score finishes processing. | | `call.security.alert` | A call's security scan raises an alert. | | `call.security.scan.ready` | A call's security scan finishes. | | `a2a.task.received` | Another agent sent one of your agents an A2A task. | | `vault.credential.refresh_failed` | A stored OAuth credential could not be refreshed and is marked `[needs reauth]`. The agent cannot authenticate to that provider until a human re-consents. | ### Subscriptions are org-scoped A subscription belongs to your **organization**, not to a single agent — one endpoint receives the events for every agent you run. There is no `agentId` on a subscription; use the `agentId` in the payload to tell agents apart. ### Wildcards A bare `*` matches everything. Otherwise `*` matches exactly **one** dot-separated segment, and `**` matches across segments. This trips people up on the three-segment names: | Pattern | `call.ended` | `call.security.alert` | | --------- | ------------ | --------------------- | | `call.*` | matches | **no match** | | `call.**` | matches | matches | | `*` | matches | matches | The same applies to `message.*`, which does **not** match `message.received.auto`. ### Payload shape Flat JSON — there is no `data` envelope to unwrap. Every event carries `event` and `occurredAt`; message events add `messageId`, `agentId`, `channel`, `direction`, `fromAddress`, `toAddress`, `threadId`, and (for email) `subject` and `spam`. That is enough addressing to reply without a second call. The message **body is not included** — fetch `GET /v1/messages/{id}` when you need the content. ## Signing secret When you create a webhook, the API returns a `secret` **once**, in the create response. Store it securely — read endpoints (`GET /webhooks` and `GET /webhooks/{id}`) never return it again. If you lose it, rotate it: ```bash theme={null} curl -X POST https://api.useanima.sh/v1/webhooks/{id}/rotate-secret \ -H "Authorization: Bearer mk_..." # → { "id": "wh_...", "secret": "" } ``` Rotating immediately invalidates the previous secret. ## Verifying deliveries Every delivery carries a signature and a timestamp so you can confirm it came from Anima and reject replays: | Header | Description | | --------------------- | ---------------------------------------------------------------------------------- | | `X-Anima-Signature` | `v1=` — HMAC-SHA256 of `{timestamp}.{rawBody}`, keyed by your signing secret. | | `X-Anima-Timestamp` | ISO-8601 time the delivery was signed; bound into the signature. | | `X-Anima-Event` | The event name (e.g. `message.received`). | | `X-Anima-Delivery-Id` | Stable id for this delivery, unchanged across retries. | Recompute the HMAC over `{timestamp}.{rawBody}`, compare it in constant time, and reject deliveries whose timestamp falls outside a tolerance window (for example, 5 minutes). The timestamp is part of the signed content specifically so you can stop replays. ```ts theme={null} import { createHmac, timingSafeEqual } from "node:crypto"; const TOLERANCE_MS = 5 * 60 * 1000; export function verifyAnimaWebhook( rawBody: string, headers: { "x-anima-signature": string; "x-anima-timestamp": string }, secret: string, ): boolean { const timestamp = headers["x-anima-timestamp"]; // Reject stale or replayed deliveries. if (Math.abs(Date.now() - Date.parse(timestamp)) > TOLERANCE_MS) return false; const provided = headers["x-anima-signature"].replace(/^v1=/, ""); const expected = createHmac("sha256", secret) .update(`${timestamp}.${rawBody}`) .digest("hex"); const a = Buffer.from(provided, "hex"); const b = Buffer.from(expected, "hex"); return a.length === b.length && timingSafeEqual(a, b); } ``` Verify against the **raw request body**, before any JSON parse or re-serialize — re-encoding can change bytes and break the signature. ## Advanced settings The `X-Anima-Signature` HMAC already proves a delivery came from Anima. On top of it, you can have Anima present a credential your endpoint checks, and control how fast it delivers. ### Endpoint authentication Handy when your gateway expects a header rather than a signature. This is in addition to the HMAC. | Type | What Anima sends | | --------------- | -------------------------------------------------- | | `bearer` | `Authorization: Bearer ` | | `basic` | `Authorization: Basic ` | | `custom_header` | A header you name, e.g. `X-My-Secret: ` | The credential is write-only — set on create or update, never returned by a read, encrypted at rest. ### Delivery throttling and retries * **`rateLimitPerMinute`** — cap deliveries per minute to a single endpoint. Over-limit deliveries defer to the next window rather than dropping. * **`maxAttempts`** — max delivery attempts before dead-lettering (default 3). Retries use exponential backoff, and an endpoint that keeps failing is auto-disabled. Set these when you create or update a webhook — via the API, the `webhook_set` MCP tool, the CLI, or any SDK: ```bash theme={null} # CLI anima webhook create \ --url https://example.com/hooks/anima \ --events message.received,message.sent \ --auth-config '{"type":"bearer","token":"your-endpoint-token"}' \ --rate-limit-per-minute 120 \ --max-attempts 5 ``` ```python theme={null} # Python from anima import Anima, WebhookAuthBearer anima = Anima(api_key="ak_...") anima.webhooks.create( url="https://example.com/hooks/anima", events=["message.received", "message.sent"], auth_config=WebhookAuthBearer(token="your-endpoint-token"), rate_limit_per_minute=120, max_attempts=5, ) ``` ```ts theme={null} // TypeScript import { Anima } from "@anima-labs/sdk"; const anima = new Anima({ apiKey: "ak_..." }); await anima.webhooks.create({ url: "https://example.com/hooks/anima", events: ["message.received", "message.sent"], authConfig: { type: "bearer", token: "your-endpoint-token" }, rateLimitPerMinute: 120, maxAttempts: 5, }); ``` ```go theme={null} // Go rateLimit, maxAttempts := 120, 5 client.Webhooks.Create(ctx, anima.CreateWebhookParams{ URL: "https://example.com/hooks/anima", Events: []anima.WebhookEventType{anima.WebhookEventMessageReceived}, AuthConfig: anima.NewBearerAuth("your-endpoint-token"), RateLimitPerMinute: &rateLimit, MaxAttempts: &maxAttempts, }) ``` The other schemes work the same way: `basic` (username + password) and `custom_header` (a header name + value) — in the SDKs, `WebhookAuthBasic` / `WebhookAuthCustomHeader` (Python), the matching `{ type: "basic", … }` union member (TypeScript), or `anima.NewBasicAuth` / `anima.NewCustomHeaderAuth` (Go). Pass `{"type":"none"}` on update to remove authentication.