# 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.