AIMERICA
Developers

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:

PermissionWhat it opens
calls:readList calls, read one call
calls:writeStart a call, answer, hang up
messages:readList conversations and messages
messages:writeSend a text or picture message
numbers:readList your phone numbers (buying and releasing numbers is never done by key)
contacts:readList contacts, look a number up
contacts:writeAdd, change and delete contacts
A key acts as the person who created it. It sees that person's lines, shared lines and company lines, and the calls they placed — the same privacy rule as in the app, so a key cannot see a colleague's private line. Create the key as the person whose lines the integration should watch. Billing, settings, people and other administration are never reachable with a key: sign in for those.

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://.

EventWhen
call.startedA call begins: an outside caller starts ringing a line, or someone places a call
call.answeredSomeone picks up
call.endedThe call is over — with its duration, direction, numbers, final status and whether a recording exists
call.missedAn inbound call nobody answered (also sent when it went to voicemail)
sms.receivedA text arrived on one of your lines
sms.sentA text was sent from one of your lines (by a person, an auto-reply, a schedule or the API)
voicemail.newA voicemail was saved
fax.receivedA 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

Using AIMERICA with Zapier

There is no AIMERICA app in the Zapier directory yet; the two pieces above are all Zapier needs.

  1. 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 under data.
  2. Actions (a Zap does something in AIMERICA). Add a step Webhooks by Zapier → Custom Request. Method POST, URL https://a1merica.ai/voip/api/sms/send (or any endpoint above), a header Authorization: Bearer aim_live_…, Content-Type: application/json, and the JSON body from this page with your Zap's fields filled in.
  3. Remember the key's permissions: give a Zap that only sends texts a key with messages:write alone.

The same recipe works with Make, n8n, Power Automate and any tool that can catch a webhook and send an HTTPS request.

Errors

CodeMeaning
400Something in the request is wrong; detail says what
401The key is missing, wrong or revoked
402The company's account needs attention before this action (e.g. a card on file)
403The key lacks the permission this endpoint needs, or the endpoint is never available by key
404Not found, or not visible to the person the key acts as
429More 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.

Français