Your agent calls from your number

Any AI agent can place calls and send texts from your own AI 2nd Line line. Not a generic robocall number — your registered, carrier-trusted number, the one people already recognise. You hand an agent a key, and you can take it back.

Why this is different from calling a telephony API

Anyone can wire up a raw telephony API in an afternoon and get a phone number. The hard part is everything carriers require before that number is trusted: brand registration, campaign approval, caller ID, and a reputation that doesn't get the call flagged as spam.

Your AI 2nd Line number already has all of that. This API lets an agent borrow it, under your permission, and lets you revoke that permission in one move. Texts an agent sends pass the same carrier compliance checks as texts you send yourself — and they land in the same thread you already read in the app.

Authentication

Every request carries your API key:

X-API-Key: a2l_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx

Authorization: Bearer <key> works too, for agent frameworks that only speak that header.

The key is shown once. We store only a hash of it, so it can never be displayed again — and a breach of our database cannot be replayed against the phone network. Lost it? Generate a new one, which revokes the old.

Getting a key

In the app: Settings › Your Agent › Agent API key › Generate a key. Copy it on the spot — that screen is the only place it is ever shown. The same screen tells you whether anything has used the key yet, and revokes it in one tap.

Your key, your traffic. Calls and texts sent with your key go out on our carrier registration under your number's identity, and you are responsible for all of them — including ones sent by software you did not write. Read the Acceptable Use Policy before you point an agent at this: consent before messaging, immediate opt-out handling, and no onward sending for third parties. Minting a key requires accepting the Terms of Service in the app, and we revoke keys that put the registration at risk.

Key management uses your normal app login, never the key itself. That is deliberate: a leaked key must not be able to mint its own replacement and turn into permanent access.

MethodPathDoes
GET/agent-keyWhether a key exists, when it was made, when it was last used. Never returns the key.
POST/agent-keyCreate a key, replacing any existing one. The only time the key is returned.
DELETE/agent-keyRevoke it. Takes effect immediately.

Calls

GET /agent/numbers

Which of your lines this key may act on. Call it first rather than guessing.

{ "numbers": [ { "numberId": "10", "e164": "+14709120899", "label": "Business" } ] }

POST /agent/calls/outbound

{
  "numberId": "10",
  "to": "+14155551234",
  "objective": "Ask whether they're open Saturday and what time they close."
}

objective is required. It is what the AI works toward and how it knows the call is finished — nobody is on the line to hang up, so without a goal a call has no reason to end. It is also the record of what your agent phoned someone about.

On a call with an objective the assistant introduces itself as an AI calling on your behalf, works the objective, reports only what it actually heard, and hangs up when it's done or when it's clear the answer isn't coming.

201 { "room": "rm_...", "status": "originating", "mode": "autonomous",
      "from": "+14709120899", "to": "+14155551234" }

GET /agent/calls/:room

Status and transcript. Only finalised speech is returned — live transcription revises itself mid-sentence, and an agent acting on interim text acts on words nobody said.

{ "status": "live", "objective": "Ask whether they're open Saturday",
  "transcript": [ { "speaker": "callee", "text": "We close at four.", "at": 1767... } ] }

Messages

POST /agent/messages

{ "numberId": "10", "to": "+14155551234", "body": "Running late, there at 3." }

Goes through the same carrier compliance checks as the app's own send, and lands in the same conversation thread you read in the app — not a separate agent-only log. Max 1600 characters.

Errors

StatusCodeMeans
401invalid_api_keyMissing, wrong, or revoked key.
400objective_requiredA call with no stated purpose.
400invalid_numberto isn't a dialable number.
403a2p_registration_requiredThat line isn't cleared for texting yet.
404—The number or call isn't yours.
429rate_limited30 calls or 100 texts an hour. Carries retryAfterSeconds.
402ai_minutes_exhaustedYou're out of AI minutes for the month.

Quickstart

# Settings > Your Agent > Agent API key > Generate a key, then:
KEY=a2l_live_...
HOST=https://www.ai2ndline.com

# 1. Which line can this key use?
NUM=$(curl -s -H "X-API-Key: $KEY" $HOST/agent/numbers | jq -r '.numbers[0].numberId')

# 2. Make the call.
ROOM=$(curl -s -X POST $HOST/agent/calls/outbound   -H "X-API-Key: $KEY" -H 'content-type: application/json'   -d "{"numberId":"$NUM","to":"+14155551234",
       "objective":"Ask their hours on Saturday."}" | jq -r .room)

# 3. Read what was said.
curl -s -H "X-API-Key: $KEY" $HOST/agent/calls/$ROOM | jq .transcript

MCP

Everything above is also reachable over MCP, with the same key, so an agent that speaks MCP needs no glue code at all.

claude mcp add --transport http ai2ndline https://www.ai2ndline.com/mcp   --header "Authorization: Bearer $KEY"

Tools: list_numbers, place_call, check_call, send_text. MCP is a transport, not a second implementation — both doors run the same checks, so there is no way in through MCP that skips a rule the HTTP API enforces.

Get a key

Open the developer console — sign up, accept the terms, generate a key, and copy a ready-made MCP config. No iPhone required.

In the app it is Settings › Your Agent › Agent API key. Either way the key is shown once.

The console carries the whole product — subscribe, claim a number, generate a key, and set up the assistant itself: name, greeting, personality, voice, what it’s allowed to do, what it knows, your hours, who it transfers to, and your calendar. Nothing about getting started requires the app.