The AIMERICA API
Read calls and texts, place calls, send texts and keep contacts in step from your own software. Signed webhooks tell your systems the moment something happens. Plain HTTPS and JSON.
Authentication
Every request carries an API key in the Authorization header. An owner or admin creates keys in the app under Settings → Developer → API keys. The key is shown once, when it is created; afterwards only its first characters are listed, and it can be revoked at any time.
curl https://a1merica.ai/voip/api/calls/?limit=5 \
-H "Authorization: Bearer aim_live_3f9c1e2d0a7b4c5d6e8f90123456789a"
Each key carries permissions, chosen when it is made:
| Permission | What it opens |
|---|---|
calls:read | List calls, read one call |
calls:write | Start a call, answer, hang up |
messages:read | List conversations and messages |
messages:write | Send a text or picture message |
numbers:read | List your phone numbers (buying and releasing numbers is never done by key) |
contacts:read | List contacts, look a number up |
contacts:write | Add, change and delete contacts |
The base address is https://a1merica.ai/voip/api. Times are ISO 8601 in UTC. Phone numbers are E.164 (+15145550100).
Rate limit
Sixty requests a minute per key, counted over a sliding minute. Above that the answer is 429 Too Many Requests with a Retry-After: 60 header. Reads that you would otherwise repeat are better replaced by a webhook.
Calls
GET /calls/
The most recent calls first. Filters: direction (inbound | outbound), status, user_id, search (a number or part of one), since and until (ISO times), limit (1–200, default 50), offset.
GET /voip/api/calls/?direction=inbound&since=2026-09-24T00:00:00Z&limit=2
[
{
"id": "6b1f0d2a-7e0c-4f1a-9c3a-1d2e3f4a5b6c",
"direction": "inbound",
"status": "completed",
"from_number": "+15145550100",
"to_number": "+16135550101",
"started_at": "2026-09-24T14:02:11+00:00",
"ended_at": "2026-09-24T14:05:40+00:00",
"duration_seconds": 209,
"recording_url": "/api/calls/6b1f0d2a-.../recording",
"transcript": null,
"ai_summary": null,
"caller_name": "Marie Tremblay",
"follow_up": null,
"record_call": true,
"transcribe_call": false,
"created_at": "2026-09-24T14:02:09+00:00",
"user_id": "9d8c7b6a-...",
"user_name": "Alex Roy",
"phone_number": "+16135550101",
"ai_agent_id": null,
"ai_agent_name": null,
"ai_outcome": null
}
]
status is one of completed, missed, voicemail, failed, canceled, or a live state (ringing, in-progress). GET /calls/{id} returns one call in the same shape.
POST /calls/initiate
Starts a call from one of your lines. The destination rings at once, and the recording rules of the line apply.
POST /voip/api/calls/initiate
{ "to_number": "+15145550100", "from_number_id": "c4e1…", "record_call": true }
201
{ "call_id": "6b1f…", "room_name": "empire-out-…", "livekit_url": "wss://…", "livekit_token": "…" }
from_number_id is the id of one of your numbers. to_number is a phone number or a colleague's extension. The response names the call's media room and a token for the caller's device to join it — that is what the AIMERICA app does when it places a call. Software that only calls this endpoint puts nobody on the caller's side, so the person answering hears silence: use it from something that joins the room, or to test routing. To end a call: POST /calls/{id}/hangup.
Text messages
GET /sms/conversations
One row per number you have exchanged texts with, newest first (limit 1–200).
[
{
"remote_number": "+15145550100",
"local_number": "+16135550101",
"last_message": "See you at 3",
"last_message_at": "2026-09-24T15:10:03+00:00",
"message_count": 12,
"unread_count": 1,
"user_id": "9d8c…",
"user_name": "Alex Roy"
}
]
GET /sms/conversations/{number}
The messages with that number, oldest first (limit 1–500, offset).
[
{
"id": "0a9b…",
"direction": "inbound",
"from_number": "+15145550100",
"to_number": "+16135550101",
"body": "See you at 3",
"media_urls": [],
"status": "received",
"created_at": "2026-09-24T15:10:03+00:00"
}
]
POST /sms/send
POST /voip/api/sms/send
{ "from_number_id": "c4e1…", "to_number": "+15145550100", "body": "Your appointment is confirmed for 3 pm.", "media_urls": null }
201
{ "id": "1c2d…", "direction": "outbound", "from_number": "+16135550101", "to_number": "+15145550100",
"body": "Your appointment is confirmed for 3 pm.", "media_urls": [], "status": "sent", "created_at": "…" }
A number that has replied STOP is refused with 403. The line must have texting switched on.
Numbers
GET /phone-numbers/
[
{
"id": "c4e1…",
"number": "+16135550101",
"label": "Front desk",
"status": "active",
"provider": "telnyx",
"trunk_group": null,
"sms_enabled": true,
"e911_configured": true,
"cnam_display": "AIMERICA",
"voicemail_enabled": true,
"routing_json": { "ring_seconds": 24 },
"monthly_cost_cents": 500,
"assigned_to_user_id": "9d8c…",
"assigned_to_name": "Alex Roy",
"assigned_to_group_id": null,
"assigned_to_group_name": null,
"ai_agent_id": null,
"ai_agent_name": null,
"ivr_flow_id": null,
"ivr_flow_name": null,
"created_at": "2026-08-01T12:00:00+00:00",
"number_type": "local",
"hd_voice": true,
"hd_voice_supported": true
}
]
Contacts
GET /contacts/
All contacts of the company. GET /contacts/lookup?phone=+15145550100 finds the person behind a number.
POST /contacts/
POST /voip/api/contacts/
{ "first_name": "Marie", "last_name": "Tremblay", "company": "Tremblay Inc.",
"phone_number": "+15145550100", "email": "marie@example.com", "notes": null, "tags": ["client"] }
201
{ "id": "7f6e…", "first_name": "Marie", "last_name": "Tremblay", "company": "Tremblay Inc.",
"phone_number": "+15145550100", "email": "marie@example.com", "notes": null,
"is_team_member": false, "linked_user_id": null, "tags": ["client"], "book": "organisation",
"created_at": "2026-09-24T15:20:00+00:00" }
PUT /contacts/{id} changes any of those fields; DELETE /contacts/{id} removes the contact (204).
Webhooks
An owner or admin adds an address under Settings → Developer → Webhooks, picks the events, and gets a signing secret (shown once). From then on AIMERICA POSTs a JSON body to that address for each event. The address must be public and https://.
| Event | When |
|---|---|
call.started | A call begins: an outside caller starts ringing a line, or someone places a call |
call.answered | Someone picks up |
call.ended | The call is over — with its duration, direction, numbers, final status and whether a recording exists |
call.missed | An inbound call nobody answered (also sent when it went to voicemail) |
sms.received | A text arrived on one of your lines |
sms.sent | A text was sent from one of your lines (by a person, an auto-reply, a schedule or the API) |
voicemail.new | A voicemail was saved |
fax.received | A fax arrived |
The body
POST https://example.com/aimerica
Content-Type: application/json
X-AIMERICA-Event: call.ended
X-AIMERICA-Delivery: 3e2d1c0b-…
X-AIMERICA-Signature: t=1758724800,v1=5f1a…c9
{
"id": "3e2d1c0b-…",
"event": "call.ended",
"created_at": "2026-09-24T16:00:00+00:00",
"org_id": "a1b2…",
"data": {
"id": "6b1f…",
"direction": "inbound",
"status": "completed",
"from_number": "+15145550100",
"to_number": "+16135550101",
"started_at": "2026-09-24T15:55:02+00:00",
"answered_at": "2026-09-24T15:55:09+00:00",
"ended_at": "2026-09-24T15:59:58+00:00",
"duration_seconds": 289,
"recording": true,
"user_id": "9d8c…",
"phone_number_id": "c4e1…"
}
}
data for sms.received / sms.sent carries id, direction, from_number, to_number, body, media_urls, status, phone_number_id and received_at or sent_at; for voicemail.new: id, call_id, from_number, to_number, duration_seconds, transcription, recording_url, phone_number_id, received_at; for fax.received: id, from_number, to_number, page_count, status, phone_number_id, received_at.
Checking the signature
Take t from the header, join it to the raw body with a dot, and compute HMAC-SHA256 with your secret. Compare with v1 in constant time, and refuse a timestamp older than five minutes.
# Python
import hmac, hashlib, time
def verify(secret: str, header: str, body: bytes) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
t, v1 = parts["t"], parts["v1"]
if abs(time.time() - int(t)) > 300:
return False
mac = hmac.new(secret.encode(), f"{t}.".encode() + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(mac, v1)
// Node.js
const crypto = require("crypto");
function verify(secret, header, rawBody) {
const p = Object.fromEntries(header.split(",").map(s => s.split("=")));
if (Math.abs(Date.now() / 1000 - Number(p.t)) > 300) return false;
const mac = crypto.createHmac("sha256", secret).update(`${p.t}.`).update(rawBody).digest("hex");
return crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(p.v1));
}
Answering, retries, order
- Answer
2xxwithin ten seconds. Anything else, or no answer, counts as a failure. - After a failure we try again after 1 minute, 5 minutes, 30 minutes and 2 hours, then give up on that delivery and count a failure on the address. Twenty-five failures in a row pause the address; switch it back on in Settings once your server is well.
- Each delivery has a unique
id; use it to ignore a repeat. A call's events are sent in the order they happened, but retries can arrive out of order — readdata.statusand the timestamps rather than trusting the arrival order. - The Test button in Settings sends a
pingevent with the same signature, so you can check your receiver before any real traffic.
Using AIMERICA with Zapier
There is no AIMERICA app in the Zapier directory yet; the two pieces above are all Zapier needs.
- Triggers (something happens in AIMERICA → a Zap runs). In Zapier, start a Zap with Webhooks by Zapier → Catch Hook. Copy the address Zapier gives you, add it under Settings → Developer → Webhooks, tick the events (a missed call, a new voicemail, a received text…), and press Test. Zapier will show the
ping; the fields of every later event appear underdata. - Actions (a Zap does something in AIMERICA). Add a step Webhooks by Zapier → Custom Request. Method
POST, URLhttps://a1merica.ai/voip/api/sms/send(or any endpoint above), a headerAuthorization: Bearer aim_live_…,Content-Type: application/json, and the JSON body from this page with your Zap's fields filled in. - Remember the key's permissions: give a Zap that only sends texts a key with
messages:writealone.
The same recipe works with Make, n8n, Power Automate and any tool that can catch a webhook and send an HTTPS request.
Errors
| Code | Meaning |
|---|---|
400 | Something in the request is wrong; detail says what |
401 | The key is missing, wrong or revoked |
402 | The company's account needs attention before this action (e.g. a card on file) |
403 | The key lacks the permission this endpoint needs, or the endpoint is never available by key |
404 | Not found, or not visible to the person the key acts as |
429 | More than sixty requests in a minute — wait for Retry-After |
Every error body is {"detail": "a sentence you can show"}.
Slack, Google Workspace and Microsoft 365 are set up in the app under Integrations, with no code. Questions about the API: write to us.
