RevenueLab API v1LIVE
Die RevenueLab REST API gibt dir vollen programmatischen Zugriff auf Leads, Kunden, Projekte und Events. Baue Integrationen mit n8n, Make, Zapier oder eigenen Systemen.
Authorization: Bearer rl_live_... → GET https://dashboard.revenue-lab.de/api?__path=projects aufrufen → fertig.
Die Plesk-Nginx-Konfiguration leitet
/v1/... noch nicht an PHP weiter. Bis das aktiviert ist, bitte den funktionierenden Query-Stil nutzen:
✓ Empfohlen:
https://dashboard.revenue-lab.de/api?__path=<resource>⏳ Geplant:
https://api.revenue-lab.de/v1/<resource> (sobald Plesk angepasst)
Beide Routen landen am gleichen Endpunkt — nur die URL-Form unterscheidet sich.
Was kann die API?
- Leads erstellen, auflisten, filtern, abrufen, aktualisieren
- Notizen an Leads anheften und Verlauf auslesen
- Lead → Kunde konvertieren (idempotent)
- Kunden auflisten und abrufen
- Projekte auflisten (fuer
project_idbei Lead-Erstellung) - Event-Log aller API-Calls und Webhook-Zustellungen abfragen
- Outbound Webhooks — bei Events automatisch Daten an Make / n8n / Zapier pushen
REST API (Bearer-Token) = Daten in RevenueLab schreiben.
Webhooks (Automationen) = Events aus RevenueLab heraus an deine Systeme senden.
Authentifizierung
Jeder Request braucht einen Bearer-Token im Authorization-Header:
Authorization: Bearer rl_live_abc123def456ghi789jkl012mno345pqrs6789
API-Key Regeln
| Eigenschaft | Wert |
|---|---|
| Format | rl_live_ + 40 Hex-Zeichen = 48 Zeichen total |
| Verwaltung | Settings → API-Zugang |
| Sichtbarkeit | Nur einmalig nach Erstellung im Klartext sichtbar |
| Limit | Max. 10 aktive Keys pro Account |
| Deaktivierung | Jederzeit deaktivierbar oder loeschbar |
Base URL & Format
https://api.revenue-lab.de/v1
Endpunkte werden als REST-Pfade angesprochen:
GET /v1/leads— Leads auflistenPOST /v1/leads— Lead erstellenGET /v1/leads/42— Einzelner LeadPATCH /v1/leads/42— Lead aktualisierenPOST /v1/leads/42/notes— Notiz hinzufuegenPOST /v1/leads/42/convert— Lead zum Kunden machen
Alle Anfragen und Antworten verwenden JSON. Bei POST/PATCH den Header Content-Type: application/json setzen.
Erfolgreiche Antwort (Einzelobjekt)
{
"success": true,
"data": {
"id": 42,
"name": "Max Mustermann"
}
}
Erfolgreiche Antwort (Liste mit Pagination)
{
"success": true,
"data": [/* Array von Objekten */],
"total": 84,
"limit": 50,
"offset": 0
}
HTTP Status Codes
Fehlerbehandlung
Fehlerantworten haben immer "success": false und ein error-Objekt mit maschinenlesbarem Code:
{
"success": false,
"error": {
"code": "missing_field",
"message": "project_id ist erforderlich."
}
}
Fehlercodes
Endpoint-Uebersicht
Alle verfuegbaren Endpunkte auf einen Blick. Base URL: https://api.revenue-lab.de/v1
GET /v1/leads — Leads auflisten (Filter: project_id, status, search)
GET /v1/leads/{id} — Einzelnen Lead abrufen
POST /v1/leads — Neuen Lead erstellen
PATCH /v1/leads/{id} — Lead-Felder aktualisieren
GET /v1/leads/{id}/notes — Notizen-Verlauf abrufen
POST /v1/leads/{id}/notes — Notiz hinzufuegen
POST /v1/leads/{id}/convert — Lead zum Kunden konvertieren
GET /v1/leads/{id}/files — Dateien zu einem Lead auflisten
POST /v1/leads/{id}/files — Datei hochladen (multipart/Base64; optional
as_contract=1 für Verträge-Tab)GET /v1/leads/{id}/files/{fid}/download — Datei downloaden
GET /v1/customers — Kunden auflisten (Filter: search)
GET /v1/customers/{id} — Einzelnen Kunden abrufen
PATCH /v1/customers/{id} — Kunde aktualisieren (Standardfelder +
custom)GET /v1/customers/{id}/notes — Kunden-Feed / Notizen abrufen
POST /v1/customers/{id}/notes — Notiz hinzufuegen (auch bei Kunden ohne lead_id)
GET /v1/todos — ToDos auflisten (Filter: column_id, assigned_user_id, completed)
GET /v1/todos/{id} — Einzelne ToDo abrufen
POST /v1/todos — ToDo anlegen (feuert
todo.created)PATCH /v1/todos/{id} — ToDo aktualisieren (feuert
todo.updated / todo.completed)DEL /v1/todos/{id} — ToDo loeschen
GET /v1/events — Event-Log (Filter: category=api|webhook)
application/json und geben JSON zurueck. Authentifizierung immer via Authorization: Bearer rl_live_.... Pagination ueber limit (max 200) + offset. Fehler liefern error.code (maschinenlesbar) + error.message (deutsch, menschenlesbar).
Projekte auflisten
Gibt alle Projekte zurueck. Die id wird als project_id beim Erstellen von Leads benoetigt.
Antwort-Felder
| Feld | Typ | Beschreibung |
|---|---|---|
| id | integer | Eindeutige Projekt-ID |
| name | string | Projektname |
| description | string|null | Projektbeschreibung |
| status | string | active oder inactive |
| lead_value | number|null | Standard-Lead-Wert in Euro |
| created_at | datetime | Erstellungszeitpunkt |
{
"success": true,
"data": [
{
"id": 1,
"name": "Outbound B2B DACH",
"description": "Kaltakquise Kampagne Q3 2026",
"status": "active",
"lead_value": 250.00,
"created_at": "2026-04-01 09:00:00"
},
{
"id": 2,
"name": "Inbound Website-Leads",
"description": "Kontaktformular und Landingpages",
"status": "active",
"lead_value": 180.00,
"created_at": "2026-05-15 10:30:00"
}
],
"total": 2
}
curl https://api.revenue-lab.de/v1/projects \
-H "Authorization: Bearer rl_live_DEIN_API_KEY"
import requests API_KEY = "rl_live_DEIN_API_KEY" BASE = "https://api.revenue-lab.de/v1" headers = {"Authorization": f"Bearer {API_KEY}"} res = requests.get(f"{BASE}/projects", headers=headers) projects = res.json()["data"] for p in projects: print(f"{p['id']}: {p['name']}")
Leads auflisten
Paginierte Liste von Leads. Unterstuetzt Filter nach Projekt, Status und Freitext-Suche.
Query-Parameter
| Parameter | Typ | Beschreibung | |
|---|---|---|---|
| project_id | integer | Optional | Nur Leads aus diesem Projekt |
| status | string | Optional | Exakter Status-Match, z.B. Neu oder Terminiert |
| search | string | Optional | Freitextsuche in Name, Firma, Telefon, E-Mail |
| limit | integer | Optional | Ergebnisse pro Seite (1–200, Standard: 50) |
| offset | integer | Optional | Startposition (Standard: 0) |
{
"success": true,
"data": [
{
"id": 42,
"project_id": 1,
"project_name": "Outbound B2B DACH",
"name": "Max Mustermann",
"company": "Mustermann GmbH",
"phone": "+49 221 1234567",
"email": "max@mustermann-gmbh.de",
"position": "Geschaeftsfuehrer",
"address": "Musterstrasse 1, 50667 Koeln",
"website": "www.mustermann-gmbh.de",
"status": "Neu",
"notes": "Kontakt ueber Kontaktformular",
"followup_at": null,
"pipeline_stage": "In Auftrag",
"deal_value": 250.00,
"created_at": "2026-06-01 09:15:00",
"updated_at": "2026-06-01 09:15:00"
},
{
"id": 43,
"project_id": 1,
"project_name": "Outbound B2B DACH",
"name": "Lisa Schmidt",
"company": "Schmidt & Partner AG",
"phone": "+49 30 9876543",
"email": "l.schmidt@schmidt-partner.de",
"position": "Vertriebsleiterin",
"address": null,
"website": "www.schmidt-partner.de",
"status": "Terminiert",
"notes": "Termin am 10.06. um 14:00 Uhr",
"followup_at": "2026-06-10 14:00:00",
"pipeline_stage": "Terminiert",
"deal_value": 500.00,
"created_at": "2026-06-02 11:30:00",
"updated_at": "2026-06-04 16:20:00"
}
],
"total": 84,
"limit": 50,
"offset": 0
}
# Alle Leads curl "https://api.revenue-lab.de/v1/leads" \ -H "Authorization: Bearer rl_live_DEIN_API_KEY" # Filtern: Projekt 1, Status "Neu", Seite 2 curl "https://api.revenue-lab.de/v1/leads?project_id=1&status=Neu&limit=50&offset=50" \ -H "Authorization: Bearer rl_live_DEIN_API_KEY" # Freitextsuche curl "https://api.revenue-lab.de/v1/leads?search=Mustermann" \ -H "Authorization: Bearer rl_live_DEIN_API_KEY"
import requests API_KEY = "rl_live_DEIN_API_KEY" BASE = "https://api.revenue-lab.de/v1" headers = {"Authorization": f"Bearer {API_KEY}"} # Alle neuen Leads aus Projekt 1 res = requests.get(f"{BASE}/leads", headers=headers, params={ "project_id": 1, "status": "Neu", "limit": 50 }) data = res.json() print(f"{len(data['data'])} von {data['total']} Leads geladen")
const API_KEY = 'rl_live_DEIN_API_KEY'; const BASE = 'https://api.revenue-lab.de/v1'; const params = new URLSearchParams({ project_id: 1, status: 'Neu', limit: 50 }); const res = await fetch(`${BASE}/leads?${params}`, { headers: { 'Authorization': `Bearer ${API_KEY}` }, }); const { success, data, total } = await res.json(); console.log(`${data.length} von ${total} Leads geladen`);
Einzelnen Lead abrufen
{
"success": true,
"data": {
"id": 42,
"project_id": 1,
"project_name": "Outbound B2B DACH",
"name": "Max Mustermann",
"company": "Mustermann GmbH",
"phone": "+49 221 1234567",
"email": "max@mustermann-gmbh.de",
"position": "Geschaeftsfuehrer",
"address": "Musterstrasse 1, 50667 Koeln",
"website": "www.mustermann-gmbh.de",
"status": "Neu",
"notes": "Kontakt ueber Kontaktformular",
"followup_at": null,
"pipeline_stage": "In Auftrag",
"deal_value": 250.00,
"created_at": "2026-06-01 09:15:00",
"updated_at": "2026-06-01 09:15:00"
}
}
{
"success": false,
"error": {
"code": "not_found",
"message": "Lead mit ID 9999 nicht gefunden."
}
}
curl "https://api.revenue-lab.de/v1/leads/42" \
-H "Authorization: Bearer rl_live_DEIN_API_KEY"
Lead erstellen
Erstellt einen neuen Lead. Pflicht: project_id + mindestens ein Kontaktfeld (name, company, phone oder email).
Request Body
| Feld | Typ | Beschreibung | |
|---|---|---|---|
| project_id | integer | Pflicht | Projekt-ID (via GET /v1/projects abrufbar) |
| name | string | Optional* | Vor- und Nachname |
| company | string | Optional* | Firmenname |
| phone | string | Optional* | Telefonnummer (freies Format, z.B. +49 221 1234567) |
| string | Optional* | E-Mail-Adresse | |
| position | string | Optional | Position / Jobtitel |
| address | string | Optional | Adresse |
| website | string | Optional | Website-URL |
| status | string | Optional | Lead-Status (Standard: Neu). Siehe Status-Werte. |
| notes | string | Optional | Notizen zum Lead |
| followup_at | datetime | Optional | Wiedervorlage-Zeitpunkt (2026-06-10T14:00:00) |
| deal_value | number | Optional | Lead-Wert in Euro (Standard: Projekt-Standardwert) |
* Mindestens eines von name, company, phone oder email muss angegeben werden.
curl -X POST "https://api.revenue-lab.de/v1/leads" \ -H "Authorization: Bearer rl_live_DEIN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "project_id": 1, "name": "Max Mustermann", "company": "Mustermann GmbH", "phone": "+49 221 1234567", "email": "max@mustermann-gmbh.de", "position": "Geschaeftsfuehrer", "notes": "Anfrage ueber Kontaktformular, Interesse an Paket Business", "status": "Neu" }'
import requests API_KEY = "rl_live_DEIN_API_KEY" BASE = "https://api.revenue-lab.de/v1" headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } lead = { "project_id": 1, "name": "Max Mustermann", "company": "Mustermann GmbH", "phone": "+49 221 1234567", "email": "max@mustermann-gmbh.de", "position": "Geschaeftsfuehrer", "notes": "Anfrage ueber Kontaktformular" } res = requests.post(f"{BASE}/leads", json=lead, headers=headers) result = res.json() if result["success"]: print(f"Lead erstellt: ID {result['data']['id']}") else: print(f"Fehler: {result['error']['message']}")
const res = await fetch('https://api.revenue-lab.de/v1/leads', { method: 'POST', headers: { 'Authorization': 'Bearer rl_live_DEIN_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ project_id: 1, name: 'Max Mustermann', company: 'Mustermann GmbH', phone: '+49 221 1234567', email: 'max@mustermann-gmbh.de', notes: 'Anfrage ueber Kontaktformular', }), }); const { success, data, error } = await res.json(); if (success) console.log('Lead erstellt:', data.id); else console.error(error.message);
{
"success": true,
"data": {
"id": 42,
"project_id": 1,
"name": "Max Mustermann",
"company": "Mustermann GmbH",
"phone": "+49 221 1234567",
"email": "max@mustermann-gmbh.de",
"position": "Geschaeftsfuehrer",
"status": "Neu",
"pipeline_stage": "In Auftrag",
"deal_value": 250.00,
"created_at": "2026-06-05 10:30:00",
"updated_at": "2026-06-05 10:30:00"
}
}
Lead aktualisieren
Aktualisiert einzelne Felder eines Leads. Nur gesendete Felder werden geaendert — alles andere bleibt unberuehrt.
Aktualisierbare Felder
Alle Felder sind optional — sende nur was sich ändert. Unbekannte Keys werden silent ignoriert (kein Fehler).
Stammdaten
| Feld | Typ | Beschreibung |
|---|---|---|
| name | string | Voller Name (Anzeige) |
| salutation | string | Anrede (Herr / Frau / Divers) |
| first_name | string | Vorname |
| last_name | string | Nachname |
| company | string | Firma |
| legal_form | string | Rechtsform (GmbH, UG, AG, ...) |
| position | string | Position / Jobtitel |
| lead_type | string | b2b oder b2c |
| phone | string | Telefon |
| string | ||
| website | string | Website |
Adresse
| Feld | Typ | Beschreibung |
|---|---|---|
| address | string | Komplette Adresse als String (Legacy — wenn gesetzt, werden Einzelfelder ignoriert) |
| street | string | Straße + Hausnummer |
| zip | string | Postleitzahl |
| city | string | Ort |
| country | string | Land |
Sales-Pipeline & Deal
| Feld | Typ | Beschreibung |
|---|---|---|
| status | string | Lead-Status. pipeline_stage wird automatisch synchronisiert (siehe Auto-Logik). |
| pipeline_stage | string | Pipeline-Stage explizit setzen. Unterstützt Custom-Stages. Bei is_won-Stages: Auto-Convert greift. |
| lost_reason | string|null | Verlust-Grund (Freitext, nur sinnvoll bei status=Verloren) |
| deal_value | number|null | Deal-Wert in Euro |
| deal_probability | int 0-100|null | Abschlusswahrscheinlichkeit in Prozent. Wird bei "Gewonnen" automatisch auf 100 gesetzt. |
| expected_close_at | date|null | Voraussichtliches Abschluss-Datum (YYYY-MM-DD). Bei "Gewonnen" und vorher leer: heute. |
| source | string | Lead-Quelle (Cold-Call, LinkedIn, Empfehlung, ...) |
| project_id | int|null | Auftrag/Projekt-Zuordnung. Wird gegen Tenant validiert. |
Termine & Datumsfelder
Alle Datetime-Felder akzeptieren: ISO-8601 (2026-07-01T09:00:00), MySQL-Format (2026-07-01 09:00:00), nur Datum (2026-07-01), null zum Entfernen, oder den Shortcut-String "NOW" (Server-Zeit).
| Feld | Typ | Beschreibung |
|---|---|---|
| followup_at | datetime|null | Wiedervorlage-Datum (für Auto-Dialer-Queue) |
| termin_at | datetime|null | Vereinbarter Termin mit dem Lead |
| appointment_set_at | datetime|null | Zeitpunkt der Terminvereinbarung (für Reports) |
Sonstiges
| Feld | Typ | Beschreibung |
|---|---|---|
| notes | string|null | Notiz-Feld (ersetzt den bestehenden Text. Für append: POST /v1/leads/{id}/notes). |
| custom | object | Custom-Field-Werte als {"field_key": "value"}. Siehe Custom Fields. |
Wenn du
status setzt, wird pipeline_stage automatisch mitgesetzt: Bad Data→Bad Data, Gewonnen→Gewonnen, Verloren→Verloren, alles andere→In Auftrag. Setzt du pipeline_stage auf eine is_won-Custom-Stage, wird auch status="Gewonnen" implizit gesetzt. Explizite Werte schlagen Auto-Logik immer.
is_won-Stage, wird automatisch ein Kunde angelegt (idempotent), deal_probability=100 gesetzt und expected_close_at=heute (sofern leer). Details: Auto-Logik bei "Gewonnen".
# Status auf "Terminiert" setzen + Wiedervorlage eintragen curl -X PATCH "https://api.revenue-lab.de/v1/leads/42" \ -H "Authorization: Bearer rl_live_DEIN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "status": "Terminiert", "notes": "Termin am 10.06. um 14:00 bestaetigt", "followup_at": "2026-06-10T14:00:00", "termin_at": "2026-06-10T14:00:00" }' # Lead als "Gewonnen" markieren — löst Auto-Convert zu Kunde aus # deal_probability=100 + expected_close_at=heute werden automatisch gesetzt curl -X PATCH "https://api.revenue-lab.de/v1/leads/42" \ -H "Authorization: Bearer rl_live_DEIN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "status": "Gewonnen", "deal_value": 12500, "custom": { "angebotspaket": "LinkedIn Starter", "kampagnenstart": "2026-07-01", "laufzeit": "9 Monate" } }'
import requests API_KEY = "rl_live_DEIN_API_KEY" BASE = "https://api.revenue-lab.de/v1" headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } lead_id = 42 update = { "status": "Terminiert", "notes": "Termin am 10.06. um 14:00 bestaetigt", "followup_at": "2026-06-10T14:00:00" } res = requests.patch(f"{BASE}/leads/{lead_id}", json=update, headers=headers) result = res.json() print(f"Neuer Status: {result['data']['status']}")
{
"success": true,
"data": {
"id": 42,
"name": "Max Mustermann",
"company": "Mustermann GmbH",
"status": "Gewonnen",
"pipeline_stage": "Gewonnen",
"deal_value": "12500.00",
"deal_probability": 100, // auto-gesetzt
"expected_close_at": "2026-06-06", // auto-gesetzt (heute)
"custom": {
"angebotspaket": "LinkedIn Starter",
"kampagnenstart": "2026-07-01",
"laufzeit": "9 Monate"
}
// ...alle weiteren Felder
},
"kunde_id": 142, // neu angelegter Kunde
"custom_saved": ["angebotspaket", "kampagnenstart", "laufzeit"]
}
Notizen
Notizen landen im Verlauf eines Leads (identisch mit dem Notizen-Feed im CRM-Drawer). Ideal fuer automatische Status-Updates aus Workflows.
Notiz hinzufuegen
| Feld | Typ | Beschreibung | |
|---|---|---|---|
| note | string | Pflicht | Text der Notiz (max. 5000 Zeichen). Alias: text. |
curl -X POST "https://api.revenue-lab.de/v1/leads/42/notes" \ -H "Authorization: Bearer rl_live_DEIN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "note": "Angebot per Mail versendet. Rueckruf in 3 Tagen." }'
import requests API_KEY = "rl_live_DEIN_API_KEY" BASE = "https://api.revenue-lab.de/v1" headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } lead_id = 42 note = {"note": "Angebot per Mail versendet. Rueckruf in 3 Tagen."} res = requests.post(f"{BASE}/leads/{lead_id}/notes", json=note, headers=headers) print(res.json()) # {"success": true, "data": {...}}
{
"success": true,
"data": {
"id": 187,
"lead_id": 42,
"note": "Angebot per Mail versendet. Rueckruf in 3 Tagen.",
"created_at": "2026-06-05 14:30:00"
}
}
Notizen-Verlauf abrufen
{
"success": true,
"data": [
{
"note": "Angebot per Mail versendet. Rueckruf in 3 Tagen.",
"created_at": "2026-06-05 14:30:00",
"user_name": null // null = via API erstellt
},
{
"note": "Erstanruf: Interesse an Paket Business, Angebot gewuenscht",
"created_at": "2026-06-03 10:15:00",
"user_name": "Tom Weber" // von einem CRM-User erstellt
}
],
"total": 2
}
Lead → Kunde konvertieren
Erstellt aus einem Lead einen Kunden-Datensatz. Idempotent: existiert bereits ein Kunde fuer diesen Lead, wird dieser zurueckgegeben ("created": false).
| Feld | Typ | Beschreibung | |
|---|---|---|---|
| mark_won | boolean | Optional | Setzt den Lead zusaetzlich auf Status Gewonnen. Default: true. |
curl -X POST "https://api.revenue-lab.de/v1/leads/42/convert" \ -H "Authorization: Bearer rl_live_DEIN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "mark_won": true }'
import requests API_KEY = "rl_live_DEIN_API_KEY" BASE = "https://api.revenue-lab.de/v1" headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } lead_id = 42 res = requests.post( f"{BASE}/leads/{lead_id}/convert", json={"mark_won": True}, headers=headers ) result = res.json() if result["data"]["created"]: print(f"Neuer Kunde: ID {result['data']['kunde']['id']}") else: print("Kunde existierte bereits")
{
"success": true,
"data": {
"created": true,
"kunde": {
"id": 12,
"name": "Max Mustermann",
"firma": "Mustermann GmbH",
"lead_id": 42
}
}
}
Dateien hochladen
Haenge Dateien (PDF-Angebote, Vertraege, Screenshots, Sprachnotizen-Transkripte, ...) direkt an einen Lead. Die Datei landet im gleichen Dokumenten-Bereich wie ein manueller Upload im CRM-Drawer.
1.
multipart/form-data — klassisch wie ein HTML-Formular (z.B. curl -F file=@...)2.
application/json mit Base64 — ideal fuer n8n/Make/Zapier ohne Multipart-Support
Limits & erlaubte Typen
- Max. Groesse: 20 MB pro Datei
- Bilder: JPG, PNG, GIF, WebP
- Dokumente: PDF, DOC(X), XLS(X), PPT(X), TXT, CSV
- Archive: ZIP
- Nicht erlaubt: SVG (XSS-Risk), EXE, JS
Optionale Felder: Upload als Vertrag
Mit dem optionalen Flag as_contract=1 landet die Datei zusätzlich im ✍️ Verträge-Tab des Leads (nicht nur unter "Dateien"). Ideal für PandaDoc/DocuSign-Workflows, bei denen die unterzeichnete PDF zurückkommt.
| Feld | Typ | Default | Beschreibung |
|---|---|---|---|
| as_contract | bool | false | Wenn 1/true/yes: zusätzlicher Eintrag in contracts-Tabelle. |
| contract_title | string | Filename ohne Endung | Titel im Verträge-Tab (z. B. "LinkedIn Premium 9M") |
| contract_recipient_name | string | Lead-Name | Empfänger-Anzeige im Verträge-Tab |
| contract_recipient_email | string | Lead-E-Mail | Empfänger-E-Mail im Verträge-Tab |
| contract_status | string | completed | pending | completed | declined | expired. Bei completed: signed_at wird auf jetzt gesetzt. |
as_contract=1 + Setting "Auto-Won": Wenn Settings → Documenso → Lead auto auf Gewonnen aktiv ist, löst der Upload die komplette Won-Kaskade aus: Pipeline-Stage → Gewonnen, Kunde wird angelegt, deal_probability=100, expected_close_at=heute. Ein einziger HTTP-Call schließt den ganzen Deal ab.
Datei hochladen (Multipart)
file
curl -X POST "https://dashboard.revenue-lab.de/api?__path=leads/42/files" \ -H "Authorization: Bearer rl_live_DEIN_API_KEY" \ -F "file=@/pfad/zu/angebot.pdf"
import requests API_KEY = "rl_live_DEIN_API_KEY" BASE = "https://dashboard.revenue-lab.de/api" headers = {"Authorization": f"Bearer {API_KEY}"} lead_id = 42 with open("angebot.pdf", "rb") as f: res = requests.post( f"{BASE}?__path=leads/{lead_id}/files", headers=headers, files={"file": ("angebot.pdf", f, "application/pdf")} ) print(res.json()) # → {"success": true, "data": {"id": 17, "lead_id": 42, "orig_name": "angebot.pdf", # "filesize": 234567, "download_url": "..."}}
const fd = new FormData(); fd.append('file', fileInput.files[0]); const res = await fetch( 'https://dashboard.revenue-lab.de/api?__path=leads/42/files', { method: 'POST', headers: { 'Authorization': 'Bearer rl_live_DEIN_API_KEY' }, body: fd } ); const { data } = await res.json(); console.log('Datei-ID:', data.id);
{
"success": true,
"data": {
"id": 17,
"lead_id": 42,
"orig_name": "angebot.pdf",
"mime_type": "application/pdf",
"filesize": 234567,
"created_at": "2026-06-05 21:00:00",
"download_url": "https://api.revenue-lab.de/v1/leads/42/files/17/download"
}
}
Datei hochladen (JSON + Base64 — fuer n8n/Make/Zapier)
Wenn deine Plattform kein Multipart kann, sende JSON mit filename + content_base64:
# Datei vorher in Base64 konvertieren B64=$(base64 -w0 angebot.pdf) curl -X POST "https://dashboard.revenue-lab.de/api?__path=leads/42/files" \ -H "Authorization: Bearer rl_live_DEIN_API_KEY" \ -H "Content-Type: application/json" \ -d "{\"filename\": \"angebot.pdf\", \"content_base64\": \"$B64\"}"
import requests, base64 API_KEY = "rl_live_DEIN_API_KEY" BASE = "https://dashboard.revenue-lab.de/api" headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } with open("angebot.pdf", "rb") as f: b64 = base64.b64encode(f.read()).decode("ascii") payload = {"filename": "angebot.pdf", "content_base64": b64} res = requests.post(f"{BASE}?__path=leads/42/files", json=payload, headers=headers) print(res.json())
# n8n HTTP Request Node Konfiguration Method: POST URL: https://dashboard.revenue-lab.de/api?__path=leads/{{$json.lead_id}}/files Authentication: Header Auth Header Name: Authorization Header Value: Bearer rl_live_DEIN_API_KEY Send Body: Yes Body Type: JSON JSON Body: { "filename": "{{$node['PandaDoc'].json['document_name']}}", "content_base64": "{{$node['PandaDoc'].json['pdf_base64']}}" } # Antwort: data.download_url kann direkt weiterverarbeitet werden
Dateien auflisten
curl "https://dashboard.revenue-lab.de/api?__path=leads/42/files" \ -H "Authorization: Bearer rl_live_DEIN_API_KEY"
{
"success": true,
"data": [
{
"id": 17,
"orig_name": "angebot.pdf",
"mime_type": "application/pdf",
"filesize": 234567,
"uploaded_by": 0,
"created_at": "2026-06-05 21:00:00",
"download_url": "https://api.revenue-lab.de/v1/leads/42/files/17/download"
}
],
"total": 1
}
Datei downloaden
curl "https://dashboard.revenue-lab.de/api?__path=leads/42/files/17/download" \ -H "Authorization: Bearer rl_live_DEIN_API_KEY" \ -o angebot.pdf
download_url ist nicht oeffentlich teilbar — ohne Header gibt's 401.
Kunden auflisten
Query-Parameter
| Parameter | Typ | Beschreibung | |
|---|---|---|---|
| search | string | Optional | Freitextsuche in Name und Firma |
| limit | integer | Optional | Max. Ergebnisse (1–200, Standard: 50) |
| offset | integer | Optional | Startposition (Standard: 0) |
{
"success": true,
"data": [
{
"id": 12,
"name": "Max Mustermann",
"firma": "Mustermann GmbH",
"email": "max@mustermann-gmbh.de",
"telefon": "+49 221 1234567",
"lead_id": 42,
"created_at": "2026-06-05 15:00:00"
},
{
"id": 13,
"name": "Lisa Schmidt",
"firma": "Schmidt & Partner AG",
"email": "l.schmidt@schmidt-partner.de",
"telefon": "+49 30 9876543",
"lead_id": 43,
"created_at": "2026-06-04 16:30:00"
}
],
"total": 2,
"limit": 50,
"offset": 0
}
curl "https://api.revenue-lab.de/v1/customers?search=Mustermann" \
-H "Authorization: Bearer rl_live_DEIN_API_KEY"
import requests API_KEY = "rl_live_DEIN_API_KEY" BASE = "https://api.revenue-lab.de/v1" headers = {"Authorization": f"Bearer {API_KEY}"} res = requests.get(f"{BASE}/customers", headers=headers) for k in res.json()["data"]: print(f"{k['id']}: {k['firma']} ({k['name']})")
Einzelnen Kunden abrufen
curl "https://api.revenue-lab.de/v1/customers/12" \
-H "Authorization: Bearer rl_live_DEIN_API_KEY"
{
"success": true,
"data": {
"id": 12,
"name": "Max Mustermann",
"firma": "Mustermann GmbH",
"email": "max@mustermann-gmbh.de",
"telefon": "+49 221 1234567",
"lead_id": 42,
"created_at": "2026-06-05 15:00:00"
}
}
ToDos auflisten
Listet alle ToDo-Karten des ToDo-Boards. Optional filterbar nach Spalte, zugewiesenem User oder Erledigt-Status.
Query-Parameter
| Parameter | Typ | Beschreibung |
|---|---|---|
| column_id | int | Nur Karten dieser Spalte (Spalten-IDs via /board sichtbar). |
| assigned_user_id | int | Nur Karten, die diesem User zugewiesen sind. |
| completed | 0|1 | 1 = nur Karten in der letzten Spalte (Erledigt). 0 = alle anderen. |
| limit | int | Max 200 (Default 50). |
| offset | int | Pagination-Offset. |
curl "https://api.revenue-lab.de/v1/todos?completed=0&assigned_user_id=5" \
-H "Authorization: Bearer rl_live_DEIN_API_KEY"
Einzelne ToDo abrufen
{
"success": true,
"data": {
"id": "todo_42",
"card_id": 42,
"title": "Lead nachfassen",
"description": "E-Mail-Sequenz Step 3",
"color_label": "#4a8fff",
"due_date": "2026-06-15",
"assigned_user_id": 5,
"assigned_user_name": "Tom Weber",
"column_id": 3,
"column_name": "In Arbeit",
"is_completed": false,
"position": 2,
"created_at": "2026-06-08 11:22:00"
}
}
ToDo anlegen
Erstellt eine neue Karte auf dem ToDo-Board. Loest den Webhook todo.created aus.
Body (JSON)
| Feld | Typ | Beschreibung |
|---|---|---|
| title * | string | Karten-Titel. Pflicht. |
| description | string | Beschreibung / Notiz. |
| column_id | int | Optional — ohne Angabe landet die Karte in der ersten Spalte. |
| color_label | string | Hex-Farbe der Karte (z.B. #4a8fff). Default Blau. |
| due_date | string | Faelligkeit (YYYY-MM-DD). |
| assigned_user_id | int | User-ID, dem die Karte zugewiesen wird. |
curl -X POST "https://api.revenue-lab.de/v1/todos" \ -H "Authorization: Bearer rl_live_DEIN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Vertrag XY pruefen", "description": "Bis Freitag durchsehen und freigeben", "due_date": "2026-06-12", "color_label": "#ffb86b", "assigned_user_id": 5 }'
{
"success": true,
"data": {
"id": "todo_127",
"card_id": 127,
"title": "Vertrag XY pruefen",
"column_id": 1,
"column_name": "Backlog",
"assigned_user_id": 5,
"is_completed": false
}
}
ToDo aktualisieren
Aendert Felder einer Karte. Loest todo.updated aus. Wechselt die Karte in die letzte (Erledigt-)Spalte, wird zusaetzlich todo.completed gefeuert.
Alle Felder optional. Felder die nicht im Body stehen, bleiben unveraendert.
curl -X PATCH "https://api.revenue-lab.de/v1/todos/127" \ -H "Authorization: Bearer rl_live_DEIN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "column_id": 5 }'
position) gilt als »Erledigt«. Verschiebst du eine Karte dorthin — egal ob per Drag im Board, per PATCH /v1/todos/{id} oder per Automation — feuert RevenueLab den Webhook todo.completed.
ToDo loeschen
curl -X DELETE "https://api.revenue-lab.de/v1/todos/127" \ -H "Authorization: Bearer rl_live_DEIN_API_KEY"
Automationen — Outbound Webhooks
Die REST-API schreibt Daten in RevenueLab. Automationen senden Events heraus: Tritt ein Ereignis ein (z.B. Pipeline-Stage aendert sich), feuert RevenueLab einen POST-Request an deine Ziel-URL.
Trigger-Ereignisse
| Event | Wann | Action im Payload |
|---|---|---|
| lead.created | Ein Lead wird angelegt (CRM oder API). | created |
| pipeline.update | Die Pipeline-Stage eines Leads aendert sich. Optional auf eine Ziel-Stage einschraenkbar (z.B. nur Gewonnen). |
updated |
| appointment.set | Ein Termin wird gesetzt (termin_at) oder der Status wechselt auf Terminiert. |
updated |
| todo.created | Eine neue ToDo-Karte wird angelegt (Board oder API). | created |
| todo.updated | Eine ToDo-Karte wird geaendert (Titel, Beschreibung, Faelligkeit, Zuweisung, Spalte). | updated |
| todo.completed | Eine ToDo-Karte wird in die letzte Spalte (Erledigt) verschoben — egal ob via Board-Drag oder API-PATCH. | completed |
Webhook-Payload
Jeder Webhook sendet ein einheitliches Envelope (Close-Stil). Geaenderte Felder werden als changed_fields mitgeliefert, inklusive vorheriger Werte in previous_data. Custom Fields liegen unter data.custom.*.
{
"subscription_id": "whsub_3",
"tenant_id": 1,
"event": {
"id": "ev_k7Bm3xQ9pLrT2vN8cZ1a4",
"date_created": "2026-06-05T14:22:00+02:00",
"object_type": "lead",
"object_id": "lead_42",
"lead_id": 42,
"action": "updated",
"trigger": "pipeline.update",
"changed_fields": ["status", "pipeline_stage"],
"data": {
"id": 42,
"name": "Max Mustermann",
"company": "Mustermann GmbH",
"email": "max@mustermann-gmbh.de",
"phone": "+49 221 1234567",
"status": "Gewonnen",
"pipeline_stage": "Gewonnen",
"deal_value": "250.00",
"termin_at": "2026-06-10 14:00:00",
"project_name": "Outbound B2B DACH",
"custom": {
"branche": "IT-Dienstleistung",
"mitarbeiter": "25"
}
},
"previous_data": {
"status": "Terminiert",
"pipeline_stage": "Terminiert"
},
"actor": {
"id": 7,
"name": "Tom Weber"
}
}
}
Envelope-Felder
| Feld | Typ | Beschreibung |
|---|---|---|
| subscription_id | string | ID der Automation. Prefix: whsub_. |
| tenant_id | integer | Dein Account / Mandant. |
| event.id | string | Eindeutige Event-ID, Prefix ev_. Nutzbar zur Deduplizierung / Idempotenz. |
| event.date_created | string | ISO-8601 Timestamp mit Zeitzone. |
| event.object_type | string | Aktuell immer "lead". |
| event.object_id | string | Sprechende ID, z.B. "lead_42". Numerisch auch als event.lead_id. |
| event.action | string | "created" oder "updated". |
| event.trigger | string | Das ausloesende Ereignis: lead.created, pipeline.update, appointment.set. |
| event.changed_fields | string[] | Liste der geaenderten Feldnamen. Bei created: alle befuellten Felder. |
| event.data | object | Vollstaendiger aktueller Lead-Zustand. Custom Fields unter data.custom.<key>. |
| event.previous_data | object | Alte Werte der geaenderten Felder (Diff). Bei created leer: {}. |
| event.actor | object | Ausloesender Nutzer mit id und name. |
event.changed_fields (z.B. enthaelt "pipeline_stage"), lies den neuen Wert aus event.data.pipeline_stage und den alten aus event.previous_data.pipeline_stage. So reagierst du gezielt auf bestimmte Aenderungen.
Event-Log
Protokoll aller eingehenden API-Calls und ausgehenden Webhook-Zustellungen. Gleiche Daten wie unter Settings → Event-Log.
Query-Parameter
| Parameter | Typ | Beschreibung |
|---|---|---|
| category | string | api (eingehende Calls) oder webhook (ausgehende Zustellungen). Leer = beides. |
| limit | integer | Max. Ergebnisse (1–200, Standard: 50) |
| offset | integer | Startposition (Standard: 0) |
{
"success": true,
"data": [
{
"id": 501,
"category": "api",
"method": "POST",
"route": "/v1/leads",
"object_type": "lead",
"object_id": "42",
"status_code": 201,
"success": true,
"actor": "API Key: rl_live_abc1...",
"ip": "203.0.113.42",
"created_at": "2026-06-05 10:30:00"
},
{
"id": 502,
"category": "webhook",
"method": "lead.created",
"route": "https://hook.eu2.make.com/abc123...",
"object_type": "lead",
"object_id": "42",
"status_code": 200,
"success": true,
"actor": "Tom Weber",
"meta": { "changed_fields": ["name", "company", "phone"] },
"created_at": "2026-06-05 10:30:01"
}
],
"total": 502,
"limit": 50,
"offset": 0
}
Wie API-Calls funktionieren
Diese Sektion erklärt das mentale Modell der API — gut zu lesen bevor du den ersten Workflow baust. Spart später viel Debugging.
1. Lebenszyklus eines Leads
Ein Lead durchläuft in RevenueLab folgende Stufen — jede davon kannst du per API steuern:
| Phase | Wer triggert | API-Call |
|---|---|---|
| Erstellung | Externe Quelle (Website-Formular, Werbe-Lead, Import) | POST /v1/leads |
| Kontakt & Pflege | Agent im Dialer + externe Tools (E-Mail-Antwort, SMS) | PATCH /v1/leads/{id} + POST /v1/leads/{id}/notes |
| Angebot/Vertrag | Vertragstool (PandaDoc, Documenso) via n8n | POST /v1/leads/{id}/files mit as_contract=1 |
| Abschluss | Vertragstool / Sales-Agent | PATCH /v1/leads/{id} mit status=Gewonnen → Auto-Logik feuert |
| Kundenpflege | CRM-Team (eigene Kunden-API, oder via Customer-ID) | GET /v1/customers/{id} |
2. Der "PATCH-Geist": send only what changes
Anders als REST-PUT überschreibt PATCH den Lead nicht mit dem Body. Es ändert nur die Felder, die du explizit mitschickst. Felder die nicht im Body sind, bleiben unangetastet.
null = bewusst leer setzen. Feld weglassen = unverändert lassen.
3. Idempotenz — ein API-Call darf doppelt passieren
Webhook-Provider (n8n, Make, Zapier) wiederholen Calls bei Timeouts. Die wichtigsten RevenueLab-Endpoints sind darauf vorbereitet:
POST /v1/leads/{id}/convert— legt den Kunden nur an wenn noch keiner existiert, sonst gibt es den bestehenden zurück (created: false).PATCH /v1/leads/{id}mitstatus=Gewonnen— Auto-Convert prüft ob schon ein Kunde da ist und legt nur fehlende an.POST /v1/leads/{id}/filesmitas_contract=1— jeder Upload erzeugt einen eigenen Datei- und Vertrags-Eintrag (kein Dedup auf Filename), das ist gewollt.
4. Fire-and-Forget bei Webhooks
Wenn ein PATCH einen Outbound-Webhook auslöst (pipeline.update, appointment.set), wartet die API nicht auf die Webhook-Zustellung. Du bekommst die 200 OK-Antwort sofort, der Webhook geht im Hintergrund raus (fastcgi_finish_request). Vorteil: keine Verzögerung in deinem Workflow. Nachteil: wenn der Empfänger 500 zurückgibt, siehst du das nur im Event-Log (category=webhook).
5. Was du beobachtest, ist nicht die Wahrheit
Der Webhook-Payload enthält previous_data (was vorher war) und changed_fields (was sich geändert hat). Wenn dein Workflow auf "Lead wurde Gewonnen" reagieren soll, prüfe "pipeline_stage" in changed_fields && data.pipeline_stage === "Gewonnen" — nicht nur data.pipeline_stage === "Gewonnen". Sonst feuerst du bei jedem Update eines Won-Leads erneut.
Auto-Logik bei "Gewonnen"
Sobald ein Lead in eine is_won-Pipeline-Stage geht (standardmäßig Gewonnen, plus jede Custom-Stage mit is_won=1), feuert RevenueLab eine Kaskade von Auto-Aktionen. Das gilt für jeden Weg dorthin: UI, PATCH, File-Upload, oder Documenso-Webhook.
Was automatisch passiert
| # | Aktion | Bedingung |
|---|---|---|
| 1 | Kunde wird angelegt (Tabelle kunden) — mit Name, Firma, Telefon, E-Mail, Website, Adresse und Umsatz (aus deal_value). | Nur wenn noch kein Kunde mit diesem lead_id existiert (idempotent). |
| 2 | Kunden-Aktivität "system" angelegt — sichtbar im Kunden-Drawer. | Immer, wenn (1) feuert. |
| 3 | status wird auf Gewonnen gesetzt (auch bei Custom-is_won-Stages). | Nur wenn Client nicht explizit anderen Status mitgab. |
| 4 | deal_probability wird auf 100 gesetzt. | Nur wenn Client keinen expliziten Wert mitgab. Überschreibt bestehende Werte (z. B. 70% → 100), denn "Gewonnen mit 70%" ist semantisch sinnlos. |
| 5 | expected_close_at wird auf heute gesetzt. | Nur wenn Client keinen expliziten Wert mitgab UND DB-Wert vorher leer war. Überschreibt KEINE bestehenden Werte — ein gesetztes Forecast-Datum bleibt. |
| 6 | Audit-Eintrag wird angelegt (vertrag_unterzeichnet bei Datei-Upload als Vertrag, sonst pipeline_stage-Änderung). | Immer. |
| 7 | Outbound-Webhook pipeline.update wird gefeuert (Close-Style mit changed_fields + previous_data). | Wenn aktive Automation für diesen Trigger existiert. |
Begründung der Asymmetrie zwischen Punkt 4 und 5
Wahrscheinlichkeit ist axiomatisch — ein gewonnener Lead hat per Definition 100%. "Gewonnen mit 70%" widerspricht sich selbst, deshalb wird der Wert überschrieben.
Close-Date dagegen kann legitim in der Zukunft liegen ("Vertrag tritt am 01.09.2026 in Kraft" / "Kampagnenstart erst nächsten Monat"). Wenn Sales das bewusst gesetzt hat, würden wir Daten zerstören, indem wir auf "heute" überschreiben. Darum: nur befüllen wenn leer.
Was du explizit überschreiben kannst
Jeder der oben automatisierten Werte lässt sich vom Client immer explizit setzen — explizite Werte schlagen Auto-Defaults:
// Beispiel: Lead gewinnen, aber mit nur 90% Wahrscheinlichkeit // (z.B. weil Anzahlung noch offen ist) und Close-Date in der Zukunft { "status": "Gewonnen", "deal_probability": 90, "expected_close_at": "2026-09-01" }
Die drei Wege zu "Gewonnen"
| Weg | Call | Wann nutzen |
|---|---|---|
| PATCH status | PATCH /v1/leads/{id}{ "status": "Gewonnen" } | Einfachster Weg. Wenn dein Workflow eh PATCH-Felder updatet (Deal-Wert, Notiz, Custom-Felder). |
| PATCH pipeline_stage | PATCH /v1/leads/{id}{ "pipeline_stage": "Premium-Won" } | Wenn du eine Custom-Stage mit is_won=1 nutzt (z. B. um nach Deal-Größe zu segmentieren). |
| File-Upload as_contract | POST /v1/leads/{id}/filesmultipart mit as_contract=1 | Wenn du eine unterzeichnete Vertrags-PDF hochlädst (PandaDoc, DocuSign). Datei landet im Verträge-Tab UND Lead geht auf Gewonnen, sofern auto_won_on_contract_signed aktiv ist. |
as_contract=1 die ganze Won-Kaskade triggert oder nur den Vertrag ablegt.
Custom Fields
Custom Fields ("Benutzerdefinierte Felder") sind tenant-spezifische Zusatzfelder, die du im Admin-Bereich definierst (Name, Typ, Sichtbarkeitsbereich). RevenueLab speichert sie getrennt von der leads-Tabelle in custom_field_values — du musst sie aber API-seitig genauso behandeln wie normale Felder.
Lesen: bei jedem Lead-Read enthalten
Sowohl GET /v1/leads/{id} als auch der Webhook-Payload (data.custom) enthalten alle Custom-Field-Werte als { field_key: value }:
{
"data": {
"id": 4426,
"name": "Sabine Jäger",
// ... Standard-Felder ...
"custom": {
"angebotspaket": "LinkedIn Starter",
"garantiertetermine": "5",
"kampagnenstart": "2026-07-01",
"laufzeit": "9 Monate",
"preisnetto": "12500"
}
}
}
Schreiben: custom-Objekt im PATCH-Body
Im PATCH-Body fügst du ein custom-Objekt hinzu. Die Keys sind die field_key-Werte (genauso wie im Read-Payload). Werte: String/Number/Boolean. null löscht den Wert.
PATCH /v1/leads/4426
{
"deal_value": 12500,
"custom": {
"angebotspaket": "LinkedIn Premium",
"kampagnenstart": "2026-07-15",
"preisnetto": "18500"
}
}
Die Response enthält custom_saved: [...] mit der Liste der tatsächlich gespeicherten Keys — perfekt zum Debugging:
{
"success": true,
"data": { /* ... inkl. custom-Objekt mit neuen Werten ... */ },
"custom_saved": ["angebotspaket", "kampagnenstart", "preisnetto"]
}
"angbotspaket" (Tippfehler) statt "angebotspaket", erscheint der Key NICHT in custom_saved — aber es gibt auch keinen Fehler. Das ist gewollt (Tenants können Felder löschen, ohne dass alte Integrationen brechen), erfordert aber dass du custom_saved in deinem Workflow prüfst wenn du sicher gehen willst.
Wo finde ich die field_keys?
- Admin → Custom Fields → Spalte "Key"
- Oder: einmal
GET /v1/leads/{id}aufrufen — imcustom-Objekt siehst du alle Keys - Oder: einen Webhook für
lead.createdoderpipeline.updateanlegen — im Payload stehtevent.data.custommit allen Keys
Lead-Status-Werte
Erlaubte Werte fuer das Feld status bei POST /v1/leads und PATCH /v1/leads/{id}:
| Status | Beschreibung | Pipeline-Stage | In Dialer-Queue? |
|---|---|---|---|
| Neu | Neuer Lead, noch nicht kontaktiert (Standard) | In Auftrag | Ja |
| Wiedervorlage | Rueckruf geplant — erscheint zum Wiedervorlage-Zeitpunkt | In Auftrag | Ja |
| Nicht erreicht | Anrufversuch ohne Erreichung | In Auftrag | Ja |
| Entscheider nicht erreicht | Nur Sekretariat/Assistent erreicht | In Auftrag | Ja |
| Terminiert | Termin wurde vereinbart | In Auftrag | Nein |
| Kein Interesse | Lead hat abgelehnt | In Auftrag | Nein |
| Bad Data | Fehlerhafte Kontaktdaten | Bad Data | Nein |
| Info Mail angefordert | Infomaterial angefordert | In Auftrag | Nein |
| Info Mail versendet | Infomaterial wurde versendet | In Auftrag | Nein |
| Gewonnen | Deal abgeschlossen — löst Auto-Convert-Kaskade aus | Gewonnen | Nein |
| Verloren | Deal verloren (Grund optional via lost_reason) | Verloren | Nein |
Lead-Objekt (vollstaendiges Schema)
Jedes Lead-Objekt in data enthaelt diese Felder:
| Feld | Typ | Beschreibung |
|---|---|---|
| id | integer | Eindeutige Lead-ID |
| project_id | integer | Zugehoeriges Projekt |
| project_name | string | Projektname (read-only, JOIN) |
| name | string|null | Vor- und Nachname |
| salutation / first_name / last_name | string|null | Aufgespaltene Namen |
| company | string|null | Firmenname |
| legal_form | string|null | Rechtsform |
| position | string|null | Position / Jobtitel |
| lead_type | string | b2b oder b2c |
| phone | string|null | Telefonnummer |
| string|null | E-Mail-Adresse | |
| website | string|null | Website-URL |
| address | string|null | Adresse als ganzer String (Legacy) |
| street / zip / city / country | string|null | Aufgespaltene Adressfelder |
| status | string | Lead-Status (siehe Status-Werte) |
| pipeline_stage | string | Aktuelle Pipeline-Stage (Standard oder Custom) |
| lost_reason | string|null | Verlust-Grund (nur bei status=Verloren) |
| source | string|null | Lead-Quelle |
| deal_value | number|null | Deal-Wert in Euro |
| deal_probability | int|null | Abschlusswahrscheinlichkeit (0–100). Bei "Gewonnen" auto auf 100. |
| expected_close_at | date|null | Voraussichtl. Abschluss-Datum (YYYY-MM-DD) |
| termin_at | datetime|null | Vereinbarter Termin mit dem Lead |
| appointment_set_at | datetime|null | Zeitpunkt der Terminvereinbarung |
| followup_at | datetime|null | Wiedervorlage-Zeitpunkt |
| notes | string|null | Notizen-Text (Single-Field — für Verlauf: /v1/leads/{id}/notes) |
| custom | object | Custom-Field-Werte (siehe Custom Fields) |
| created_at | datetime | Erstellungszeitpunkt |
| updated_at | datetime | Letzter Aenderungszeitpunkt |
Pagination
Alle Listen-Endpunkte nutzen Offset-Pagination ueber limit und offset.
| Parameter | Default | Max | Beschreibung |
|---|---|---|---|
| limit | 50 | 200 | Ergebnisse pro Seite |
| offset | 0 | — | Ueberspringt die ersten N Ergebnisse |
Alle Seiten laden
import requests API_KEY = "rl_live_DEIN_API_KEY" BASE = "https://api.revenue-lab.de/v1" headers = {"Authorization": f"Bearer {API_KEY}"} all_leads = [] offset = 0 limit = 200 # Maximum while True: res = requests.get(f"{BASE}/leads", headers=headers, params={"limit": limit, "offset": offset}) page = res.json() all_leads.extend(page["data"]) if len(all_leads) >= page["total"]: break offset += limit print(f"{len(all_leads)} Leads geladen")
async function getAllLeads() { const API_KEY = 'rl_live_DEIN_API_KEY'; const BASE = 'https://api.revenue-lab.de/v1'; const all = []; let offset = 0; const limit = 200; while (true) { const res = await fetch(`${BASE}/leads?limit=${limit}&offset=${offset}`, { headers: { 'Authorization': `Bearer ${API_KEY}` } }); const page = await res.json(); all.push(...page.data); if (all.length >= page.total) break; offset += limit; } return all; }
Zapier, Make & n8n Integration
n8n — HTTP Request Node
- Node: HTTP Request
- Method:
POST - URL:
https://api.revenue-lab.de/v1/leads - Authentication: Header Auth
- Header Name:
Authorization— Value:Bearer rl_live_DEIN_API_KEY - Body Type:
JSON - Body: Felder mappen (project_id, name, phone, email, ...)
n8n — Webhook empfangen
- Node: Webhook (Trigger)
- HTTP Method:
POST - Webhook-URL kopieren und in Settings → Automationen als Ziel-URL eintragen
- Daten liegen unter
body.event.data.*undbody.event.changed_fields
Make (Integromat) — HTTP-Modul
- Modul: HTTP → Make a request
- URL:
https://api.revenue-lab.de/v1/leads - Method:
POST - Headers:
Authorization→Bearer rl_live_DEIN_API_KEY - Body type:
Raw→application/json
Zapier — Webhooks by Zapier
- Trigger: Beliebig (Typeform, Jotform, Google Forms, ...)
- Action: Webhooks by Zapier → POST
- URL:
https://api.revenue-lab.de/v1/leads - Payload Type:
json - Headers:
Authorization | Bearer rl_live_DEIN_API_KEY
{
"project_id": 1,
"name": "{{Name}}",
"email": "{{Email}}",
"phone": "{{Telefon}}",
"company": "{{Firma}}",
"notes": "Quelle: {{Formularname}}"
}
GET /v1/projects aufrufen, die gewuenschte ID notieren und im Workflow fest hinterlegen.