Getting Started with Anima in 5 Minutes
Create an agent identity, send an email from it, and place a voice call. A working Anima integration in roughly five minutes.
If you can run a TypeScript script, you can launch your first agent communication workflow in minutes. This guide shows the fastest path: install the SDK, create an identity, send an email, and place a phone call. You will also see where to expand into SMS and vault-backed secrets when you are ready.
1) Install the SDK#
Create a small project or use an existing Node runtime. Add the Anima SDK:
bun add @anima-labs/sdkSet your API key in environment variables:
export ANIMA_API_KEY="ak_..."If you are building in a server app, place this key in your secrets manager and inject it at runtime. Do not expose it client-side.
2) Initialize the client and create an agent#
An agent is the actor boundary. Instead of one shared account for every automation, each agent gets its own identity, its own credentials, and its own policy.
import { Anima } from "@anima-labs/sdk";
const anima = new Anima({ apiKey: process.env.ANIMA_API_KEY! });
const agent = await anima.agents.create({
orgId: process.env.ANIMA_ORG_ID!,
name: "Support Triage Agent",
slug: "support-triage-agent",
provisionPhone: true,
metadata: {
environment: "sandbox",
owner: "support-platform",
},
});
console.log("Agent created:", agent.id);
console.log("Inbox:", agent.emailIdentities[0]?.email);The agent gets an inbox on agents.useanima.sh automatically. provisionPhone asks for a number as well — that one needs a paid plan, since telephony starts on Starter.
In production, you can attach policy constraints to the agent itself: allowed and blocked recipient domains for email, per-hour send and SMS rate limits, allowed destination countries for phone and voice, and a switch that disables a channel outright. Spend is bounded separately, by your plan's quotas rather than by agent policy.
3) Send your first email#
Now that your agent exists, you can send mail as that agent. to is an array, and body is the plain-text part — bodyHtml is optional and additive, so a client that refuses HTML still has something to render:
await anima.messages.sendEmail({
agentId: agent.id,
to: ["customer@example.com"],
subject: "Welcome to Anima",
body: "You're live. Your agent channel is configured and ready.",
bodyHtml: `<h1>You're live</h1><p>Your agent channel is configured and ready.</p>`,
});If you are receiving inbound messages too, configure webhook delivery and verify signatures in your backend. This preserves a tamper-evident audit path for agent actions.
4) Place a phone or voice call#
Voice uses the same agent boundary. A minimal outbound call looks like this:
const call = await anima.calls.create({
agentId: agent.id,
to: "+15551234567",
greeting: "Hello! This is your support assistant confirming your ticket was received.",
});
console.log(call.callId, call.state);Outbound voice runs behind a server-side TCPA consent gate. It is a one-time attestation your org completes in the console under Settings → Outbound Calling & SMS; until it is done, every outbound call is refused before it reaches the dialer. There is no per-call consent flag. For SMS the flow is anima.messages.sendSms — same agent boundary, different channel. That consistency is useful when you later add fallback logic.
5) Expand capabilities without changing your trust model#
After the quickstart, most teams add at least one of these:
- Vault-backed secrets for agent credentials, stored in a dedicated encrypted vault.
- A voice consent gate — a TCPA
consent_sourceassertion enforced server-side on every outbound call. - Agent-to-agent messaging — a
did:webidentifier, a public agent card, and signed envelopes other agents can verify without holding any of your keys.
The value is that you do not need to invent a separate identity model for each capability. The same core identity remains the source of truth for attribution, policy, and audit trails.
Complete quickstart script#
This script puts the full happy path together:
import { Anima } from "@anima-labs/sdk";
async function main() {
const anima = new Anima({ apiKey: process.env.ANIMA_API_KEY! });
const agent = await anima.agents.create({
orgId: process.env.ANIMA_ORG_ID!,
name: "Onboarding Agent",
slug: "onboarding-agent",
});
await anima.messages.sendEmail({
agentId: agent.id,
to: ["ops@example.com"],
subject: "Agent onboarding complete",
body: "Agent and channels are live.",
});
await anima.calls.create({
agentId: agent.id,
to: "+15557654321",
greeting: "Your onboarding workflow completed successfully.",
});
console.log("Done. Agent:", agent.id);
}
main().catch((error) => {
console.error(error);
process.exit(1);
});From here, you can move directly into production hardening: policy scoping, webhook verification, channel-specific retries, and observability dashboards. The important part is already done: your agent now has a first-class identity and can operate across communication surfaces cleanly.