Einführung
Die Public API ist eine REST-API mit JSON-Bodys. Alle Endpoints liegen unter der Base-URL:
https://comu-ai.de/api/v1Typischer Ablauf: Dein System (Website-Formular, CRM, eigenes Backend) legt per POST /v1/leads einen Lead an. Comu.ai schreibt den Lead sofort über den gewählten Agent per WhatsApp an. Ob die Nummer WhatsApp-fähig ist, erfährst du Sekunden später per Webhook (lead.undeliverable) oder per Polling (GET /v1/leads/{id}).
Authentifizierung
Alle Requests brauchen einen API-Schlüssel als Bearer-Token. Schlüssel erstellst du in den Einstellungen unter „API“ (nur Account-Owner). Der Schlüssel wird dir genau einmal angezeigt — speichere ihn sicher, z. B. in einem Secret-Manager. Übertrage ihn nie im Frontend-Code.
curl https://comu-ai.de/api/v1/agents \
-H "Authorization: Bearer comu_live_IhrSchluessel"Ungültige oder widerrufene Schlüssel erhalten 401 UNAUTHORIZED.
Rate Limits
Limits gelten pro API-Schlüssel und Minute:
| Endpoint | Limit |
|---|---|
POST /v1/leads | 10 Requests / Minute (jeder Call löst einen WhatsApp-Versand aus) |
GET-Endpoints | 60 Requests / Minute |
Bei Überschreitung antwortet die API mit 429 RATE_LIMITED inklusive Retry-After-Header. Jede Antwort enthält X-RateLimit-Limit, X-RateLimit-Remaining und X-RateLimit-Reset.
Lead anlegen
POST/v1/leads
Legt einen Lead an und verschickt sofort die Startnachricht (bzw. Opt-In-Frage) des Agents. Der Agent muss dafür bereit sein — seine Opener-Vorlage muss von WhatsApp freigegeben sein (siehe Agents auflisten).
| Feld | Typ | Beschreibung |
|---|---|---|
agent_id | string (UUID), Pflicht | Der Agent, der den Lead anschreibt (siehe GET /v1/agents) |
phone | string, Pflicht | Telefonnummer, beliebiges Format — wird zu E.164 normalisiert (deutsche Nummern ohne Ländervorwahl werden als +49 interpretiert) |
name | string, Pflicht | Name des Leads (Vorname wird in der Startnachricht verwendet) |
email | string, optional | E-Mail-Adresse |
external_id | string, optional | Deine eigene ID (Idempotenz-Schlüssel, siehe unten) |
custom_fields | object, optional | Schlüssel-Wert-Paare (z. B. Formularantworten) — stehen dem KI-Agent als Kontext zur Verfügung |
curl -X POST https://comu-ai.de/api/v1/leads \
-H "Authorization: Bearer comu_live_IhrSchluessel" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "6f1e...c2a0",
"phone": "+49 151 23456789",
"name": "Max Mustermann",
"email": "max@example.org",
"external_id": "crm-lead-4711",
"custom_fields": { "plz": "10717", "anliegen": "Beratung Solaranlage" }
}'Antwort 201 Created:
{
"lead": {
"id": "a3d2...91bc",
"external_id": "crm-lead-4711",
"status": "open",
"name": "Max Mustermann",
"phone": "4915123456789",
"agent_id": "6f1e...c2a0",
"created_at": "2026-07-08T09:30:00.000Z",
"whatsapp": { "undeliverable": false, "undeliverable_at": null },
"qualification": { "qualified": false, "qualified_at": null, "trigger": null },
"appointment": { "at": null },
"opted_out_at": null
},
"first_message": { "status": "sent" }
}first_message.status ist "sent" oder "blocked" (mit reason), falls WhatsApp den Versand abgelehnt hat. Der Lead existiert auch bei "blocked" und ist im Dashboard sichtbar.
Idempotenz
Übergib eine external_id (im Body oder als Idempotency-Key-Header). Wiederholte Requests mit derselben external_id legen keinen zweiten Lead an, sondern antworten 200 mit dem bestehenden Lead und "replayed": true. Ein Lead mit bereits aktiver Telefonnummer wird unabhängig davon mit 409 DUPLICATE_LEAD abgelehnt.
Lead abfragen
GET/v1/leads/{id}
{id} ist die Comu-Lead-UUID oder deine external_id. Die Antwort enthält denselben lead-Body wie oben — inklusive whatsapp.undeliverable für Polling, falls du keine Webhooks nutzen willst.
curl https://comu-ai.de/api/v1/leads/crm-lead-4711 \
-H "Authorization: Bearer comu_live_IhrSchluessel"Agents auflisten
GET/v1/agents
Liefert alle Agents des Accounts mit ihrer Sende-Bereitschaft. Nur Agents mit "ready": true können Leads entgegennehmen; sonst nennt reason die Ursache (NO_WHATSAPP, NO_TEMPLATE, TEMPLATE_NOT_APPROVED).
{
"agents": [
{ "id": "6f1e...c2a0", "name": "Solar-Beratung", "ready": true, "reason": null }
]
}Fehlercodes
Fehler kommen immer im selben Envelope:
{ "error": { "code": "TEMPLATE_NOT_APPROVED", "message": "...", "details": { } } }| HTTP | Code | Bedeutung |
|---|---|---|
| 400 | INVALID_JSON / VALIDATION_ERROR | Body fehlerhaft; details.issues nennt die Felder |
| 400 | INVALID_PHONE | Nummer konnte nicht zu E.164 normalisiert werden |
| 401 | UNAUTHORIZED | API-Schlüssel fehlt, ist ungültig oder widerrufen |
| 404 | AGENT_NOT_FOUND / LEAD_NOT_FOUND | Ressource existiert nicht in diesem Account |
| 409 | DUPLICATE_LEAD | Aktiver Lead mit dieser Nummer existiert bereits (details.existing_lead_id) |
| 422 | NO_WHATSAPP | Agent hat kein aktives WhatsApp-Konto |
| 422 | NO_TEMPLATE | Agent hat keine Opener-Vorlage konfiguriert |
| 422 | TEMPLATE_NOT_APPROVED | Opener-Vorlage wartet noch auf WhatsApp-Freigabe |
| 429 | RATE_LIMITED | Rate Limit überschritten — Retry-After beachten |
| 500 | INTERNAL_ERROR | Unerwarteter Fehler — Request wiederholen |
Webhooks
Registriere in den Einstellungen unter „API“ eine https-URL und erhalte Lead-Ereignisse als POST mit JSON-Body. Ohne Event-Auswahl bekommst du alle Ereignisse.
| Event | Wann |
|---|---|
lead.created | Neuer Lead angelegt (API, Meta-Formular, Dashboard, WhatsApp-Erstkontakt) |
lead.undeliverable | WhatsApp meldet: Nummer nicht erreichbar (kein WhatsApp) — i. d. R. Sekunden nach dem ersten Sendeversuch |
lead.responded | Lead hat zum ersten Mal geantwortet |
lead.qualified | Lead wurde qualifiziert (data.trigger: booking oder classifier) |
lead.lost | Lead verloren (data.reason: opt_out, opt_in_expired oder manual) |
appointment.booked | Termin gebucht oder umgebucht (data.appointment mit Zeit, Provider, Link) |
Beispiel-Payload:
{
"id": "7c04...e8d1",
"type": "lead.undeliverable",
"created_at": "2026-07-08T09:30:07.000Z",
"data": {
"lead": {
"id": "a3d2...91bc",
"external_id": "crm-lead-4711",
"status": "open",
"whatsapp": { "undeliverable": true, "undeliverable_at": "2026-07-08T09:30:06.512Z" }
}
}
}Signatur prüfen
Jeder Request trägt einen X-Comu-Signature-Header im Format t=<unix>,v1=<hex>. Die Signatur ist ein HMAC-SHA256 über `${t}.${rawBody}` mit deinem Endpoint-Secret (whsec_…). Prüfe zusätzlich, dass t nicht älter als 5 Minuten ist (Replay-Schutz).
// Node.js
const crypto = require("crypto")
function verifyComuSignature(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(signatureHeader.split(",").map(p => p.split("=")))
const age = Math.abs(Date.now() / 1000 - Number(parts.t))
if (age > 300) return false // Replay-Fenster 5 min
const expected = crypto.createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex")
return crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected))
}Weitere Header: X-Comu-Event (Event-Typ), X-Comu-Event-Id (stabile Event-ID für Dedup) und X-Comu-Delivery-Id (pro Zustellversuch).
Zustellung & Retries
Antworte mit einem 2xx-Status innerhalb von 10 Sekunden. Zustellung ist at-least-once: Dedupliziere über die Event-ID (id im Body bzw. X-Comu-Event-Id). Bei Fehlern versuchen wir es erneut nach 1, 5 und 30 Minuten sowie 2 und 12 Stunden (insgesamt 6 Versuche). Nach 20 fehlgeschlagenen Zustellungen in Folge wird der Endpoint automatisch deaktiviert — in den Einstellungen kannst du ihn wieder aktivieren und testen.
WhatsApp-Erreichbarkeit
Es gibt keinen offiziellen Weg, vor dem Versand zu prüfen, ob eine Nummer auf WhatsApp ist — Meta bietet dafür bewusst keine Lookup-API an. Das zuverlässige (und einzige ToS-konforme) Signal entsteht beim ersten Sendeversuch: Ist die Nummer nicht auf WhatsApp, meldet Meta den Fehler 131026 („Message undeliverable“) — asynchron, typischerweise wenige Sekunden nach dem Versand.
Comu.ai übersetzt das für dich in Echtzeit: Lead per API anlegen → Startnachricht geht raus → bei Nichterreichbarkeit kommt das Webhook-Event lead.undeliverable (bzw. whatsapp.undeliverable = true beim Polling). In der Praxis hast du das Ergebnis meist in unter 10 Sekunden nach dem API-Call. Nicht zustellbare Nachrichten berechnet WhatsApp nicht.
Hinweis: 131026 ist ein Sammel-Fehlercode — neben „kein WhatsApp“ kann in seltenen Fällen z. B. eine stark veraltete WhatsApp-Version des Empfängers die Ursache sein. Für Formular-Validierung in Echtzeit empfehlen wir zusätzlich eine clientseitige Plausibilitätsprüfung (Mobilfunknummer, E.164-Format), bevor du den Lead pushst.
Abrechnung
API-Leads durchlaufen dieselbe Pipeline wie alle anderen Leads: Abgerechnet werden ausschließlich qualifizierte Leads gemäß deinem Tarif — das Anlegen von Leads und die Webhook-Zustellung sind kostenfrei. Nicht zustellbare Nummern (lead.undeliverable) erzeugen keine Kosten.
Stand: Juli 2026 · API-Version v1 · Fragen an info@phonoflow.de