Mit der inDeal API legst du Leads aus anderen Systemen an, liest sie, hältst sie synchron und schreibst Aktivitäten wie Notizen oder Anrufe in die Timeline. Alles, was du dafür brauchst, findest du unter Einstellungen -> API & Webhooks.
Inhalt
- Schlüssel erstellen
- Grundlagen
- Lead anlegen
- Lead lesen
- Lead aktualisieren
- Leads auflisten
- Aktivitäten
- Felder
- Doppelte Leads
- Fehlercodes
- Weiter
Schlüssel erstellen
- Öffne Einstellungen -> API & Webhooks.
- Klick auf Neuen Schlüssel erstellen und vergib einen Namen, der das angebundene System beschreibt.
- Kopiere den Schlüssel sofort. Kopiere den Schlüssel jetzt. Er wird nur einmal angezeigt und kann später nicht erneut abgerufen werden.


Danach zeigt die Liste nur noch die letzten vier Zeichen. Brauchst du den Schlüssel erneut, widerrufe den alten mit Widerrufen und erstelle einen neuen. Es sind bis zu 10 aktive Schlüssel möglich - so kannst du je System einen eigenen vergeben und einzeln widerrufen.

Grundlagen
| Angabe | Wert |
|---|---|
| Base-URL | https://app.indeal.ai/api/v1 |
| Authentifizierung | Header Authorization: Bearer indeal_... |
| Format | JSON in Anfrage und Antwort |
| Limit | 600 Anfragen pro Minute je Schlüssel |
Über dem Limit antwortet die API mit Status 429 und dem Header Retry-After: 60 - warte dann eine Minute.
Lead anlegen
POST /leads legt einen Lead an. Mindestens eines von email oder linkedin_url (die Profil-URL des Kontakts) ist Pflicht.
curl -X POST https://app.indeal.ai/api/v1/leads \
-H "Authorization: Bearer indeal_..." \
-H "Content-Type: application/json" \
-d '{
"full_name": "Julia Weber",
"email": "julia.weber@muster.de",
"company_name": "Muster GmbH",
"role_title": "Geschaeftsfuehrerin"
}'
Antwort bei Erfolg (Status 201):
{
"status": "created",
"lead": {
"id": "0b0e...",
"stage": "qualifying",
"full_name": "Julia Weber",
"email": "julia.weber@muster.de",
"company_name": "Muster GmbH"
}
}
Existiert der Lead schon, bekommst du Status 200 mit "status": "duplicate" und dem bestehenden Lead - siehe Doppelte Leads.
Lead lesen
GET /leads/{id} liefert einen einzelnen Lead.
curl https://app.indeal.ai/api/v1/leads/0b0e... \
-H "Authorization: Bearer indeal_..."
Antwort (Status 200): { "lead": { ... } }. Eine unbekannte ID liefert Status 404.
Lead aktualisieren
PATCH /leads/{id} ändert nur die Felder, die du schickst. null leert ein Feld. Mit stage wechselst du die Phase - die Verlaufshistorie in inDeal wird dabei automatisch geschrieben. custom_data wird zusammengeführt, nicht ersetzt: null als Wert löscht einen Key, alle anderen Keys bleiben.
curl -X PATCH https://app.indeal.ai/api/v1/leads/0b0e... \
-H "Authorization: Bearer indeal_..." \
-H "Content-Type: application/json" \
-d '{ "stage": "meeting_scheduled", "next_step": "Demo vorbereiten" }'
Antwort (Status 200): { "lead": { ... } }. Kollidiert die Änderung mit einem bestehenden Lead (E-Mail oder Profil-URL schon vergeben), kommt Status 409.
Leads auflisten
GET /leads liefert Leads seitenweise, sortiert nach Änderungszeitpunkt.
curl "https://app.indeal.ai/api/v1/leads?updated_since=2026-09-01T00:00:00Z&limit=100" \
-H "Authorization: Bearer indeal_..."
| Parameter | Bedeutung |
|---|---|
updated_since |
nur Leads, die seit diesem Zeitpunkt geändert wurden (ISO 8601) |
limit |
1 bis 200, Standard 100 |
cursor |
next_cursor aus der vorigen Antwort |
order |
asc (Standard, älteste Änderung zuerst) oder desc (neueste zuerst) |
Antwort: { "leads": [...], "next_cursor": "..." }. Ist next_cursor null, bist du am Ende. Der Cursor merkt sich die Sortierrichtung, du musst order beim Blättern nicht wiederholen. Für laufende Synchronisation: regelmäßig mit updated_since anfragen und mit cursor blättern.
Lead per E-Mail oder Profil-URL finden
Du kennst die E-Mail-Adresse oder die Profil-URL und brauchst die id? Gib sie als Parameter mit. inDeal sucht dann genauso wie beim Anlegen: Groß- und Kleinschreibung spielen keine Rolle, bei der Profil-URL auch nicht https://, www oder ein Schrägstrich am Ende.
curl "https://app.indeal.ai/api/v1/leads?email=julia.weber@muster.de" \
-H "Authorization: Bearer indeal_..."
| Parameter | Bedeutung |
|---|---|
email |
Treffer über die E-Mail-Adresse |
linkedin_url |
Treffer über die Profil-URL |
Gibst du beide an, bekommst du jeden Lead, auf den mindestens eines passt. Die Antwort ist { "leads": [...] } ohne next_cursor, denn mehr als zwei Treffer kann es nicht geben. Passt nichts, ist die Liste leer. updated_since und cursor werden bei dieser Suche ignoriert. Eine ungültige E-Mail-Adresse oder URL liefert Status 400 mit dem Feldnamen.
Aktivitäten
Jeder Lead hat eine Timeline. Über die API legst du dort Einträge an und liest sie.
Aktivität anlegen
POST /leads/{id}/activities legt einen Timeline-Eintrag am Lead an. Schreibbar sind die Typen note (Notiz), call (Anruf), email (E-Mail) und meeting_scheduled (Termin). Mindestens eines von title oder body ist Pflicht. Fehlt title, bildet inDeal ihn aus den ersten 80 Zeichen von body.
| Feld | Pflicht | Hinweis |
|---|---|---|
type |
ja | note, call, email, meeting_scheduled |
title |
nein | max. 200 Zeichen |
body |
nein | Freitext, max. 10000 Zeichen, etwa das Gesprächsergebnis |
channel |
nein | linkedin, email, meet, call |
occurred_at |
nein | Zeitpunkt (ISO 8601), Standard jetzt, nicht in der Zukunft |
curl -X POST https://app.indeal.ai/api/v1/leads/0b0e.../activities \
-H "Authorization: Bearer indeal_..." \
-H "Content-Type: application/json" \
-d '{ "type": "call", "body": "Kurzes Telefonat, Demo nächste Woche", "channel": "call" }'
Antwort bei Erfolg (Status 201):
{
"activity": {
"id": "9f3a...",
"type": "call",
"title": "Kurzes Telefonat, Demo nächste Woche",
"body": "Kurzes Telefonat, Demo nächste Woche",
"quote": null,
"channel": "call",
"occurred_at": "2026-09-03T14:00:00+00:00",
"created_at": "2026-09-03T14:00:01+00:00",
"pinned": false,
"edited_at": null
}
}
Der Eintrag erscheint sofort in der Timeline des Leads, und Anrufe sowie E-Mails zählen als Kontaktpunkt. Die Typen reply (Antwort aus einer Kampagne) und stage_change (Phasenwechsel) schreibt inDeal selbst; schickst du sie, kommt Status 400. Eine unbekannte Lead-ID liefert Status 404.
Timeline lesen
GET /leads/{id}/activities liefert alle Einträge des Leads, neueste zuerst, inklusive der nur lesbaren Typen reply und stage_change.
| Parameter | Bedeutung |
|---|---|
type |
nur ein Typ: note, call, email, meeting_scheduled, reply, stage_change |
limit |
1 bis 200, Standard 50 |
cursor |
next_cursor aus der vorigen Antwort |
curl "https://app.indeal.ai/api/v1/leads/0b0e.../activities?type=reply&limit=50" \
-H "Authorization: Bearer indeal_..."
Antwort: { "activities": [...], "next_cursor": "..." }. Bei Antworten aus Kampagnen steht der Text in quote. edited_at zeigt, dass ein Eintrag nachträglich bearbeitet wurde. Ändern oder Löschen per API gibt es nicht.
Willst du über neue Einträge sofort informiert werden, statt die Timeline abzufragen, abonniere das Webhook-Ereignis activity.created - siehe inDeal Webhooks.
Felder
Pflichtregel: mindestens eines von email / linkedin_url beim Anlegen. Alles andere ist optional.
| Feld | Typ | Hinweis |
|---|---|---|
full_name |
Text | Name des Kontakts |
role_title |
Text | Position |
email |
Text | Dedup-Anker |
phone, mobile, company_phone |
Text | werden automatisch bereinigt |
linkedin_url |
Text | Profil-URL, Dedup-Anker |
company_name |
Text | Firma |
company_size |
Ganzzahl | Mitarbeiterzahl |
industry |
Text | Branche |
company_website, company_linkedin |
Text | Firmen-Links |
street, zip, city, country |
Text | Adresse |
next_step |
Text | nächster Schritt |
next_step_due |
Datum | Format YYYY-MM-DD |
est_value |
Zahl | geschätzter Wert |
description |
Text | Beschreibung |
lead_source |
Text | Herkunft |
sentiment |
Auswahl | positive, neutral, negative |
channel_direction |
Auswahl | inbound, outbound |
lost_reason |
Text | Verlustgrund |
stage |
Auswahl | qualifying, schedule_meeting, meeting_scheduled, meeting_no_show, meeting_done, lead_lost |
custom_data |
Objekt | eigene Felder, siehe Eigene Felder |
Jede Antwort enthält zusätzlich url, den Link zur Lead-Seite in inDeal (nur lesend). Unbekannte Felder lehnt die API ab - die Fehlermeldung nennt das betroffene Feld. Interne Felder (etwa Anreicherungs-Status) gibt die API weder heraus noch nimmt sie sie an.
Eigene Felder
Eigene Felder legst du in inDeal unter Layout anpassen an, getrennt für Leads und Deals. Ihre Werte stehen im Objekt custom_data, der Schlüssel ist der API-Name des Feldes.
Felder abrufen
curl "https://app.indeal.ai/api/v1/custom-fields?entity=lead" \
-H "Authorization: Bearer indeal_..."
entity ist Pflicht (lead oder deal). Die Antwort enthält alle aktiven Felder in der Reihenfolge des Layouts, jeweils mit key (API-Name), label, type, bei Auswahlfeldern options, bei Währung currency_code und der section. Ausgeblendete Felder fehlen.
Wertformat pro Typ
type |
Wert in custom_data |
Beispiel |
|---|---|---|
text |
Text, max. 10.000 Zeichen, wird getrimmt | "Rückruf nach Messe" |
url |
Link, nur http oder https; ohne Schema wird https:// ergänzt |
"https://indeal.ai" |
number |
Zahl | 42 |
currency |
Zahl; die Währung steht in currency_code des Feldes |
5000 |
checkbox |
true oder false |
true |
date |
YYYY-MM-DD |
"2026-10-15" |
datetime |
ISO 8601 mit Offset | "2026-10-15T09:30:00+02:00" |
dropdown |
der API-Name einer Option, nicht ihr Name | "gold" |
multiselect |
Liste von Options-API-Namen, doppelte werden entfernt | ["messe", "empfehlung"] |
null löscht einen Wert, ebenso ein leerer Text oder eine leere Liste. Pro Request sind höchstens 50 Schlüssel in custom_data erlaubt.
Prüfung beim Schreiben
POST und PATCH auf Leads und Deals prüfen custom_data gegen die Felder des jeweiligen Moduls:
- Unbekannte Schlüssel und Schlüssel ausgeblendeter Felder werden ignoriert und in
warningsgemeldet. Das ist kein Fehler, der Rest wird gespeichert.
{ "lead": { ... }, "warnings": [{ "key": "partner_id", "code": "unknown_key" }, { "key": "alt", "code": "deleted_field" }] }
- Ungültige Werte bekannter Felder ergeben Status 422 mit
validation_errorunddetails. Dann wird nichts geschrieben, auch keine anderen Felder aus demselben Request.
{
"error": {
"code": "validation_error",
"message": "custom_data: invalid values.",
"details": [
{ "key": "tarif", "code": "invalid_option" },
{ "key": "budget", "code": "invalid_type" }
]
}
}
Mögliche Werte für details.code: invalid_type, invalid_option, invalid_date, invalid_url, too_long, readonly_field. warnings ist in jeder Schreibantwort enthalten, als leere Liste, wenn nichts ignoriert wurde.
Doppelte Leads
Beim Anlegen prüft inDeal zuerst die Profil-URL, dann die E-Mail-Adresse - beide unabhängig von Groß- und Kleinschreibung. Gibt es bereits einen passenden Lead, wird KEIN neuer angelegt: du bekommst Status 200 mit "status": "duplicate" und dem bestehenden Lead samt seiner id.
Das macht den Endpoint gefahrlos wiederholbar: dieselbe Anfrage zweimal zu senden erzeugt nie zwei Leads. Willst du den bestehenden Lead ändern, nutze die id aus der Antwort mit PATCH /leads/{id}.
Fehlercodes
Alle Fehler haben dieselbe Form: { "error": { "code": "...", "message": "..." } }. Die Meldung ist immer auf Englisch und für Menschen gedacht, prüfe in deinem Code den code.
{ "error": { "code": "not_found", "message": "Lead not found." } }
| Code | Status | Bedeutung |
|---|---|---|
unauthorized |
401 | Schlüssel fehlt, ist ungültig oder widerrufen |
rate_limited |
429 | Limit überschritten, warte die Sekunden aus Retry-After |
validation_error |
400 | Eingabe ungültig, die Meldung nennt das Feld |
validation_error |
422 | Ungültige Werte in custom_data, details nennt Feld und Grund, es wurde nichts geschrieben |
not_found |
404 | Lead existiert nicht oder liegt im Papierkorb |
conflict |
409 | Änderung kollidiert mit einem bestehenden Lead |
blacklisted |
409 | Person oder Firma steht auf der Blacklist, es wurde nichts angelegt |
internal |
500 | Unerwarteter Fehler, versuch es erneut |
internal |
503 | Blacklist-Prüfung vorübergehend nicht möglich, es wurde nichts angelegt, versuch es später erneut |