AIMERICA
Développeurs

L'API AIMERICA

Lisez les appels et les textos, lancez des appels, envoyez des textos et gardez vos contacts à jour depuis vos propres logiciels. Des webhooks signés préviennent vos systèmes dès que quelque chose se passe. Du HTTPS et du JSON, tout simplement.

Authentification

Chaque requête porte une clé API dans l'en-tête Authorization. Un propriétaire ou un administrateur crée les clés dans l'application sous Réglages → Développeur → Clés API. La clé est affichée une seule fois, à sa création; ensuite, seuls ses premiers caractères sont listés, et elle peut être révoquée à tout moment.

curl https://a1merica.ai/voip/api/calls/?limit=5 \
  -H "Authorization: Bearer aim_live_3f9c1e2d0a7b4c5d6e8f90123456789a"

Chaque clé porte des permissions, choisies à sa création :

PermissionCe qu'elle ouvre
calls:readLister les appels, lire un appel
calls:writeLancer un appel, répondre, raccrocher
messages:readLister les conversations et les messages
messages:writeEnvoyer un texto ou un message avec image
numbers:readLister vos numéros de téléphone (acheter ou libérer un numéro ne se fait jamais par clé)
contacts:readLister les contacts, retrouver la personne derrière un numéro
contacts:writeAjouter, modifier et supprimer des contacts
Une clé agit comme la personne qui l'a créée. Elle voit les lignes de cette personne, les lignes partagées et celles de l'entreprise, ainsi que les appels qu'elle a passés — la même règle de confidentialité que dans l'application; une clé ne peut donc pas voir la ligne privée d'un collègue. Créez la clé avec le compte de la personne dont l'intégration doit suivre les lignes. La facturation, les réglages, les personnes et le reste de l'administration ne sont jamais accessibles par clé : connectez-vous pour cela.

L'adresse de base est https://a1merica.ai/voip/api. Les heures sont en ISO 8601, en UTC. Les numéros sont au format E.164 (+15145550100).

Limite de débit

Soixante requêtes par minute par clé, comptées sur une minute glissante. Au-delà, la réponse est 429 Too Many Requests avec un en-tête Retry-After: 60. Les lectures que vous répéteriez en boucle gagnent à être remplacées par un webhook.

Appels

GET /calls/

Les appels les plus récents en premier. Filtres : direction (inbound | outbound), status, user_id, search (un numéro, ou une partie), since et until (heures ISO), limit (1 à 200, 50 par défaut), 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 vaut completed, missed, voicemail, failed, canceled, ou un état en cours (ringing, in-progress). GET /calls/{id} renvoie un appel, dans la même forme.

POST /calls/initiate

Lance un appel depuis l'une de vos lignes. Le destinataire sonne aussitôt, et les règles d'enregistrement de la ligne s'appliquent.

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 est l'id d'un de vos numéros. to_number est un numéro de téléphone ou le poste d'un collègue. La réponse nomme la salle média de l'appel et un jeton pour que l'appareil de l'appelant s'y joigne — c'est ce que fait l'application AIMERICA quand elle passe un appel. Un logiciel qui ne fait qu'appeler ce point d'accès ne met personne du côté de l'appelant : la personne qui répond entend le silence. Utilisez-le depuis quelque chose qui rejoint la salle, ou pour tester le routage. Pour terminer un appel : POST /calls/{id}/hangup.

Textos

GET /sms/conversations

Une ligne par numéro avec lequel vous avez échangé des textos, du plus récent au plus ancien (limit 1 à 200).

[
  {
    "remote_number": "+15145550100",
    "local_number": "+16135550101",
    "last_message": "À 15 h, c'est bon",
    "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}

Les messages échangés avec ce numéro, du plus ancien au plus récent (limit 1 à 500, offset).

[
  {
    "id": "0a9b…",
    "direction": "inbound",
    "from_number": "+15145550100",
    "to_number": "+16135550101",
    "body": "À 15 h, c'est bon",
    "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": "Votre rendez-vous est confirmé pour 15 h.", "media_urls": null }

201
{ "id": "1c2d…", "direction": "outbound", "from_number": "+16135550101", "to_number": "+15145550100",
  "body": "Votre rendez-vous est confirmé pour 15 h.", "media_urls": [], "status": "sent", "created_at": "…" }

Un numéro qui a répondu STOP (ou ARRÊT) est refusé avec 403. La ligne doit avoir les textos activés.

Numéros

GET /phone-numbers/

[
  {
    "id": "c4e1…",
    "number": "+16135550101",
    "label": "Réception",
    "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/

Tous les contacts de l'entreprise. GET /contacts/lookup?phone=+15145550100 retrouve la personne derrière un numéro.

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} modifie n'importe lequel de ces champs; DELETE /contacts/{id} supprime le contact (204).

Webhooks

Un propriétaire ou un administrateur ajoute une adresse sous Réglages → Développeur → Webhooks, choisit les événements et reçoit un secret de signature (affiché une seule fois). Dès lors, AIMERICA envoie (POST) un corps JSON à cette adresse pour chaque événement. L'adresse doit être publique et en https://.

ÉvénementQuand
call.startedUn appel commence : un appelant de l'extérieur fait sonner une ligne, ou quelqu'un passe un appel
call.answeredQuelqu'un décroche
call.endedL'appel est terminé — avec sa durée, sa direction, les numéros, l'état final et s'il existe un enregistrement
call.missedUn appel entrant auquel personne n'a répondu (envoyé aussi s'il est allé à la boîte vocale)
sms.receivedUn texto est arrivé sur l'une de vos lignes
sms.sentUn texto est parti de l'une de vos lignes (par une personne, une réponse automatique, une programmation ou l'API)
voicemail.newUn message vocal a été enregistré
fax.receivedUne télécopie est arrivée

Le corps

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 pour sms.received / sms.sent contient id, direction, from_number, to_number, body, media_urls, status, phone_number_id et received_at ou sent_at; pour voicemail.new : id, call_id, from_number, to_number, duration_seconds, transcription, recording_url, phone_number_id, received_at; pour fax.received : id, from_number, to_number, page_count, status, phone_number_id, received_at.

Vérifier la signature

Prenez t dans l'en-tête, joignez-le au corps brut avec un point, et calculez un HMAC-SHA256 avec votre secret. Comparez avec v1 en temps constant, et refusez un horodatage de plus de cinq 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));
}

Réponse, nouvelles tentatives, ordre

Utiliser AIMERICA avec Zapier

Il n'y a pas encore d'application AIMERICA dans le répertoire Zapier; les deux pièces ci-dessus sont tout ce dont Zapier a besoin.

  1. Déclencheurs (quelque chose se passe dans AIMERICA → un Zap démarre). Dans Zapier, commencez un Zap avec Webhooks by Zapier → Catch Hook. Copiez l'adresse que Zapier vous donne, ajoutez-la sous Réglages → Développeur → Webhooks, cochez les événements (un appel manqué, un nouveau message vocal, un texto reçu…), puis appuyez sur Envoyer un test. Zapier affichera le ping; les champs de chaque événement suivant se trouvent sous data.
  2. Actions (un Zap fait quelque chose dans AIMERICA). Ajoutez une étape Webhooks by Zapier → Custom Request. Méthode POST, URL https://a1merica.ai/voip/api/sms/send (ou tout autre point d'accès ci-dessus), un en-tête Authorization: Bearer aim_live_…, Content-Type: application/json, et le corps JSON de cette page rempli avec les champs de votre Zap.
  3. Pensez aux permissions de la clé : un Zap qui ne fait qu'envoyer des textos mérite une clé avec messages:write seulement.

La même recette fonctionne avec Make, n8n, Power Automate et tout outil capable de recevoir un webhook et d'envoyer une requête HTTPS.

Erreurs

CodeSignification
400Quelque chose cloche dans la requête; detail dit quoi
401La clé est absente, erronée ou révoquée
402Le compte de l'entreprise demande une action avant celle-ci (p. ex. une carte au dossier)
403La clé n'a pas la permission requise, ou ce point d'accès n'est jamais offert par clé
404Introuvable, ou invisible pour la personne que la clé représente
429Plus de soixante requêtes dans la minute — attendez le délai de Retry-After

Chaque corps d'erreur est {"detail": "une phrase que vous pouvez afficher"}.

Slack, Google Workspace et Microsoft 365 se configurent dans l'application sous Intégrations, sans code. Une question sur l'API? Écrivez-nous.

English