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

EndpointBeschreibung
GET /leadsFilter: 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 /leadsAnlegen 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/bulkBis 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/:idKomplettes Profil: Stage, Tags, Zusatzfelder, Formular-Antworten, UTM-Touches, Termine, Einwilligungen, Aufzeichnungen — dazu hasDeal, dealCount, primaryDeal und deals[] (alle Deals, jüngster zuerst).
PATCH /leads/:idname, 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/:idDSGVO-Löschung inkl. aller Lead-Daten.
GET/POST/DELETE /leads/:id/tagsSchlagworte 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).

  • stageId in der Antwort → primaryDeal.stageId; stage im Einzelabruf → primaryDeal.stageName / primaryDeal.stageKey.
  • Filter ?stageId=?dealStageId=. stageId trifft den maßgeblichen Deal, dealStageId irgendeinen.
  • Ein Lead ohne Deal hat keine Phase: stage und stageId sind dann null. Vorher stand dort die Lead-Phase.
  • stageId in PATCH /leads/:id verschiebt den maßgeblichen Deal und feuert deal.stage_changed; bei einer Gewinn- oder Verlustphase zusätzlich deal.won/deal.lost. Ohne Deal antwortet die API mit 422 — zuerst POST /deals.
  • POST /leads mit stageId bricht nicht: die Phase wird weiter gesetzt, taucht ohne Deal in der Antwort aber nicht auf.

Kontakte, Deals, Aufgaben, Aktivitäten

EndpointBeschreibung
GET/POST /contacts, GET/PATCH/DELETE /contacts/:idKontaktpersonen 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/:idAbschlü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/:idAufgaben: 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 /activitiesTouchpoint-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 /tagsSchlagworte 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

EndpointBeschreibung
GET /pipelines, GET /pipelines/:id/stagesPipelines 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/:idBenutzerdefinierte 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/slotsKalender inkl. Buchungs-URLs; freie Slots des rollenden Fensters.
GET /bookings, GET /bookings/:idTermine mit Status, Lead und Kalender; dazu POST /bookings/:id/confirm, …/cancel, …/show.
GET /eventsDomain-Event-Log (alles, was im Workspace passiert) — die Polling-Alternative zu Webhooks.
GET /event-typesAlle abonnierbaren Event-Typen mit deutschem Label und Gruppe (siehe unten).
GET /recordings, POST /recordingsAnruf-Aufzeichnungen mit Transkripten.
POST /messagesEingehende SMS/WhatsApp zurückmelden — ordnet dem Lead zu und beendet laufende Sequenzen.
GET/POST /webhooks, GET/PATCH/DELETE /webhooks/:idWebhook-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) und DELETE je Webhook.
  • Zustellung als POST mit { type, payload, occurredAt }, den Headern X-ShowRate-Event und X-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 (mit assignee), contact, calendar, host — plus ein deutsches label. Direkt in make.com verwendbar, ohne Nachladen.

Abo-Ausdruck

eventsBedeutung
["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):

GruppeTypen
Workspacetenant.created, member.joined
Funnelfunnel.started, funnel.step_completed, funnel.optin, funnel.survey_completed, funnel.abandoned (mit resumeUrl), funnel.resumed
Leadslead.created, lead.updated, lead.scored, lead.assigned
Dealsdeal.created, deal.stage_changed, deal.won, deal.lost
Aufgabentask.created, task.completed, task.reminder_due, task.overdue
Kommunikationemail.reply_received, message.received (Antwort per SMS/WhatsApp, mit optOut)
Terminebooking.created, booking.confirmed, booking.cancelled (mit Absagegrund), booking.rescheduled
Show-Erkennungcall.showed, call.no_show, show.confirmation_requested
Terminsteuerungpolicy.confirm_requested (mit confirmUrl), policy.slot_released (mit rebookUrl), routing.assigned
Pipelinepipeline.stage_changed
Kalender-Monitoringslots.shortage_detected, slots.imbalance_detected
Erinnerungenreminder.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_workspaces zeigt den Workspace des Keys, select_workspace bestä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 Prompt kalender_anlegen. Fertiger Einstieg: der Prompt formular_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).