Skip to main content

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
  • 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

npm install @anima-labs/sdk

2. Search for available numbers

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);
}

3. Provision the number

const phone = await anima.phones.provision({
  agentId,
  countryCode: "US",
  areaCode: "415",
  capabilities: ["sms", "voice"],
});

console.log(`Provisioned: ${phone.phoneNumber}`);
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

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})`);

5. Call yourself

Outbound voice calls run through the voice gate before dialing. Only call recipients where you have the required consent.
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}`);
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}`);
}

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.
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:
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, TCPA consent posture, Reassigned Numbers Database checks, time-of-day windows, and per-tier call caps server-side. If a gate fails, the call is rejected before the provider dials.

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 | Set up webhooks | Get started