Leitfaden

SwissAI gibt Ihnen Sprachagenten, die Telefonanrufe beantworten. Sie erstellen sie im Dashboard mit Prompt, Sprache und optionalen Tools; jeder Agent erhält eine SIP-URI. Leiten Sie Anrufe an diese URI, und der Agent nimmt ab, spricht mit dem Anrufer und ruft Ihre HTTP-Endpunkte auf, wenn er Daten braucht oder handeln soll.

Alles wird im Dashboard eingerichtet: Agenten, Tools, Ihre Kunden und die Abrechnung.

Agenten

Agenten erstellen und bearbeiten Sie im Dashboard: Name, Sprache, Scope (der Prompt), eine optionale Wissensbasis, die Modelle und die Tools. Jeder Agent erhält eine ID und eine SIP-URI und kann einem Ihrer Kunden zugewiesen werden.

Beispiel für einen Scope

Schreiben Sie den Scope so, wie Sie eine neue Kollegin einweisen würden: wer der Agent ist, was er tun darf, was er nie tun darf und wie das Gespräch endet.

Du bist Anna, die Assistentin der Zahnarztpraxis Keller in Zürich.
Du gehst ans Telefon, wenn der Empfang besetzt oder geschlossen ist.

Was du tust:
- Termine buchen, verschieben oder absagen, mit den Tools;
- Fragen zu Öffnungszeiten, Adresse und Preisen beantworten (siehe Wissensbasis).

Regeln:
- sprich kurz und höflich, eine Frage nach der anderen;
- wiederhole vor dem Buchen Tag, Uhrzeit und Namen und warte auf ein klares Ja;
- gib nie medizinischen Rat: biete bei Schmerzen oder Notfällen den ersten freien
  Termin an und nenne die Notfallnummer;
- wenn du nicht helfen kannst, notiere Namen und Telefonnummer und sage, dass die Praxis zurückruft.

Beende das Gespräch, indem du das Vereinbarte wiederholst.

Tools

Tool-Definition

Ein Tool ist ein HTTP-Endpunkt auf Ihrer Seite, dem Modell mit einem JSON-Schema beschrieben. Das Modell entscheidet, wann und mit welchen Argumenten es ihn aufruft; die Plattform führt den HTTP-Request aus.

{
  "name": "book_appointment",
  "description": "Bucht einen Termin. Erst aufrufen, wenn der Anrufer Tag, Uhrzeit und Namen bestätigt hat.",
  "method": "POST",
  "url": "https://api.example.ch/clinics/{{clinic}}/appointments",
  "headers": { "Authorization": "Bearer sk_…" },
  "content_type": "application/json",
  "body": "{ \"start\": \"{{start}}\", \"name\": \"{{name}}\", \"phone\": \"{{phone}}\" }",
  "schema": {
    "type": "object",
    "properties": {
      "clinic": { "type": "string", "description": "Code der Praxis: immer 'zurich'" },
      "start":  { "type": "string", "description": "Beginn, yyyy-MM-dd HH:mm, z. B. 2026-10-06 10:30" },
      "name":   { "type": "string", "description": "Vor- und Nachname des Anrufers" },
      "phone":  { "type": "string", "description": "Telefonnummer des Anrufers, nur Ziffern" }
    },
    "required": ["clinic", "start", "name", "phone"]
  }
}
FeldFürBeschreibung
nameModellFunktionsname, Buchstaben und Unterstriche.
descriptionModellWas sie tut und wann sie aufzurufen ist. Für das Modell schreiben.
schemaModellJSON-Schema der Argumente.
response_schemaModellOptional. Form Ihrer Antwort, damit das Modell sie lesen kann.
urlRuntimeIhr Endpunkt, HTTPS.
methodRuntimeGET (Argumente im Query-String) oder POST (Argumente als JSON-Body).
content_typeRuntimeÜblicherweise application/json.
headersRuntimeOptional. Header, die bei jedem Aufruf gesendet werden, z. B. Authorization; Werte dürfen {{Parameter}} enthalten.
bodyRuntimeOptional. Body-Vorlage mit {{Parametern}}. Leer: Parameter, die nicht in URL oder Headern stehen, gehen als Query-String (GET, DELETE) oder JSON-Body.

Parameter und Platzhalter

Schreiben Sie {{name}} in die URL, Header-Werte oder den Body. Jeder Platzhalter muss in schema.properties mit einer Beschreibung deklariert sein: Das Modell füllt ihn aus dem Gespräch, die Runtime ersetzt ihn (URL-kodiert in der URL, escaped in einem JSON-Body). Das Dashboard baut das Schema aus den Platzhaltern und lässt das Tool vor dem Speichern von Claude prüfen.

So ruft der Agent Sie auf

Jeder Request trägt den Header token mit dem tool_token des Agenten. Antworten Sie mit 200 und einem JSON-Body:

Ihr Endpunkt: Beispiele

Das ist die Anfrage, die die Plattform für das Tool oben sendet, und ein minimaler Endpunkt, der sie beantwortet. Prüfen Sie zuerst den Header token: Es ist das Tool-Token, das Sie beim Agenten hinterlegt haben.

Von der Plattform gesendete Anfrage
POST /clinics/zurich/appointments HTTP/1.1
Host: api.example.ch
Authorization: Bearer sk_…
token: 5f1c…            # das Tool-Token des Agenten
Content-Type: application/json

{ "start": "2026-10-06 10:30", "name": "Anna Keller", "phone": "41791234567" }
Node.js (Express)
app.post('/clinics/:clinic/appointments', express.json(), async (req, res) => {
  // 1. nur Ihr Agent darf aufrufen
  if (req.get('token') !== process.env.SWISSAI_TOOL_TOKEN) return res.status(401).end();

  const { start, name, phone } = req.body;

  // 2. fachlicher Fehler: ein Satz, mit dem der Agent weiterarbeiten kann
  if (!(await isFree(req.params.clinic, start)))
    return res.json({ success: false, error: "Diese Uhrzeit ist belegt. Biete 11:00 oder 14:30 am selben Tag an." });

  // 3. Erfolg: Die Nachricht ist das, was der Agent dem Anrufer sagt
  await book(req.params.clinic, start, name, phone);
  res.json({ success: true, message: "Termin gebucht für " + start + '.' });
});
PHP
<?php
// appointments.php
header('Content-Type: application/json');
if (($_SERVER['HTTP_TOKEN'] ?? '') !== getenv('SWISSAI_TOOL_TOKEN')) { http_response_code(401); exit; }

$in = json_decode(file_get_contents('php://input'), true);

if (!is_free($in['start'])) {
    echo json_encode(['success' => false, 'error' => "Diese Uhrzeit ist belegt. Biete 11:00 oder 14:30 am selben Tag an."]);
    exit;
}
book($in['start'], $in['name'], $in['phone']);
echo json_encode(['success' => true, 'message' => "Termin gebucht für " . $in['start'] . '.']);

Der Bereich Tools

Tools stehen in einer einzigen Liste, dem Bereich Tools des Dashboards, und Agenten verwenden sie per Verweis: Sie ändern ein Tool einmal, und jeder Agent, der es verwendet, wird aktualisiert. Ein Tool, das einem Kunden zugewiesen ist, kann nur in den Agenten dieses Kunden verwendet werden; ein Tool ohne Kunden in allen Ihren Agenten. Im Agenten-Editor wählen Sie die Tools aus der Liste oder erstellen ein neues, das der Liste hinzugefügt wird.

Sandbox: fertige Tools und Beispielcode

Die Sandbox ist ein laufender Satz von Beispiel-Endpunkten mit Beispieldaten, getrennt für jedes Konto: Kalender, Restauranttische, Lager, Rückrufe, Kontakte, Bestellungen. Ihre 22 Tools stehen bereits im Bereich Tools: Fügen Sie sie einem Agenten hinzu und testen Sie ihn in wenigen Minuten, ohne etwas zu hosten. Zu jedem Tool gibt es eine Anleitung mit den Parametern, einer durchgespielten Anfrage und Antwort und dem Code des Endpunkts, den Sie herunterladen und als Ausgangspunkt für Ihren eigenen verwenden können.

Gesamten Code herunterladen (zip)

Jedes Konto hat seine eigene Testumgebung mit denselben Ausgangsdaten: drei buchbare Leistungen mit einem bereits vergebenen Termin, acht Restauranttische mit einer Reservierung, sechs Produkte, zwei Kontakte und vier Bestellungen. Sie entsteht automatisch, sobald Sie einem Agenten zum ersten Mal ein Sandbox-Tool hinzufügen, oder im Bereich Tools, wo Sie sie auch auf die Ausgangsdaten zurücksetzen können. Eine 30 Tage lang nicht genutzte Umgebung wird gelöscht und kann neu erstellt werden.

Es genügt, einem Agenten ein Sandbox-Tool hinzuzufügen: Das Dashboard ergänzt die Adresse um den Schlüssel Ihrer Umgebung. Um die Endpunkte selbst aufzurufen, senden Sie den Schlüssel im Header Authorization: Bearer ws_… (im Dashboard zeigt die Anleitung jedes Tools den Befehl mit Ihrem Schlüssel). Die Aufrufe sind GET, mit den Argumenten im Query-String, oder POST, mit den Argumenten als JSON.

Jede Antwort hat den Status 200 und einen JSON-Body: success true mit einer Nachricht für den Anrufer und den Daten, oder success false mit einem Fehler, der als Satz für den Agenten formuliert ist, zum Beispiel welche Zeiten stattdessen frei sind. Tage sind yyyy-MM-dd, Uhrzeiten HH:mm, Telefonnummern nur Ziffern. Unten folgt jeder Bereich mit seiner Beschreibung, seinen Einsatzgebieten und allen Aufrufen, jeweils mit einer Beispielanfrage und der echten Antwort.

Kunden

Ein Kunde ist einer Ihrer Endkunden. Weisen Sie ihm Agenten im Agenten-Editor zu; er erhält eine Einladung per E-Mail und kann sich anmelden (Passwort, Google oder Apple), um seine Agenten, Anrufe und den Monatsverbrauch zu dem von Ihnen festgelegten Preis zu sehen. Der Kunde zahlt Ihnen: SwissAI zeigt nur die Zahlen.

Einladen, bearbeiten, entfernen

Beim Anlegen eines Kunden im Dashboard wird die Einladung gesendet. Beim Entfernen gehen die Agenten an Sie zurück und sein Zugang wird widerrufen.

Einen Agenten zuweisen

Wählen Sie im Agenten-Editor den Kunden, der die Anrufe sieht; Eigengebrauch behält den Agenten nur für Sie.

Telefonie

Ein Agent nimmt die Anrufe an, die seine SIP-URI auf unserer Vermittlung erreichen. Es gibt drei Wege, einen Anruf dorthin zu bringen, alle im Menü Telefonie des Dashboards einzurichten: eine per IP-Adresse freigeschaltete externe Telefonanlage, eine Rufnummer und einen WebRTC-Client auf einer Website. Aus dem Dashboard können Sie jeden Agenten auch aus dem Browser anrufen, um ihn zu testen; Testanrufe zählen wie alle anderen als Minuten.

SIP-URI und externe Telefonanlagen

Jeder Agent hat eine SIP-URI, sichtbar in der Agentenliste, etwa sip:ag_7f3a2b91@pbx.swissai.dev. Ihre Telefonanlage sendet den Anruf an diese Adresse: Der Teil vor dem @ wählt den Agenten.

Anrufe werden nur von Telefonanlagen angenommen, die Sie angegeben haben. Unter Telefonie › Externe Telefonanlagen tragen Sie die Anlage mit einem Namen, dem zugehörigen Kunden (oder Eigengebrauch) und der IP-Adresse ein, von der ihre Anrufe kommen. Es gibt weder Benutzernamen noch Passwort: Die Anlage wird an ihrer IP-Adresse erkannt und kann alle Agenten dieses Kunden anrufen.

Asterisk (chan_sip)
; extensions.conf: Nebenstelle 200 ruft den Agenten an
exten => 200,1,Dial(SIP/ag_7f3a2b91@pbx.swissai.dev,60)
 same => n,Hangup()
Asterisk (PJSIP)
; pjsip.conf: die SwissAI-Anlage als ausgehender Endpunkt, ohne Authentifizierung
[swissai]
type=endpoint
context=from-swissai
disallow=all
allow=alaw,ulaw
aors=swissai

[swissai]
type=aor
contact=sip:pbx.swissai.dev:5060

; extensions.conf: Nebenstelle 200 ruft den Agenten an
exten => 200,1,Dial(PJSIP/ag_7f3a2b91@swissai,60)
 same => n,Hangup()
FreeSWITCH
<!-- Dialplan: Nebenstelle 200 ruft den Agenten an -->
<extension name="swissai_agent">
  <condition field="destination_number" expression="^200$">
    <action application="bridge" data="sofia/external/ag_7f3a2b91@pbx.swissai.dev"/>
  </condition>
</extension>
Kamailio / OpenSIPS
# kamailio.cfg / opensips.cfg, in der Request-Route: Anrufe an 200 gehen an den Agenten
if ($rU == "200") {
    $ru = "sip:ag_7f3a2b91@pbx.swissai.dev:5060";
    t_relay();
    exit;
}

Anrufe von einer nicht angegebenen Adresse werden abgewiesen, und eine Adresse, die es weiter versucht, wird für eine Weile gesperrt. Wenn Ihre Telefonanlage die IP-Adresse wechselt, aktualisieren Sie sie im Dashboard, bevor Sie Anrufe senden.

Rufnummern

Eine Rufnummer bringt gewöhnliche Telefonanrufe zu einem Agenten. Unter Telefonie › Rufnummern zeigt jede Nummer das SIP-Konto, mit dem sie registriert wird (Benutzername, Passwort, Host, Port), und den Agenten, der antwortet.

WebRTC-Clients (Click-to-Call)

Ein WebRTC-Client ist eine Anruftaste auf einer Website: Der Besucher spricht aus dem Browser mit dem Agenten, ohne Telefon. Legen Sie ihn unter Telefonie › WebRTC-Clients an: Wählen Sie den Agenten und listen Sie die Domains auf, auf denen die Taste verwendet wird (localhost hinzufügen, um auf Ihrem Rechner zu testen). Sie erhalten den Namen des Clients (wc_…) und einen geheimen Schlüssel (wk_…), der nur einmal angezeigt wird.

Der geheime Schlüssel darf nie in den Browser gelangen. Die Seite fragt Ihr Backend, Ihr Backend fordert bei uns mit dem Schlüssel ein temporäres Passwort an und gibt es an die Seite weiter. Das temporäre Passwort gilt 60 Sekunden und für einen einzigen Anruf.

1Ihr Backend fordert das temporäre Passwort an
curl -X POST https://developers.swissai.dev/api/webrtc/token \
  -H "X-Api-Key: wk_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{"client": "wc_3f9a1c2b7d4e", "identity": "visitor-1842"}'
// 200
{
  "wss": "wss://pbx.swissai.dev:8089",
  "domain": "pbx.swissai.dev",
  "user": "w4c1f09ab37de5521",
  "pass": "9b2e61f0c4a87d35e1f2a6b90c7d4e88",
  "target": "sip:ag_7f3a2b91@pbx.swissai.dev",
  "expires_in": 60
}
FeldBeschreibung
clientPflicht. Der im Dashboard angezeigte Name des Clients.
identityOptional. Ihre Referenz für den Besucher, zum Beispiel eine Benutzer-ID.
401Unbekannter Client oder falscher Schlüssel.
503Browser-Anrufe sind für diesen Client nicht verfügbar, zum Beispiel weil sein Agent gelöscht wurde.
Node.js (Express)
// Ihr Backend: Der geheime Schlüssel bleibt hier
app.post('/call-token', async (req, res) => {
  const r = await fetch('https://developers.swissai.dev/api/webrtc/token', {
    method: 'POST',
    headers: { 'X-Api-Key': process.env.SWISSAI_CLIENT_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ client: 'wc_3f9a1c2b7d4e' })
  });
  res.status(r.status).json(await r.json());
});
PHP
<?php
// call-token.php: Der geheime Schlüssel bleibt auf Ihrem Backend
$ch = curl_init('https://developers.swissai.dev/api/webrtc/token');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['X-Api-Key: ' . getenv('SWISSAI_CLIENT_KEY'), 'Content-Type: application/json'],
    CURLOPT_POSTFIELDS => json_encode(['client' => 'wc_3f9a1c2b7d4e']),
]);
$body = curl_exec($ch);
http_response_code(curl_getinfo($ch, CURLINFO_HTTP_CODE));
header('Content-Type: application/json');
echo $body;
2Die Seite startet den Anruf

Laden Sie unser Skript und übergeben Sie ihm die Antwort unverändert. onState erhält connecting, calling, connected und idle; onError erhält den Grund, wenn der Anruf nicht starten kann.

<button id="call">Rufen Sie uns an</button>
<button id="hangup" hidden>Auflegen</button>
<p id="status"></p>

<script src="https://developers.swissai.dev/js/call.js"></script>
<script>
const call = document.getElementById('call'), hangup = document.getElementById('hangup'), status = document.getElementById('status');

call.onclick = async () => {
  const params = await (await fetch('/call-token', { method: 'POST' })).json();   // Ihr Backend, Schritt 1
  SwissCall.start(params, {
    onState: s => { status.textContent = s; call.hidden = s !== 'idle'; hangup.hidden = s === 'idle'; },
    onError: m => { status.textContent = m; }
  });
};
hangup.onclick = () => SwissCall.hangup();
</script>

GetCalls: Anrufe und was sie Ihren Kunden kosten

Mit dem Namen und dem geheimen Schlüssel eines WebRTC-Clients kann Ihr Backend die Anrufe des Kunden lesen, zu dem dieser Client gehört (alle Agenten dieses Kunden), und was jeder Anruf den Kunden nach der unter Kunden festgelegten Preisliste kostet.

curl "https://developers.swissai.dev/api/calls?client=wc_3f9a1c2b7d4e&month=2026-10" \
  -H "X-Api-Key: wk_your_secret_key"
// 200
{
  "month": "2026-10",
  "customer": { "customer_id": 7, "name": "Dental Practice Keller" },
  "currency": "CHF",
  "rate": 0.25,
  "free_minutes": 100,
  "calls": [
    { "call_ref": "c-91f3a2", "agent_id": "ag_7f3a2b91", "agent_name": "Anna", "source": "webrtc",
      "from": "", "started": "2026-10-06 10:41:03", "seconds": 84, "outcome": "appointment",
      "tool_calls": 2, "cost": 0.35 }
  ],
  "total": { "minutes": 312.4, "calls": 148, "billable_minutes": 213, "amount": 53.25 }
}
FeldBeschreibung
clientPflicht. Der im Dashboard angezeigte Name des Clients.
monthOptional. yyyy-MM; ohne Angabe der laufende Monat. Bis zu 500 Anrufe, neueste zuerst.
costDie Dauer des Anrufs zum Minutenpreis des Kunden, vor den Inklusivminuten.
totalDer Monat, wie ihn der Kunde in seinem Portal sieht: Inklusivminuten abgezogen, Sekunden auf die Minute aufgerundet, bezogen auf die Monatssumme.
customernull, wenn der Agent des Clients für den Eigengebrauch ist: Es sind die Anrufe Ihrer Agenten ohne Kunden, zum Preis der Plattform.

Beide Aufrufe können Sie, mit dem Code zum Kopieren, im Testbereich des Dashboards ausprobieren.

Chat: Textnachrichten an den Agenten

Derselbe Agent, der ans Telefon geht, kann auch schriftlich antworten: auf Ihrer Website, in Ihrer App oder in einem Messaging-Kanal, den Sie betreiben. Ihr Backend sendet, was der Nutzer geschrieben hat, und erhält die Antwort als Text; beim Antworten kann der Agent seine Tools aufrufen wie am Telefon.

Der Aufruf erfolgt mit dem Namen und dem geheimen Schlüssel eines WebRTC-Clients, die den Agenten bestimmen, und muss von Ihrem Backend kommen: Der Schlüssel darf nie in den Browser gelangen. Er speichert keinen Zustand: Senden Sie jedes Mal die letzten Nachrichten in history mit.

curl -X POST https://developers.swissai.dev/api/messages \
  -H "X-Api-Key: wk_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "client": "wc_3f9a1c2b7d4e",
    "user": "visitor-1842",
    "name": "Laura Meier",
    "message": "Ist Donnerstag um 10 noch frei?",
    "history": [
      { "direction": "in",  "text": "Guten Tag, ich möchte einen Kontrolltermin buchen." },
      { "direction": "out", "text": "Gern. Welcher Tag würde Ihnen passen?" }
    ]
  }'
// 200
{ "reply": "Ja, Donnerstag um 10:00 ist frei. Soll ich den Termin für Sie buchen?" }
FeldBeschreibung
clientPflicht. Der im Dashboard angezeigte Name des Clients.
messagePflicht. Der Text des Nutzers, bis zu 4000 Zeichen.
userOptional. Ihre Referenz für den Schreibenden, zum Beispiel eine Nutzer-ID oder eine Telefonnummer.
nameOptional. Der Name des Schreibenden, falls bekannt.
historyOptional. Die letzten Nachrichten, älteste zuerst, bis zu 20: direction ist in für den Nutzer und out für den Agenten, text ist die Nachricht.
replyDie Antwort des Agenten für den Nutzer; leer, wenn der Agent nichts zu sagen hat.
401Unbekannter Client oder falscher Schlüssel.
402Die Testphase oder das Abo ist nicht aktiv, oder das Wallet hat kein Guthaben.
502Der Agent konnte nicht antworten: Versuchen Sie es erneut.

Unter Telefonie › Chat können Sie mit jedem Agenten chatten und diesen Aufruf samt Code zum Kopieren im Testbereich ausprobieren.

WhatsApp Business

Ein Agent kann auf zwei Arten in WhatsApp antworten. Entweder Sie verbinden die Nummer im Dashboard, und SwissAI empfängt die Nachrichten und antwortet, oder Sie behalten Ihre eigene Meta-App und Ihren Webhook auf Ihrer Website und senden jede Nachricht mit dem Chat-Aufruf oben an den Agenten.

A. Die Nummer im Dashboard verbinden

Telefonie › WhatsApp › Mit WhatsApp verbinden. Ein Meta-Fenster öffnet sich: Sie melden sich mit dem Facebook-Konto Ihres Unternehmens an, wählen das Unternehmensportfolio (oder erstellen eines) und die Nummer; die Verbindung wird automatisch abgeschlossen. Sie wählen den antwortenden Agenten und können ihn später in der Liste ändern.

Zwei Arten, die Nummer zu nutzen: nur für den Agenten (die Nummer wird nicht mehr in der Telefon-App genutzt) oder WhatsApp Business auf dem Telefon behalten, wobei der Agent antwortet und Sie die App weiter nutzen. Die zweite braucht die aktuelle WhatsApp-Business-App und eine Nummer, die dort seit mindestens einer Woche tatsächlich genutzt wird, sonst lehnt Meta sie ab.

Jede eingehende Nachricht geht zusammen mit den letzten 20 Nachrichten dieser Unterhaltung an den Agenten, und die Antwort wird in WhatsApp zurückgesendet; Sprachnachrichten werden transkribiert. Jeder Austausch erscheint in den Logs. Der Agent antwortet nur, solange die Testphase oder ein Abo aktiv ist und das Wallet Guthaben hat; Nachrichten verbrauchen kein Guthaben. Meta rechnet die Unterhaltungen zu eigenen Tarifen ab und verlangt eine Zahlungsmethode in Ihrem Unternehmenskonto, bevor der Agent antworten kann.

Trennen auf derselben Seite: Die Nummer wird aus der Cloud API gelöst, und der Gesprächsverlauf im Portal wird gelöscht.

B. Ihre eigene Meta-App und Ihr Webhook

Wenn Sie die WhatsApp Cloud API bereits mit einer eigenen Meta-App betreiben, behalten Sie sie. Ihr Webhook empfängt die Nachricht, sendet sie mit dem Chat-Aufruf an den Agenten (user ist die Nummer des Absenders, name der Profilname, history die letzten Nachrichten, die Sie selbst aufbewahren: der Aufruf hat keinen Zustand) und sendet die Antwort mit der Graph API. Der Agent ist der des WebRTC-Clients, den Sie angeben; sein geheimer Schlüssel bleibt auf Ihrem Server.

// Ihr Backend: Meta-Webhook → SwissAI-Agent → Graph API
app.post('/webhook', express.json(), async (req, res) => {
  res.sendStatus(200);  // Meta sofort antworten, dann arbeiten
  for (const entry of req.body.entry || [])
    for (const change of entry.changes || []) {
      const v = change.value, m = (v.messages || [])[0];
      if (!m || m.type !== 'text') continue;
      const name = ((v.contacts || [])[0]?.profile || {}).name || '';
      const r = await fetch('https://developers.swissai.dev/api/messages', {
        method: 'POST',
        headers: { 'X-Api-Key': process.env.SWISSAI_CLIENT_KEY, 'Content-Type': 'application/json' },
        body: JSON.stringify({
          client: 'wc_3f9a1c2b7d4e',
          user: '+' + m.from,
          name: name,
          message: m.text.body,
          history: lastExchanges(m.from)  // die letzten Nachrichten, die Sie gespeichert haben, älteste zuerst
        })
      }).then(r => r.json());
      if (!r.reply) continue;
      // die Antwort geht mit der Graph API zurück, von Ihrer Nummer
      await fetch('https://graph.facebook.com/v25.0/' + v.metadata.phone_number_id + '/messages', {
        method: 'POST',
        headers: { Authorization: 'Bearer ' + process.env.WA_TOKEN, 'Content-Type': 'application/json' },
        body: JSON.stringify({ messaging_product: 'whatsapp', to: m.from, type: 'text', text: { body: r.reply } })
      });
    }
});

Die Webhook-Verifizierung (GET mit hub.verify_token und hub.challenge), die Signaturprüfung jedes Ereignisses (X-Hub-Signature-256) und das Abonnieren des Feldes messages sind die der WhatsApp Cloud API: siehe Metas Dokumentation. Speichern Sie die letzten Nachrichten pro Absender, um sie in history zu senden: Der Agent weiß nur, was Sie ihm senden.

Beide Wege nutzen denselben Agenten mit seinem Scope und seinen Tools: Wählen Sie A, wenn Sie keinen Webhook betreiben wollen, B, wenn WhatsApp bereits Teil Ihrer Plattform ist.

Abrechnung

Jedes Konto startet mit einer kostenlosen Testphase von 30 Tagen mit 100 Minuten; eine Karte ist nicht nötig. Nach der Testphase wählen Sie in der Abrechnung einen Plan. Die Monatsgebühr wird als Planguthaben ins Wallet geladen, jeder Anruf wird sekundengenau zum Minutenpreis des Plans davon abgezogen. Das Planguthaben wird bei jeder Verlängerung zurückgesetzt. Ist es aufgebraucht, können Sie das Wallet aufladen: aufgeladenes Guthaben verfällt nicht und wird nach dem Planguthaben verwendet. Auf Anfrage vergebene Rufnummern werden monatlich vom Wallet abgebucht. Agenten antworten nur, solange die Testphase oder ein Plan läuft und das Wallet Guthaben hat.