API, Webhooks & MCP
Authentifizierung
API-Keys erstellst du unter Einstellungen → API (pro Workspace, jederzeit widerrufbar). Jeder Request trägt den Key als Bearer-Token:
Authorization: Bearer <API-KEY>Jeder Key trägt Rechte: read für alle GET-Endpunkte, write für alles Verändernde (POST, PATCH, DELETE) — sonst 403. Optional mit Ablaufdatum; jeder schreibende Zugriff wird protokolliert.
Rate-Limit je API-Key und Minute: 600 Lesezugriffe, 300 Schreibzugriffe. Darüber antwortet die API mit 429 und Retry-After (Sekunden). Für Massenanlagen gibt es POST /leads/bulk, für laufende Änderungen Webhooks — beides schont das Limit gegenüber vielen Einzelaufrufen.
REST-API (v1)
Basis-URL: https://app.showrate.app/api/v1. Alle Listen sind Cursor-paginiert (?limit=50&cursor=<letzte-id>, max. 200) und antworten mit { data, nextCursor }. Alles ist auf den Workspace des Keys begrenzt; unbekannte IDs geben 404, ungültige Eingaben 422 mit details je Feld.
Leads
| Endpoint | Beschreibung |
|---|---|
GET /leads | Filter: email, stageId (veraltet, siehe unten), ownerId, source, minScore sowie die Deal-Filter hasDeal (true/false), dealPipelineId, dealStageId, dealStatus (OPEN/WON/LOST). Jeder Lead trägt hasDeal, dealCount und primaryDeal — die volle Deal-Liste nur im Einzelabruf. |
POST /leads | Anlegen bzw. Wiedererkennen über E-Mail/Telefon (201 = neu, 200 = Bestandslead). Felder: name, email (Pflicht), phone, source, companyName, campaign, contactRole, ownerId, stageId (veraltet), tags[] (fehlende werden angelegt), notes (wird zur Aktivität), customFields, utm (Touchpoint) und notify: true für den Echtzeit-Push an den Vertrieb. |
POST /leads/bulk | Bis zu 200 Leads in einem Aufruf (Body: { leads: [...] } oder direkt ein Array). Antwortet immer mit 200 und { created, updated, failed, results[] } — eine fehlerhafte Zeile stoppt den Block nicht, sie erscheint mit index und error in results. |
GET /leads/:id | Komplettes Profil: Stage, Tags, Zusatzfelder, Formular-Antworten, UTM-Touches, Termine, Einwilligungen, Aufzeichnungen — dazu hasDeal, dealCount, primaryDeal und deals[] (alle Deals, jüngster zuerst). |
PATCH /leads/:id | name, email, phone, source, companyName, campaign, ownerId (null hebt auf und gibt den Lead wieder an die automatische Verteilung), followUpAt, stageId (veraltet — verschiebt den maßgeblichen Deal; ohne Deal 422), scoreManual 1–4 (null gibt die Regelrechnung wieder frei), tags, notes, customFields, utm. score selbst ist nicht schreibbar — das ist das Ergebnis des Regelwerks. |
DELETE /leads/:id | DSGVO-Löschung inkl. aller Lead-Daten. |
GET/POST/DELETE /leads/:id/tags | Schlagworte lesen, ergänzen, entfernen (Body: { tags: ["Kunde"] }). Groß-/Kleinschreibung egal, unbekannte Tags werden angelegt. |
E-Mail und Telefonnummer sind die Dubletten-Schlüssel. Gehört der neue Wert bereits zu einem anderen Lead, antwortet PATCH mit 409 statt die Dublettenerkennung stillschweigend zu zerlegen — beide Datensätze zuerst zusammenführen.
Deals am Lead
Jede Lead-Antwort sagt, ob der Lead schon in einem Vorgang steckt — damit ein make.com-Szenario darauf verzweigen kann, ohne zusätzlich /deals abzufragen. hasDeal ist true, sobald mindestens ein Deal am Lead hängt, dealCount nennt die Anzahl, und primaryDeal ist der jüngste offene Deal — gibt es keinen offenen, der jüngste überhaupt. Dieselbe Regel wie in der Oberfläche, sonst zeigte make.com eine andere Phase als das CRM daneben.
{
"hasDeal": true,
"dealCount": 2,
"primaryDeal": {
"id": "cme4k2x9c0001",
"title": "Coaching-Programm Q3",
"status": "OPEN",
"pipelineId": "cmd1pipeline01",
"pipelineName": "Vertrieb",
"stageId": "cmd1stage04",
"stageName": "Angebot",
"stageKey": "offer",
"stageIsWon": false,
"stageIsLost": false,
"valueCents": 250000,
"currency": "EUR",
"stageEnteredAt": "2026-08-03T09:12:00.000Z",
"expectedCloseAt": "2026-08-20T00:00:00.000Z",
"createdAt": "2026-07-28T14:05:00.000Z"
}
}dealPipelineId, dealStageId und dealStatus treffen denselben Deal: ?dealPipelineId=P&dealStatus=OPEN liefert keinen Lead, der einen verlorenen Deal in P und daneben irgendeinen offenen hat. hasDeal=false zusammen mit einem der drei ist ein Widerspruch und wird mit 422 abgelehnt, statt still eine leere Liste zu liefern.
Veraltet: stage und stageId am Lead. Der Vertriebsfortschritt ist vom Lead auf den Deal umgezogen. Beide Felder bleiben erhalten, damit bestehende Szenarien nicht brechen — sie liefern und filtern ab sofort aber die Phase des maßgeblichen Deals (dieselbe Auswahl wie primaryDeal).
stageIdin der Antwort →primaryDeal.stageId;stageim Einzelabruf →primaryDeal.stageName/primaryDeal.stageKey.- Filter
?stageId=→?dealStageId=.stageIdtrifft den maßgeblichen Deal,dealStageIdirgendeinen. - Ein Lead ohne Deal hat keine Phase:
stageundstageIdsind dannnull. Vorher stand dort die Lead-Phase. stageIdinPATCH /leads/:idverschiebt den maßgeblichen Deal und feuertdeal.stage_changed; bei einer Gewinn- oder Verlustphase zusätzlichdeal.won/deal.lost. Ohne Deal antwortet die API mit 422 — zuerstPOST /deals.POST /leadsmitstageIdbricht nicht: die Phase wird weiter gesetzt, taucht ohne Deal in der Antwort aber nicht auf.
Kontakte, Deals, Aufgaben, Aktivitäten
| Endpoint | Beschreibung |
|---|---|
GET/POST /contacts, GET/PATCH/DELETE /contacts/:id | Kontaktpersonen am Lead (Close-Modell: Lead = Firma, Kontakt = Person). Felder: leadId, name, email, phone, role, isPrimary, customFields. Es gibt genau eine Hauptkontaktperson je Lead. Filter: leadId, email, phone, isPrimary. |
GET/POST /deals, GET/PATCH/DELETE /deals/:id | Abschlüsse: leadId, title, pipelineId, stageId (ohne Angabe die erste Phase), product, valueCents, currency, probability, expectedCloseAt, ownerId, collectedCents, customFields. Ein stageId im PATCH ist ein Phasenwechsel und feuert deal.stage_changed, beim Abschluss zusätzlich deal.won / deal.lost; Verlustphasen verlangen lostReason. Filter: leadId, pipelineId, stageId, ownerId, status (OPEN/WON/LOST). |
GET/POST /tasks, GET/PATCH/DELETE /tasks/:id | Aufgaben: title, leadId, dealId, notes, type (CALL, EMAIL, FOLLOW_UP, CHECK, OTHER), priority (LOW, NORMAL, HIGH), dueAt, remindMinutesBefore, assigneeId. completed: true erledigt die Aufgabe (feuert task.completed), false öffnet sie wieder. Filter: leadId, dealId, assigneeId, completed, dueBefore, dueAfter. |
GET/POST /activities | Touchpoint-Historie eines Leads — leadId ist beim GET Pflicht, type filtert. POST schreibt Notizen und Aktivitäten: type (NOTE, CALL, STAGE_CHANGE, SYSTEM, TASK, DEAL, ASSIGNMENT, FIELD_CHANGE), content, callOutcome (REACHED, NOT_REACHED, BOOKED, DQ, FOLLOW_UP), userId, metadata. |
GET/POST /tags | Schlagworte des Workspace. POST ist idempotent über den Namen und hängt das Schlagwort mit leadId gleich an einen Lead; color als Hex-Wert. |
Stammdaten & Konfiguration
| Endpoint | Beschreibung |
|---|---|
GET /pipelines, GET /pipelines/:id/stages | Pipelines mit ihren Phasen (id, name, key, position, probability, isWon, isLost) — das Futter für abhängige Auswahllisten in make.com. |
GET/POST /custom-fields, GET/PATCH/DELETE /custom-fields/:id | Benutzerdefinierte Felder für LEAD, CONTACT, DEAL und BOOKING. Typen: TEXT, TEXTAREA, NUMBER, DATE, BOOLEAN, SELECT, MULTISELECT, URL, EMAIL, PHONE (SELECT/ MULTISELECT brauchen options). entity und key sind unveränderlich — unter dem Schlüssel liegen die erfassten Werte. DELETE archiviert die Definition, die Werte bleiben erhalten; ?includeArchived=true zeigt sie wieder an. |
GET /calendars, GET /calendars/:id/slots | Kalender inkl. Buchungs-URLs; freie Slots des rollenden Fensters. |
GET /bookings, GET /bookings/:id | Termine mit Status, Lead und Kalender; dazu POST /bookings/:id/confirm, …/cancel, …/show. |
GET /events | Domain-Event-Log (alles, was im Workspace passiert) — die Polling-Alternative zu Webhooks. |
GET /event-types | Alle abonnierbaren Event-Typen mit deutschem Label und Gruppe (siehe unten). |
GET /recordings, POST /recordings | Anruf-Aufzeichnungen mit Transkripten. |
POST /messages | Eingehende SMS/WhatsApp zurückmelden — ordnet dem Lead zu und beendet laufende Sequenzen. |
GET/POST /webhooks, GET/PATCH/DELETE /webhooks/:id | Webhook-Endpunkte verwalten (siehe unten). |
Beispiel
curl -X POST https://app.showrate.app/api/v1/leads \
-H "Authorization: Bearer <API-KEY>" \
-H "Content-Type: application/json" \
-d '{
"name": "Max Mustermann",
"email": "max@example.com",
"phone": "+4915112345678",
"companyName": "Muster GmbH",
"contactRole": "Geschäftsführer",
"campaign": "meta-q3",
"tags": ["Warm", "Webinar"],
"notes": "Kam über das Webinar, will im September starten.",
"customFields": { "budget": 5000 },
"utm": { "source": "meta", "medium": "paid", "campaign": "q3" },
"notify": true
}'Webhooks (make.com & Co.)
- Pro Webhook: Ziel-URL, HMAC-Secret (nur beim Anlegen einmalig in der Antwort) und abonnierte Event-Typen.
GET,PATCH(URL, Abo,active) undDELETEje Webhook. - Zustellung als
POSTmit{ type, payload, occurredAt }, den HeadernX-ShowRate-EventundX-ShowRate-Signature: t=<unix>,v1=<hex>(v1 = HMAC_SHA256(secret, "<t>.<body>")). Retry bei Nicht-2xx: 30 s → 60 s → 2 min → 4 min → 8 min. - Payloads sind angereichert: statt bloßer IDs enthalten sie aufgelöste Objekte —
lead(inkl.tags,owner,stage, Zusatzfeldern),booking,deal,task(mitassignee),contact,calendar,host— plus ein deutscheslabel. Direkt in make.com verwendbar, ohne Nachladen.
Abo-Ausdruck
events | Bedeutung |
|---|---|
["booking.created", "call.showed"] | nur diese Typen |
["*"] | alles, auch künftige Event-Typen |
["*", "!funnel.started"] | alles außer den ausgeschlossenen (!-Präfix); Ausschluss schlägt Einschluss |
Tippfehler werden abgelehnt: Abos werden gegen die echte Typ-Registry geprüft. Empfehlung für Integrationen: mit * plus Ausschlussliste registrieren und im Szenario nach type verzweigen — dann muss nichts angefasst werden, wenn neue Events dazukommen.
Event-Typen
GET /event-types liefert alle 37 abonnierbaren Typen mit Label und Gruppe (die Auswahllisten in make.com bauen sich daraus):
| Gruppe | Typen |
|---|---|
| Workspace | tenant.created, member.joined |
| Funnel | funnel.started, funnel.step_completed, funnel.optin, funnel.survey_completed, funnel.abandoned (mit resumeUrl), funnel.resumed |
| Leads | lead.created, lead.updated, lead.scored, lead.assigned |
| Deals | deal.created, deal.stage_changed, deal.won, deal.lost |
| Aufgaben | task.created, task.completed, task.reminder_due, task.overdue |
| Kommunikation | email.reply_received, message.received (Antwort per SMS/WhatsApp, mit optOut) |
| Termine | booking.created, booking.confirmed, booking.cancelled (mit Absagegrund), booking.rescheduled |
| Show-Erkennung | call.showed, call.no_show, show.confirmation_requested |
| Terminsteuerung | policy.confirm_requested (mit confirmUrl), policy.slot_released (mit rebookUrl), routing.assigned |
| Pipeline | pipeline.stage_changed |
| Kalender-Monitoring | slots.shortage_detected, slots.imbalance_detected |
| Erinnerungen | reminder.due (fertiger SMS-Text), sms.first_contact_due (Erst-SMS mit Vertriebler-Absendernummer) |
MCP-Server (Claude & andere LLMs)
ShowRate stellt einen Model-Context-Protocol-Server bereit: https://app.showrate.app/api/mcp/mcp — Authentifizierung über den API-Key des Workspace als Bearer-Token. Eintragen mit claude mcp add --scope local showrate --transport http … im jeweiligen Projektordner (nicht global, und nicht als .mcp.json im Repo — der Key stünde sonst im Klartext in der Versionsverwaltung).
- Erster Schritt ist immer die Workspace-Auswahl:
list_workspaceszeigt den Workspace des Keys,select_workspacebestätigt ihn. Bis dahin sind alle anderen Werkzeuge gesperrt — so schreibt kein Modell versehentlich im falschen Mandanten. Die Auswahl gilt 12 Stunden. - Ein API-Key gehört zu genau einem Workspace. Wer mehrere Mandanten betreut, legt je Workspace einen Key an und bindet ihn im jeweiligen Projektordner ein (
--scope local) statt global. - Tools:
list_leads,get_lead(inkl. Antworten, UTM, Terminen),list_transcripts(Call-Transkripte),pipeline_stats. - Beispiel-Anwendung: „Lies die letzten 50 Call-Transkripte und ziehe daraus 10 Ad-Hooks.“ — der Zugriff ist strikt auf den eigenen Workspace begrenzt.
- Formulare bauen (Keys mit Schreibrecht):
form_building_guide(Bauanleitung + alle Auswahlmöglichkeiten),list_calendars,list_forms,get_form,create_form,update_form_steps,update_form_settings,set_form_design,duplicate_form. Claude Code fragt damit im Terminal Schritt für Schritt ab („Was soll Step 1 sein?“), legt das mehrstufige Formular inklusive Terminkalender an, überträgt das Design der gerade gebauten Seite und liefert den Einbett-Code (embedHtml) zurück. Kalender anlegen und ändern:list_hosts,get_calendar,create_calendar,update_calendar,set_calendar_hosts(Hosts mit Wochenverfügbarkeit in Alltagsform, z. B. „Mo–Fr 09:00–17:00“),set_calendar_questions— geführt über den Promptkalender_anlegen. Fertiger Einstieg: der Promptformular_bauen. - Sicherheit: Rechte pro Key (nur Lesen / Lesen + Schreiben), optionales Ablaufdatum, Widerruf jederzeit, getrennte Rate-Limits für Lesen und Schreiben, vollständiges Zugriffsprotokoll je Aufruf. Gelöscht wird über MCP nichts; Texte werden nie als HTML übernommen, URLs nur als http(s).