🚀 Jetzt neu: FlowAI! Ein revolutionärer Chat mit personalisierten AI-Agenten. 🌟

FlowCRM API - für moderne CRM-Integrationen

Eine moderne REST API zur Verwaltung von Kundendaten, Leads, Kanban-Boards und Timeline-Einträgen. Perfekt für CRM-Integrationen, Lead-Ingest, Automatisierung und individuelle Anwendungen.

🔐 Schnellstart

Basis-URL: https://creativeskyline.de/api/clients https://creativeskyline.de/api/clients/{uuid}/timeline https://creativeskyline.de/api/clients/{uuid}/projects https://creativeskyline.de/api/leads https://creativeskyline.de/api/lead-boards https://creativeskyline.de/api/leads/{uuid}/docs
Sicherheitshinweis
Behandeln Sie Ihren API-Key wie ein Passwort und teilen Sie ihn niemals öffentlich.
Zugriff auf die Leads-Endpunkte
Die Leads-, Board- und Lead-FlowDoc-Endpunkte erfordern zusätzlich zum FlowCRM-Modul die Leads-Unterberechtigung Ihres Teams. Ein Token ohne diese Unterberechtigung erhält HTTP 403. Die Kunden-Endpunkte sowie die FlowDoc-Endpunkte für Kunden, Kontakte und Projekte benötigen nur den FlowCRM-Modulzugriff.

1. API-Zugangsdaten abrufen

Bevor Sie die API nutzen können, benötigen Sie Ihre persönlichen Zugangsdaten: Melden Sie sich an, navigieren Sie zu Profil → API-Zugang und kopieren Sie Ihren persönlichen API-Key (pk_...).

2. Authentifizierung

Alle API-Anfragen benötigen diesen HTTP-Header. Akzeptiert werden ein persönlicher Key (pk_...) oder ein Team-API-Token (at_...):

Authorization: Bearer pk_dein_api_key
Accept: application/json

Pro Token sind bis zu 300 Anfragen pro Minute erlaubt.

🏢 Kunden-Verwaltung

Kunden auflisten

GET /api/clients

Ruft eine Liste aller aktiven Kunden Ihres Teams ab. Verwenden Sie für die Adressierung einzelner Kunden ausschließlich das Feld uuid.

Query-Parameter:

per_page integer Einträge pro Seite (1 bis 500, Standard 500). Die Antwort enthält zusätzlich einen <code>meta</code>-Block mit <code>current_page</code>, <code>per_page</code>, <code>total</code> und <code>last_page</code>.
page integer Seitennummer, beginnend bei 1
GET /api/clients/search

Durchsucht die Kundendatenbank nach Name, Firmenname oder E-Mail-Adresse.

Query-Parameter:

search string * Suchbegriff für die Kundensuche
per_page integer Einträge pro Seite (1 bis 500, Standard 500). Die Antwort enthält zusätzlich einen <code>meta</code>-Block mit <code>current_page</code>, <code>per_page</code>, <code>total</code> und <code>last_page</code>.
page integer Seitennummer, beginnend bei 1

Kunde anlegen (Find-or-Create)

POST /api/clients

Legt einen Kunden im Team des Tokens an. Idempotent per Find-or-Create: Zuerst wird über eine gesetzte external_id gesucht, danach über die E-Mail-Adresse (teamweit, Gross-/Kleinschreibung egal). Wird ein bestehender aktiver Kunde gefunden, gibt die Antwort diesen mit created: false zurück und synchronisiert nur die mitgesendeten tag_uuids und sources. Nur bei einer echten Neuanlage ist created: true (HTTP 201). Kollidiert die E-Mail oder external_id mit einem Kunden im Papierkorb, antwortet der Endpunkt mit HTTP 409.

Request Body:

name string * Name des Kunden
email string * E-Mail-Adresse (dient als Find-or-Create-Schlüssel)
phone string Telefonnummer
source string Einzelne Herkunftsquelle als Freitext
external_id string Maschinen-Kennung für den Upsert-Abgleich (hat Vorrang vor der E-Mail)
whmcs_client_id integer Optionale WHMCS-Kunden-ID
address_line_1 string Adresszeile 1
address_line_2 string Adresszeile 2
postal_code string Postleitzahl
city string Ort
country string Land (ISO-Code oder Name)
contact_person string Ansprechpartner
tax_id string Steuernummer
vat_id string USt-IdNr. des Kunden (max. 32 Zeichen). Für E-Rechnungen mit Reverse Charge erforderlich
tax_treatment string Steuerbehandlung: standard (mit Umsatzsteuer, Vorgabe) | reverse_charge (Belege ohne Umsatzsteuer, Steuerschuld beim Empfänger)
tag_uuids array Liste team-eigener Tag-UUIDs, die dem Kunden zugeordnet werden
sources array Liste von Herkunftsquellen als Freitext
stripe_customer_id string Optionale Stripe-Kunden-ID (Präfix cus_), wird in die Zahlungszuordnung geschrieben

Kunde aktualisieren

PATCH /api/clients/{uuid}

Aktualisiert einen team-eigenen Kunden anhand seiner UUID. Nur explizit gesendete Felder werden geschrieben, ausgelassene Felder bleiben unverändert. Breaking Change: Der frühere numerische Pfad /api/clients/{id} wurde ersatzlos entfernt (HTTP 404); es gilt ausschließlich der UUID-Pfad.

Request Body:

name string Name des Kunden
contact_person string Ansprechpartner
phone string Telefonnummer
website string Webseite
address_line_1 string Adresszeile 1
address_line_2 string Adresszeile 2
postal_code string Postleitzahl
city string Ort
state string Bundesland / Region
country string Land
tax_id string Steuernummer
vat_id string USt-IdNr. des Kunden (max. 32 Zeichen). Für E-Rechnungen mit Reverse Charge erforderlich
tax_treatment string Steuerbehandlung: standard | reverse_charge

Kunde abrufen

GET /api/clients/{uuid}

Ruft einen einzelnen Kunden Ihres Teams über seine uuid ab. Unbekannte oder teamfremde Kunden führen zu 404.

Kunde nachschlagen

GET /api/clients/lookup

Exakte Suche nach genau einem aktiven Kunden, ohne jemals einen anzulegen. Mindestens einer der drei Parameter ist erforderlich. Bei mehreren Angaben gilt die feste Reihenfolge external_id, dann customer_number, dann email (ohne Beachtung der Groß- und Kleinschreibung).

Query-Parameter:

external_id string Externe Kennung des Kunden (max. 255)
customer_number string Kundennummer (max. 255)
email string E-Mail-Adresse (max. 255)

Kunde löschen (DSGVO)

DELETE /api/clients/{uuid}

Löscht den Kunden im Sinne der DSGVO. Aufbewahrungspflichtige Buchhaltungsbelege bleiben erhalten. Ein auf diesem Weg gelöschter Kunde lässt sich nicht wiederherstellen.

Query-Parameter:

include_export boolean Bei <code>true</code> enthält die Antwort zusätzlich den DSGVO-Datenexport des Kunden

Kunde wiederherstellen

POST /api/clients/{uuid}/restore

Stellt einen archivierten Kunden wieder her. Ein bereits aktiver Kunde wird unverändert zurückgegeben; die Antwort weist aus, ob eine Wiederherstellung stattgefunden hat. Nach DSGVO gelöschte Kunden sind davon ausgenommen.

📅 Timeline-Verwaltung

Einträge bestehen nur aus Text
Ein Timeline-Eintrag besteht aus content, Typ und Datum. Das frühere Feld title ist veraltet: Es wird beim Anlegen und Ändern weiter angenommen, aber nicht mehr als Titel gespeichert. Sendet ein Client beim Anlegen nur title, wird dieser Text als content übernommen. Einträge aus der Zeit davor liefern ihren alten Titel weiterhin im Feld title, neue Einträge eine leere Zeichenkette.
Einträge adressieren und bereinigen
Jeder Eintrag trägt eine uuid. Mit ihr wird er geändert oder gelöscht; editable sagt vorab, ob das mit dem verwendeten Schlüssel möglich ist. Wer denselben Eintrag mit demselben event_date erneut sendet, erhält den vorhandenen Eintrag mit duplicate: true zurück, statt eine Kopie anzulegen.

Timeline-Einträge auflisten

GET /api/clients/{uuid}/timeline

Ruft alle Timeline-Einträge für einen bestimmten Kunden chronologisch sortiert ab. Jeder Eintrag enthält seine uuid für Ändern und Löschen.

Timeline-Eintrag erstellen

POST /api/clients/{uuid}/timeline

Erstellt einen neuen Timeline-Eintrag für den angegebenen Kunden. Erfordert wie jede Timeline-Änderung das FlowCRM-Schreibrecht, sonst HTTP 403. Existiert bereits ein eigener Eintrag mit identischem Inhalt und identischem event_date, wird kein zweiter angelegt: Die Antwort ist dann HTTP 200 mit duplicate: true und dem vorhandenen Eintrag. Ohne event_date entsteht immer ein neuer Eintrag.

Request Body:

content string * Text des Eintrags. Ältere Clients, die nur title senden, bleiben gültig: Der Titel wird dann als Text übernommen.
event_date string Ereignisdatum (ISO 8601, Standard: jetzt)
title string Veraltet. Neben content wird er verworfen, allein gesendet wird er als content gespeichert.

Timeline-Eintrag ändern

PATCH /api/clients/{uuid}/timeline/{entry_uuid}

Korrigiert einen eigenen, manuell verfassten Eintrag. Einträge anderer Personen und automatisch erzeugte Einträge, etwa aus einer Besprechungszuordnung, antworten mit HTTP 403 und behalten ihre Quelle.

Request Body:

content string Neuer Inhalt
title string Veraltet. Wird angenommen und nicht gespeichert.
type string note, meeting, email, call, text, coffee, food, networking, party oder other
event_date string Neuer Zeitpunkt

Timeline-Eintrag löschen

DELETE /api/clients/{uuid}/timeline/{entry_uuid}

Entfernt einen eigenen, manuell verfassten Eintrag.

Mehrere Timeline-Einträge löschen

POST /api/clients/{uuid}/timeline/bulk-delete

Entfernt bis zu 200 eigene, manuell verfasste Einträge eines Kunden in einem Aufruf. Für jeden Eintrag gilt dieselbe Regel wie beim einzelnen Löschen. Fremde, automatisch erzeugte und unbekannte Einträge brechen den Aufruf nicht ab, sondern erscheinen mit ihrem Ergebnis in results.

Request Body:

entry_uuids array * Liste der zu löschenden Eintrags-UUIDs (1 bis 200, jede nur einmal)
GET /api/clients/{uuid}/timeline/search

Durchsucht die Timeline-Einträge eines Kunden nach Inhalt. Bei älteren Einträgen wird auch der frühere Titel durchsucht. Die Treffer haben dieselbe Form wie in der Liste.

Query-Parameter:

search string * Suchbegriff

👥 Kontakt-Verwaltung

Geteilte und private Kontakte
Ein Kontakt mit Teambindung (visibility: team) ist für alle Mitglieder mit der Unterberechtigung „kunden" sichtbar. Ein privater Kontakt (visibility: private) gehört nur dem anlegenden Benutzer und benötigt lediglich den FlowCRM-Modulzugriff. Wird ein privater Kontakt einem Kunden zugeordnet, wird er dadurch bewusst mit dem Team geteilt.

Kontakte auflisten

GET /api/contacts

Listet die Kontakte auf, die der verwendete Schlüssel sehen darf: geteilte Teamkontakte und die eigenen privaten Kontakte.

Query-Parameter:

search string Sucht in Vorname, Nachname, Firma sowie in hinterlegten E-Mail-Adressen und Rufnummern
client_uuid uuid Nur Kontakte des angegebenen Kunden
status string <code>active</code> (Standard), <code>archived</code> oder <code>all</code>
visibility string <code>team</code> oder <code>private</code>; ohne Angabe beides
per_page integer Einträge pro Seite (1 bis 200, Standard 50)
page integer Seitennummer, beginnend bei 1

Kontakt anlegen

POST /api/contacts

Legt einen Kontakt an. Mindestens Vor- oder Nachname ist erforderlich. Wird client_uuid gesetzt, entsteht direkt ein Teamkontakt beim angegebenen Kunden.

Request Body:

first_name string Vorname
last_name string Nachname
salutation string Herr, Frau, Divers, Firma oder keine
title string Titel, etwa Dr. oder Prof.
job_title string Position im Unternehmen
company string Firmenname
description string Freitext zur Person
birth_day integer Geburtstag, 1 bis 31
birth_month integer Geburtsmonat, 1 bis 12
birth_year integer Geburtsjahr
client_uuid uuid Kunde, zu dem der Kontakt gehört
visibility string <code>team</code> (Standard) oder <code>private</code>

Kontakt abrufen

GET /api/contacts/{uuid}

Liefert einen Kontakt mit allen Wertesammlungen: E-Mail-Adressen, Rufnummern, Anschriften, Links, Profile, Daten und Freifelder.

Kontakt aktualisieren

PATCH /api/contacts/{uuid}

Ändert nur die gesendeten Felder. Über status lässt sich ein Kontakt archivieren und wieder aktivieren. Vor- und Nachname dürfen nicht gleichzeitig leer werden.

Request Body:

first_name string Vorname
last_name string Nachname
salutation string Herr, Frau, Divers, Firma oder keine
title string Titel
job_title string Position im Unternehmen
company string Firmenname
description string Freitext zur Person
birth_day integer Geburtstag
birth_month integer Geburtsmonat
birth_year integer Geburtsjahr
status string <code>active</code> oder <code>archived</code>

Kontakt löschen

DELETE /api/contacts/{uuid}

Verschiebt den Kontakt in den Papierkorb, genau wie die Kontaktliste in der Oberfläche. Wer den Kontakt nur vorübergehend ausblenden möchte, setzt stattdessen status: archived.

Kontakt einem Kunden zuordnen

PUT /api/contacts/{uuid}/client

Ordnet den Kontakt einem Kunden zu oder löst die Zuordnung mit client_uuid: null. Ein bisher privater Kontakt wird dabei bewusst mit dem Team geteilt. Ist beim Kunden noch kein Ansprechpartner hinterlegt, übernimmt er den Namen dieses Kontakts.

Request Body:

client_uuid uuid * Kunde, oder <code>null</code> zum Lösen der Zuordnung

Kontaktwert hinzufügen

POST /api/contacts/{uuid}/values/{collection}

Fügt genau einen Wert einer Sammlung hinzu. Mögliche Sammlungen: emails, phones, addresses, links, socials, dates, customs. Pro Sammlung kann höchstens ein Wert is_primary sein; ein neuer Hauptwert löst den bisherigen ab.

Request Body:

label string * Bezeichnung, etwa Arbeit oder Privat. Bei <code>socials</code> stattdessen <code>platform</code>
email string Nur bei <code>emails</code>
phone string Nur bei <code>phones</code>
url string Nur bei <code>links</code> und <code>socials</code>
date string Nur bei <code>dates</code>, im Format JJJJ-MM-TT
value string Nur bei <code>customs</code>
is_primary boolean Nur bei <code>emails</code>, <code>phones</code> und <code>addresses</code>

Kontaktwert ändern

PATCH /api/contacts/{uuid}/values/{collection}/{value_uuid}

Ändert genau einen Wert. Es müssen nur die tatsächlich geänderten Felder gesendet werden. Bei Anschriften werden hinterlegte Koordinaten verworfen, sobald sich ein Adressfeld ändert.

Request Body:

label string Bezeichnung des Werts
is_primary boolean Macht den Wert zum Hauptwert der Sammlung

Kontaktwert entfernen

DELETE /api/contacts/{uuid}/values/{collection}/{value_uuid}

Entfernt genau einen Wert aus der angegebenen Sammlung.

📁 Projekt-Verwaltung

Berechtigung und Archivierung
Projekte erfordern zusätzlich zum FlowCRM-Modul die Unterberechtigung „projekte". Ein Projekt wird nie gelöscht: DELETE archiviert es, weil Zeiterfassung, Rechnungen und Aufgabenboards weiter darauf verweisen. Ein PATCH mit status: active holt es zurück.

Kundenprojekte auflisten

GET /api/clients/{uuid}/projects

Listet alle Projekte des angegebenen Kunden auf, inklusive Status, Beschreibung und einem modules-Array mit aktiven Integrationen (flowcall, flowtime, flowtasks).

Alle Projekte auflisten

GET /api/projects

Listet die Projekte des Teams über alle Kunden hinweg.

Query-Parameter:

client_uuid uuid Nur Projekte des angegebenen Kunden
search string Sucht im Projektnamen
status string <code>active</code> (Standard), <code>archived</code> oder <code>all</code>
per_page integer Einträge pro Seite (1 bis 200, Standard 50)
page integer Seitennummer, beginnend bei 1

Projekt anlegen

POST /api/projects

Legt ein Projekt beim angegebenen Kunden an.

Request Body:

name string * Name des Projekts
client_uuid uuid * Kunde, zu dem das Projekt gehört
description string Beschreibung
notes string Interne Notizen
budget_type string <code>hours</code> oder <code>amount</code>
budget_value number Budgetwert
budget_currency string Währung als dreistelliger Code
budget_interval string <code>total</code>, <code>monthly</code> oder <code>daily</code>
budget_enforcement string <code>soft</code> oder <code>hard</code>

Projekt beim Kunden anlegen

POST /api/clients/{uuid}/projects

Wie „Projekt anlegen", der Kunde steht jedoch bereits im Pfad und muss nicht im Rumpf wiederholt werden.

Request Body:

name string * Name des Projekts
description string Beschreibung
notes string Interne Notizen

Projekt abrufen

GET /api/projects/{uuid}

Liefert ein einzelnes Projekt samt Budgetangaben und aktiven Modulen.

Projekt aktualisieren

PATCH /api/projects/{uuid}

Ändert nur die gesendeten Felder. Über client_uuid lässt sich ein Projekt einem anderen Kunden desselben Teams zuordnen, über status archivieren und wieder aktivieren.

Request Body:

name string Name des Projekts
description string Beschreibung
notes string Interne Notizen
client_uuid uuid Neuer Kunde des Projekts
status string <code>active</code> oder <code>archived</code>
budget_type string <code>hours</code> oder <code>amount</code>
budget_value number Budgetwert
budget_currency string Währung als dreistelliger Code
budget_interval string <code>total</code>, <code>monthly</code> oder <code>daily</code>
budget_enforcement string <code>soft</code> oder <code>hard</code>

Projekt archivieren

DELETE /api/projects/{uuid}

Archiviert das Projekt. Es bleibt erhalten, weil Zeiterfassung, Rechnungen und Aufgabenboards weiter darauf verweisen.

🗂️ Projekt-Timeline

Projekt-Timeline abrufen

GET /api/clients/{uuid}/projects/{project_uuid}/timeline

Ruft alle Timeline-Einträge für ein bestimmtes Projekt ab, analog zur Kunden-Timeline und in derselben Form, einschließlich uuid je Eintrag.

Projekt-Timeline-Eintrag erstellen

POST /api/clients/{uuid}/projects/{project_uuid}/timeline

Hängt einen Eintrag an die Projekt-Timeline an. Verfasser und Team ergeben sich aus dem Schlüssel und können nicht im Rumpf gesetzt werden. Ein eigener Eintrag mit identischem Inhalt, Typ und event_date wird nicht doppelt angelegt; die Antwort ist dann HTTP 200 mit duplicate: true.

Request Body:

content string * Inhalt des Eintrags
title string Veraltet. Wird angenommen und nicht gespeichert.
type string note (Standard), meeting, email, call, text, coffee, food, networking, party oder other
event_date string Zeitpunkt des Ereignisses; ohne Angabe jetzt

Projekt-Timeline-Eintrag ändern

PATCH /api/clients/{uuid}/projects/{project_uuid}/timeline/{entry_uuid}

Korrigiert einen eigenen, manuell verfassten Eintrag. Einträge anderer Personen und automatisch erzeugte Einträge antworten mit HTTP 403.

Request Body:

content string Neuer Inhalt
title string Veraltet. Wird angenommen und nicht gespeichert.
type string Neuer Typ
event_date string Neuer Zeitpunkt

Projekt-Timeline-Eintrag löschen

DELETE /api/clients/{uuid}/projects/{project_uuid}/timeline/{entry_uuid}

Entfernt einen eigenen, manuell verfassten Eintrag.

Mehrere Projekt-Timeline-Einträge löschen

POST /api/clients/{uuid}/projects/{project_uuid}/timeline/bulk-delete

Entfernt bis zu 200 eigene, manuell verfasste Einträge eines Projekts in einem Aufruf. Regeln und Antwort entsprechen dem Sammel-Löschen der Kunden-Timeline.

Request Body:

entry_uuids array * Liste der zu löschenden Eintrags-UUIDs (1 bis 200, jede nur einmal)

🗃️ Aufgaben, Metadaten und Dateien

Gleiche Endpunkte auf drei Datensatzarten
Aufgaben, Metadaten und Dateien gibt es in identischer Form unter /api/clients, /api/contacts und /api/projects. Projekte kennen keine Tags und keine Quellen; ein solcher Versuch antwortet mit HTTP 422. Private Kontakte nehmen keine CRM-Aufgaben auf.
Dateien kommen als Upload
Dateien werden ausschließlich als multipart/form-data übertragen. Es gibt bewusst keinen Weg, dem Server eine Adresse zum Herunterladen zu nennen. Der Rückweg läuft über einen kurzlebigen Abruflink, damit große Dateien nicht zweimal durch die Anwendung wandern.

Aufgaben auflisten (Kunden)

GET /api/clients/{uuid}/todos

Listet die Aufgaben des Datensatzes auf. Erledigte Aufgaben erscheinen nur mit include_completed=1.

Query-Parameter:

include_completed boolean Erledigte Aufgaben mit ausgeben
per_page integer Einträge pro Seite (1 bis 200, Standard 50)
page integer Seitennummer, beginnend bei 1

Aufgabe anlegen (Kunden)

POST /api/clients/{uuid}/todos

Legt eine Aufgabe am Datensatz an. Zuständige werden über ihre Benutzer-UUID benannt und müssen zum Team des Schlüssels gehören.

Request Body:

title string * Titel der Aufgabe
description string Beschreibung
due_date string Fälligkeit im Format JJJJ-MM-TT
priority string <code>high</code>, <code>medium</code> (Standard) oder <code>low</code>
completed boolean Direkt als erledigt anlegen
assigned_user_uuids array Benutzer-UUIDs der Zuständigen

Aufgabe aktualisieren (Kunden)

PATCH /api/clients/{uuid}/todos/{todo_uuid}

Ändert nur die gesendeten Felder. Über completed wird eine Aufgabe erledigt oder wieder geöffnet. Ein weggelassenes assigned_user_uuids lässt die Zuständigkeit unverändert, ein leeres Feld hebt sie auf.

Request Body:

title string Titel der Aufgabe
description string Beschreibung
due_date string Fälligkeit im Format JJJJ-MM-TT
priority string <code>high</code>, <code>medium</code> oder <code>low</code>
completed boolean Erledigt-Zustand
assigned_user_uuids array Benutzer-UUIDs der Zuständigen

Aufgabe löschen (Kunden)

DELETE /api/clients/{uuid}/todos/{todo_uuid}

Entfernt die Aufgabe.

Metadaten abrufen (Kunden)

GET /api/clients/{uuid}/metadata

Liefert Tags, Quellen und Zusatzfelder des Datensatzes sowie die im Team verfügbaren Tags und Zusatzfeld-Definitionen. Zusatzfelder vom Typ reference werden mit der UUID und dem Namen des Ziels beantwortet.

Metadaten ändern (Kunden)

PATCH /api/clients/{uuid}/metadata

Ergänzt oder entfernt gezielt einzelne Tags, Quellen und Zusatzfeldwerte. Nichts wird als Ganzes ersetzt, ein leerer Wert löscht das jeweilige Zusatzfeld. Zusatzfelder werden über definition_uuid oder ihren eindeutigen name benannt; bei Referenzfeldern ist der Wert die UUID des Ziels.

Request Body:

add_tag_uuids array Tags, die ergänzt werden
remove_tag_uuids array Tags, die entfernt werden
add_sources array Quellen, die ergänzt werden
remove_sources array Quellen, die entfernt werden
custom_fields array Einträge aus <code>definition_uuid</code> oder <code>name</code> und <code>value</code>

Dateien auflisten (Kunden)

GET /api/clients/{uuid}/files

Listet die Dateien des Datensatzes auf. has_document_source zeigt an, ob die Datei aus Markdown erzeugt wurde und damit überarbeitet statt ersetzt werden kann.

Query-Parameter:

per_page integer Einträge pro Seite (1 bis 200, Standard 50)
page integer Seitennummer, beginnend bei 1

Datei hochladen (Kunden)

POST /api/clients/{uuid}/files

Hängt eine hochgeladene Datei an den Datensatz. Übertragung als multipart/form-data, höchstens 50 MB. Nicht zugelassene Dateitypen antworten mit HTTP 422.

Request Body:

file file * Die hochzuladende Datei
name string Abweichender Dateiname

Abruflink für eine Datei (Kunden)

GET /api/clients/{uuid}/files/{file_uuid}/download

Liefert einen kurzlebigen Abruflink samt Gültigkeitsdauer in Sekunden. Die Antwort enthält den Link, nicht die Bytes.

Text einer Datei lesen (Kunden)

GET /api/clients/{uuid}/files/{file_uuid}/text

Gibt den bereits extrahierten Text der Datei in Abschnitten von 20.000 Zeichen zurück. Der Endpunkt liest nur; er stößt keine neue Extraktion an.

Query-Parameter:

offset integer Startposition im Text; <code>next_offset</code> der Antwort führt zum nächsten Abschnitt

Dokument aus Markdown erzeugen (Kunden)

POST /api/clients/{uuid}/documents

Erzeugt aus Markdown ein PDF oder ein Word-Dokument und legt es am Datensatz ab. Mit file_uuid wird eine bestehende, ebenfalls so erzeugte Fassung überarbeitet statt eine zweite daneben zu legen.

Request Body:

title string * Titel und zugleich Dateiname ohne Endung
source_markdown string * Der Inhalt als Markdown
format string * <code>pdf</code> oder <code>docx</code>
file_uuid uuid Bestehende Fassung, die ersetzt wird

Aufgaben auflisten (Kontakte)

GET /api/contacts/{uuid}/todos

Listet die Aufgaben des Datensatzes auf. Erledigte Aufgaben erscheinen nur mit include_completed=1.

Query-Parameter:

include_completed boolean Erledigte Aufgaben mit ausgeben
per_page integer Einträge pro Seite (1 bis 200, Standard 50)
page integer Seitennummer, beginnend bei 1

Aufgabe anlegen (Kontakte)

POST /api/contacts/{uuid}/todos

Legt eine Aufgabe am Datensatz an. Zuständige werden über ihre Benutzer-UUID benannt und müssen zum Team des Schlüssels gehören.

Request Body:

title string * Titel der Aufgabe
description string Beschreibung
due_date string Fälligkeit im Format JJJJ-MM-TT
priority string <code>high</code>, <code>medium</code> (Standard) oder <code>low</code>
completed boolean Direkt als erledigt anlegen
assigned_user_uuids array Benutzer-UUIDs der Zuständigen

Aufgabe aktualisieren (Kontakte)

PATCH /api/contacts/{uuid}/todos/{todo_uuid}

Ändert nur die gesendeten Felder. Über completed wird eine Aufgabe erledigt oder wieder geöffnet. Ein weggelassenes assigned_user_uuids lässt die Zuständigkeit unverändert, ein leeres Feld hebt sie auf.

Request Body:

title string Titel der Aufgabe
description string Beschreibung
due_date string Fälligkeit im Format JJJJ-MM-TT
priority string <code>high</code>, <code>medium</code> oder <code>low</code>
completed boolean Erledigt-Zustand
assigned_user_uuids array Benutzer-UUIDs der Zuständigen

Aufgabe löschen (Kontakte)

DELETE /api/contacts/{uuid}/todos/{todo_uuid}

Entfernt die Aufgabe.

Metadaten abrufen (Kontakte)

GET /api/contacts/{uuid}/metadata

Liefert Tags, Quellen und Zusatzfelder des Datensatzes sowie die im Team verfügbaren Tags und Zusatzfeld-Definitionen. Zusatzfelder vom Typ reference werden mit der UUID und dem Namen des Ziels beantwortet.

Metadaten ändern (Kontakte)

PATCH /api/contacts/{uuid}/metadata

Ergänzt oder entfernt gezielt einzelne Tags, Quellen und Zusatzfeldwerte. Nichts wird als Ganzes ersetzt, ein leerer Wert löscht das jeweilige Zusatzfeld. Zusatzfelder werden über definition_uuid oder ihren eindeutigen name benannt; bei Referenzfeldern ist der Wert die UUID des Ziels.

Request Body:

add_tag_uuids array Tags, die ergänzt werden
remove_tag_uuids array Tags, die entfernt werden
add_sources array Quellen, die ergänzt werden
remove_sources array Quellen, die entfernt werden
custom_fields array Einträge aus <code>definition_uuid</code> oder <code>name</code> und <code>value</code>

Dateien auflisten (Kontakte)

GET /api/contacts/{uuid}/files

Listet die Dateien des Datensatzes auf. has_document_source zeigt an, ob die Datei aus Markdown erzeugt wurde und damit überarbeitet statt ersetzt werden kann.

Query-Parameter:

per_page integer Einträge pro Seite (1 bis 200, Standard 50)
page integer Seitennummer, beginnend bei 1

Datei hochladen (Kontakte)

POST /api/contacts/{uuid}/files

Hängt eine hochgeladene Datei an den Datensatz. Übertragung als multipart/form-data, höchstens 50 MB. Nicht zugelassene Dateitypen antworten mit HTTP 422.

Request Body:

file file * Die hochzuladende Datei
name string Abweichender Dateiname

Abruflink für eine Datei (Kontakte)

GET /api/contacts/{uuid}/files/{file_uuid}/download

Liefert einen kurzlebigen Abruflink samt Gültigkeitsdauer in Sekunden. Die Antwort enthält den Link, nicht die Bytes.

Text einer Datei lesen (Kontakte)

GET /api/contacts/{uuid}/files/{file_uuid}/text

Gibt den bereits extrahierten Text der Datei in Abschnitten von 20.000 Zeichen zurück. Der Endpunkt liest nur; er stößt keine neue Extraktion an.

Query-Parameter:

offset integer Startposition im Text; <code>next_offset</code> der Antwort führt zum nächsten Abschnitt

Dokument aus Markdown erzeugen (Kontakte)

POST /api/contacts/{uuid}/documents

Erzeugt aus Markdown ein PDF oder ein Word-Dokument und legt es am Datensatz ab. Mit file_uuid wird eine bestehende, ebenfalls so erzeugte Fassung überarbeitet statt eine zweite daneben zu legen.

Request Body:

title string * Titel und zugleich Dateiname ohne Endung
source_markdown string * Der Inhalt als Markdown
format string * <code>pdf</code> oder <code>docx</code>
file_uuid uuid Bestehende Fassung, die ersetzt wird

Aufgaben auflisten (Projekte)

GET /api/projects/{uuid}/todos

Listet die Aufgaben des Datensatzes auf. Erledigte Aufgaben erscheinen nur mit include_completed=1.

Query-Parameter:

include_completed boolean Erledigte Aufgaben mit ausgeben
per_page integer Einträge pro Seite (1 bis 200, Standard 50)
page integer Seitennummer, beginnend bei 1

Aufgabe anlegen (Projekte)

POST /api/projects/{uuid}/todos

Legt eine Aufgabe am Datensatz an. Zuständige werden über ihre Benutzer-UUID benannt und müssen zum Team des Schlüssels gehören.

Request Body:

title string * Titel der Aufgabe
description string Beschreibung
due_date string Fälligkeit im Format JJJJ-MM-TT
priority string <code>high</code>, <code>medium</code> (Standard) oder <code>low</code>
completed boolean Direkt als erledigt anlegen
assigned_user_uuids array Benutzer-UUIDs der Zuständigen

Aufgabe aktualisieren (Projekte)

PATCH /api/projects/{uuid}/todos/{todo_uuid}

Ändert nur die gesendeten Felder. Über completed wird eine Aufgabe erledigt oder wieder geöffnet. Ein weggelassenes assigned_user_uuids lässt die Zuständigkeit unverändert, ein leeres Feld hebt sie auf.

Request Body:

title string Titel der Aufgabe
description string Beschreibung
due_date string Fälligkeit im Format JJJJ-MM-TT
priority string <code>high</code>, <code>medium</code> oder <code>low</code>
completed boolean Erledigt-Zustand
assigned_user_uuids array Benutzer-UUIDs der Zuständigen

Aufgabe löschen (Projekte)

DELETE /api/projects/{uuid}/todos/{todo_uuid}

Entfernt die Aufgabe.

Metadaten abrufen (Projekte)

GET /api/projects/{uuid}/metadata

Liefert Tags, Quellen und Zusatzfelder des Datensatzes sowie die im Team verfügbaren Tags und Zusatzfeld-Definitionen. Zusatzfelder vom Typ reference werden mit der UUID und dem Namen des Ziels beantwortet.

Metadaten ändern (Projekte)

PATCH /api/projects/{uuid}/metadata

Ergänzt oder entfernt gezielt einzelne Tags, Quellen und Zusatzfeldwerte. Nichts wird als Ganzes ersetzt, ein leerer Wert löscht das jeweilige Zusatzfeld. Zusatzfelder werden über definition_uuid oder ihren eindeutigen name benannt; bei Referenzfeldern ist der Wert die UUID des Ziels.

Request Body:

add_tag_uuids array Tags, die ergänzt werden
remove_tag_uuids array Tags, die entfernt werden
add_sources array Quellen, die ergänzt werden
remove_sources array Quellen, die entfernt werden
custom_fields array Einträge aus <code>definition_uuid</code> oder <code>name</code> und <code>value</code>

Dateien auflisten (Projekte)

GET /api/projects/{uuid}/files

Listet die Dateien des Datensatzes auf. has_document_source zeigt an, ob die Datei aus Markdown erzeugt wurde und damit überarbeitet statt ersetzt werden kann.

Query-Parameter:

per_page integer Einträge pro Seite (1 bis 200, Standard 50)
page integer Seitennummer, beginnend bei 1

Datei hochladen (Projekte)

POST /api/projects/{uuid}/files

Hängt eine hochgeladene Datei an den Datensatz. Übertragung als multipart/form-data, höchstens 50 MB. Nicht zugelassene Dateitypen antworten mit HTTP 422.

Request Body:

file file * Die hochzuladende Datei
name string Abweichender Dateiname

Abruflink für eine Datei (Projekte)

GET /api/projects/{uuid}/files/{file_uuid}/download

Liefert einen kurzlebigen Abruflink samt Gültigkeitsdauer in Sekunden. Die Antwort enthält den Link, nicht die Bytes.

Text einer Datei lesen (Projekte)

GET /api/projects/{uuid}/files/{file_uuid}/text

Gibt den bereits extrahierten Text der Datei in Abschnitten von 20.000 Zeichen zurück. Der Endpunkt liest nur; er stößt keine neue Extraktion an.

Query-Parameter:

offset integer Startposition im Text; <code>next_offset</code> der Antwort führt zum nächsten Abschnitt

Dokument aus Markdown erzeugen (Projekte)

POST /api/projects/{uuid}/documents

Erzeugt aus Markdown ein PDF oder ein Word-Dokument und legt es am Datensatz ab. Mit file_uuid wird eine bestehende, ebenfalls so erzeugte Fassung überarbeitet statt eine zweite daneben zu legen.

Request Body:

title string * Titel und zugleich Dateiname ohne Endung
source_markdown string * Der Inhalt als Markdown
format string * <code>pdf</code> oder <code>docx</code>
file_uuid uuid Bestehende Fassung, die ersetzt wird

🎯 Leads-Verwaltung

Berechtigung
Alle Leads-Endpunkte erfordern den FlowCRM-Modulzugriff plus die Leads-Unterberechtigung Ihres Teams.
Idempotentes Anlegen
Senden Sie beim Anlegen ein Paar aus external_source und external_id. Ein zweiter POST mit demselben Paar aktualisiert den bestehenden Lead (Upsert), statt ein Duplikat zu erzeugen. Dabei werden nur die tatsächlich gesendeten Felder überschrieben.

Leads sind team-eigen: team_id und status werden serverseitig gesetzt und nie aus dem Request übernommen. Ein neuer Lead startet im Standard-Status des Teams. Der Status wird ausschliesslich über den eigenen Status-Endpunkt geändert, der Systemstatus converted nur über die Konvertierung.

Idempotenz: Trifft ein POST über external_source + external_id auf einen bestehenden Lead, werden nur die im Payload enthaltenen Felder aktualisiert (Merge). Nicht gesendete Felder bleiben unberührt. Ein solcher Wiederholungs-POST liefert created: false. Das Roh-Payload wird als source_payload gespeichert, erscheint aber nie in der Antwort.

Duplikatwarnung: Wird beim Anlegen ein mögliches Duplikat erkannt, wird der Lead trotzdem angelegt und die Antwort trägt ein Feld duplicate_warning mit den Treffern. Ohne Treffer ist das Feld null.

Leads auflisten

GET /api/leads

Listet die Leads des Teams paginiert auf. Standardmässig wird nur die offene Pipeline gezeigt (abgeschlossene und konvertierte Leads ausgeblendet); mit pipeline=false wird der volle Satz zurückgegeben. Die Antwort folgt dem Standard-Paginierungsformat mit data, links und meta.

Query-Parameter:

pipeline boolean Standard true (nur offene Pipeline). false zeigt alle Leads inkl. abgeschlossener
search string Volltextsuche über Firmenname, Vor-/Nachname und E-Mail
source string Filter auf eine Quelle oder mehrere per Komma getrennt
tag_uuid string Filter auf eine team-eigene Tag-UUID
assigned_user_uuid string Filter auf die UUID des zugewiesenen Benutzers. Eine UUID ausserhalb des Teams liefert eine leere Liste. Breaking Change: assigned_user_id wird nicht mehr akzeptiert (422)
created_from string Erstellt ab (Datum, YYYY-MM-DD)
created_to string Erstellt bis (Datum, YYYY-MM-DD)
updated_from string Geändert ab (Datum, YYYY-MM-DD)
updated_to string Geändert bis (Datum, YYYY-MM-DD)
per_page integer Einträge pro Seite (Standard 50, Maximum 100)
include string Zusätzliche Relationen, per Komma: contacts, sources, tags, status, board_cards, assigned_user, notes, custom_fields

Lead anlegen (Upsert)

POST /api/leads

Legt einen Lead an oder aktualisiert per external_source + external_id idempotent einen bestehenden (Merge nur der gesendeten Felder). Der Status kann hier nie gesetzt werden; ein neuer Lead startet im Standard-Status. Ein optionales doc-Objekt befüllt beim erstmaligen Anlegen das FlowDoc des Leads, nicht bei einem idempotenten Wiederholungs-POST. Das frühere docs[]-Array wird nicht mehr akzeptiert (422), weil ein Lead genau ein FlowDoc hat. Die Antwort trägt created (bool) und duplicate_warning (Objekt mit matches oder null). HTTP 201 bei Neuanlage, 200 bei idempotenter Aktualisierung.

Request Body:

type string * company oder person
company_name string Firmenname (Pflicht wenn type=company)
first_name string Vorname (Pflicht wenn type=person)
last_name string Nachname
email string E-Mail-Adresse
phone string Telefonnummer
website string Webseite
address_line_1 string Adresszeile 1
address_line_2 string Adresszeile 2
postal_code string Postleitzahl
city string Ort
state string Bundesland / Region
country string Land (ISO-Code)
country_display string Anzeigename des Landes
deal_value number Potenzieller Auftragswert
deal_currency string Währung (3-stelliger ISO-Code)
notes string Freitext-Notiz am Lead
external_source string Quellsystem-Kennung, Teil des Idempotenzschlüssels
external_id string Datensatz-Kennung im Quellsystem, Teil des Idempotenzschlüssels
source_payload object Roh-Payload der Quelle. Wird gespeichert, erscheint aber nie in der Antwort
sources array Liste von Herkunftsquellen als Freitext (max. 20 Einträge)
tag_uuids array Liste team-eigener Tag-UUIDs (max. 50 Einträge)
assigned_user_uuid string UUID eines Team-Mitglieds als zugewiesener Benutzer. Benutzer außerhalb des Teams werden mit 422 abgelehnt. Breaking Change: assigned_user_id wird nicht mehr akzeptiert (Validierungsfehler)
contacts array Ansprechpartner-Objekte (first_name, last_name, email, phone, role; max. 50 Einträge)
custom_fields array Custom-Field-Werte (definition_uuid oder name, plus value). Bei Feldern vom Typ reference ist value die UUID des Ziels (Kunde, Kontakt, Projekt oder Team-Mitglied), zum Beispiel {"definition_uuid": "...", "value": "3f7c1a02-9b64-4d1e-a5c8-2e7b90d41f6a"}. Breaking Change: definition_id wird nicht mehr akzeptiert, und eine numerische Ziel-ID in value wird bei Referenzfeldern ebenfalls mit 422 abgelehnt
doc object Das FlowDoc des Leads, nur bei Neuanlage. Einziges Feld ist content als Markdown-String; ein Titel ist nicht vorgesehen. Breaking Change: das frühere docs[]-Array wird mit 422 abgelehnt

Lead abrufen

GET /api/leads/{uuid}

Ruft einen einzelnen team-eigenen Lead per UUID ab. Status, Quellen, Tags, Ansprechpartner und zugewiesener Benutzer sind enthalten. Custom-Field-Werte nur mit include=custom_fields; Referenzfelder werden ausschließlich per UUID adressiert.

Query-Parameter:

include string custom_fields, um die Custom-Field-Werte mitzuladen. Referenzfelder liefern die UUID des Ziels in value und referenced_uuid sowie den Anzeigenamen in referenced_name; ist das Ziel nicht mehr vorhanden, ist referenced_uuid null

Lead aktualisieren

PATCH /api/leads/{uuid}

Aktualisiert die Kernfelder eines team-eigenen Leads (Teil-Update, nur gesendete Felder). Der Status kann hier nicht gesetzt werden (status und status_id werden mit 422 abgelehnt), dafür dient der Status-Endpunkt. Die Antwort trägt zusätzlich duplicate_warning.

Request Body:

type string company oder person
company_name string Firmenname
first_name string Vorname
last_name string Nachname
email string E-Mail-Adresse
phone string Telefonnummer
website string Webseite
address_line_1 string Adresszeile 1
address_line_2 string Adresszeile 2
postal_code string Postleitzahl
city string Ort
state string Bundesland / Region
country string Land (ISO-Code)
country_display string Anzeigename des Landes
deal_value number Potenzieller Auftragswert
deal_currency string Währung (3-stelliger ISO-Code)
notes string Freitext-Notiz
external_source string Quellsystem-Kennung
external_id string Datensatz-Kennung im Quellsystem
source_payload object Roh-Payload der Quelle. Wird gespeichert, erscheint aber nie in der Antwort
sources array Herkunftsquellen, additiv (fügt nur hinzu, entfernt nie; zum Ersetzen dient PUT /sources; max. 20 Einträge)
tag_uuids array Team-eigene Tag-UUIDs, additiv (zum Ersetzen dient PUT /tags; max. 50 Einträge)
assigned_user_uuid string UUID eines Team-Mitglieds als zugewiesener Benutzer; null entfernt die Zuweisung. Benutzer außerhalb des Teams werden mit 422 abgelehnt. Breaking Change: assigned_user_id wird nicht mehr akzeptiert (Validierungsfehler)
contacts array Ansprechpartner-Objekte (first_name, last_name, email, phone, role), additiv: legt neue Ansprechpartner an, verändert oder löscht keine bestehenden (dafür die /contacts-Endpunkte; max. 50 Einträge)
custom_fields array Custom-Field-Werte (definition_uuid oder name, plus value). Bei Feldern vom Typ reference ist value die UUID des Ziels (Kunde, Kontakt, Projekt oder Team-Mitglied). Breaking Change: definition_id wird nicht mehr akzeptiert, und eine numerische Ziel-ID in value wird bei Referenzfeldern ebenfalls mit 422 abgelehnt

Lead löschen

DELETE /api/leads/{uuid}

Löscht einen team-eigenen Lead (Soft-Delete).

🔗 Lead-Beziehungen

Sync ersetzt, Notizen sind append-only
PUT auf Quellen und Tags ist ein voller Sync: die gesendete Liste ersetzt die gespeicherte (ausgelassene Werte werden entfernt, ein leeres Array leert die Relation). Notizen sind unveränderlich, es gibt bewusst kein PATCH oder DELETE.

Alle Endpunkte sind team-scoped: eine fremde Lead-UUID ergibt 404. Fremd-Team-Tag-UUIDs werden als ungültige Auswahl (422) abgelehnt. Die Autorenschaft einer Notiz stammt immer vom Token-Benutzer und kann nicht mitgesendet werden.

Quellen synchronisieren

PUT /api/leads/{uuid}/sources

Voller Sync der Quellen des Leads: die gesendete Liste ersetzt die gespeicherte. Ein leeres Array leert die Quellen. Trimmen, Leer-Entfernen und Deduplizieren übernimmt der Server.

Request Body:

sources array * Vollständige Liste der Quellen (Freitext, max. 50 Einträge). Muss vorhanden sein; leeres Array leert die Quellen

Tags synchronisieren

PUT /api/leads/{uuid}/tags

Voller Sync der Tags des Leads über team-eigene Tag-UUIDs: die gesendete Liste ersetzt die gespeicherte. Behaltene Tags behalten ihre Pivot-Quelle, neu angehängte erhalten source=api, ausgelassene werden entfernt. Ein leeres Array entfernt alle Tags. Eine Fremd-Team-UUID wird mit 422 abgelehnt.

Request Body:

tag_uuids array * Vollständige Liste team-eigener Tag-UUIDs (max. 50 Einträge). Muss vorhanden sein; leeres Array entfernt alle Tags

Ansprechpartner auflisten

GET /api/leads/{uuid}/contacts

Listet die Ansprechpartner eines team-eigenen Leads auf.

Ansprechpartner anlegen

POST /api/leads/{uuid}/contacts

Legt einen Ansprechpartner am Lead an. Nur first_name ist Pflicht.

Request Body:

first_name string * Vorname
last_name string Nachname
email string E-Mail-Adresse
phone string Telefonnummer
role string Rolle / Funktion

Ansprechpartner aktualisieren

PATCH /api/leads/{uuid}/contacts/{contactUuid}

Aktualisiert einen Ansprechpartner (Teil-Update). first_name darf, wenn gesendet, nicht leer sein. Der Ansprechpartner wird per UUID innerhalb des Leads adressiert; numerische IDs werden nicht mehr akzeptiert (HTTP 404).

Request Body:

first_name string Vorname (darf nicht geleert werden)
last_name string Nachname
email string E-Mail-Adresse
phone string Telefonnummer
role string Rolle / Funktion

Ansprechpartner entfernen

DELETE /api/leads/{uuid}/contacts/{contactUuid}

Entfernt einen Ansprechpartner des Leads.

Notizen auflisten

GET /api/leads/{uuid}/notes

Listet die Notizen des Leads auf, neueste zuerst. Der Autor wird als Objekt (uuid, name) mitgeliefert.

Notiz anhängen

POST /api/leads/{uuid}/notes

Hängt eine Notiz an den Lead an. Notizen sind append-only, es gibt kein Bearbeiten oder Löschen. Der Autor ist immer der Token-Benutzer.

Request Body:

body string * Notizinhalt (max. 65535 Zeichen)

🚦 Lead-Status

converted ist nicht setzbar
Der Systemstatus converted kann über diesen Endpunkt nicht gesetzt werden (HTTP 422). Er entsteht ausschliesslich über die Konvertierung. Andere Status (auch gewonnen/verloren) sind normal setzbar.

Der Status wird ausschliesslich über seine team-eigene UUID gesetzt, nie über einen Schlüssel oder eine numerische id. Eine Fremd-Team-Status-UUID wird als ungültige Auswahl (422) abgelehnt. Der Schreibvorgang läuft über denselben Pfad wie die Detailseite, sodass derselbe Timeline-Eintrag geschrieben und das Board synchronisiert wird.

Status setzen

PUT /api/leads/{uuid}/status

Setzt den Status eines team-eigenen Leads auf einen team-eigenen Status, adressiert per Status-UUID. converted wird mit 422 abgelehnt; eine UUID, die die Validierung passiert, aber keinen auffindbaren Status trifft, liefert 404.

Request Body:

status_uuid string * UUID eines team-eigenen Lead-Status

📆 Lead-Timeline

Die Lead-Timeline spiegelt die Kunden-Timeline: GET ist paginiert (neueste zuerst), POST hängt einen Eintrag an. Über die API angelegte Einträge bleiben in der bestehenden Timeline sichtbar und fliessen nach einer Konvertierung in die Kunden-Timeline. Ein konvertierter Lead ist schreibgeschützt: ein POST wird mit HTTP 409 abgelehnt.

Timeline abrufen

GET /api/leads/{uuid}/timeline

Ruft die Timeline-Einträge eines team-eigenen Leads paginiert ab (neueste zuerst).

Query-Parameter:

per_page integer Einträge pro Seite (Standard 50, Maximum 100)

Timeline-Eintrag anlegen

POST /api/leads/{uuid}/timeline

Hängt einen Timeline-Eintrag an den Lead an. type wird gegen die erlaubte Timeline-Typenliste geprüft; fehlt type, wird note verwendet. Bei einem konvertierten Lead antwortet der Endpunkt mit HTTP 409.

Request Body:

content string * Inhalt des Eintrags
type string Timeline-Typ aus der erlaubten Vokabel (Standard note)
event_date string Ereignisdatum (ISO 8601, Standard: jetzt)

🔄 Lead-Konvertierung

Einmalig und unumkehrbar
Eine Konvertierung ist unumkehrbar. Der konvertierte Lead wird schreibgeschützt. Ein zweiter Konvertierungsversuch desselben Leads wird mit HTTP 409 abgelehnt.

Konvertiert einen team-eigenen Lead in einen neuen Kunden über denselben Konvertierungsdienst wie das UI-Modal. Die vier optionalen Transfer-Flags entsprechen den Checkboxen im Modal; ohne Flag wird die jeweilige Kategorie nicht übertragen. Die Flags tragen keine IDs, der Dienst leitet jede übertragene Zeile serverseitig vom gesperrten Lead ab. Die Antwort ist der neu angelegte Kunde (nicht der Lead).

Lead in Kunden konvertieren

POST /api/leads/{uuid}/convert

Konvertiert den Lead in einen neuen Kunden. Ohne client_name wird der Name aus dem Lead abgeleitet (Firmenname bei Firma, Vor- und Nachname bei Person). Die Antwort ist der neue Kunde (CmmClient) mit HTTP 201. Ein bereits konvertierter Lead ergibt 409.

Request Body:

client_name string Expliziter Kundenname (überschreibt den abgeleiteten Namen)
transfer_contacts boolean Ansprechpartner mit übernehmen
transfer_todos boolean Aufgaben mit übernehmen
transfer_docs boolean FlowDocs mit übernehmen
transfer_custom_fields boolean Custom-Field-Werte mit übernehmen

📊 Lead-Berichte

Nur offene Leads
Alle Berichte zählen ausschließlich offene Leads. Ein konvertierter Lead ist ein Kunde und würde sonst doppelt erscheinen, einmal im Trichter und einmal im Kundenbestand.

Trichter je Status

GET /api/leads/reports/funnel

Anzahl und Wert der offenen Leads je Status, in der Reihenfolge der Statusliste, dazu die Zahl der Leads ohne Status.

Verteilung auf Zuständige

GET /api/leads/reports/assignments

Anzahl und Wert der offenen Leads je zuständiger Person. Nicht zugewiesene Leads erscheinen als eigene Zeile mit user: null, damit genau diese Lücke sichtbar bleibt.

Herkunft der Leads

GET /api/leads/reports/sources

Anzahl der Leads je Quelle, absteigend sortiert.

Mögliche Dubletten finden

GET /api/leads/{uuid}/duplicates

Andere offene Leads mit derselben E-Mail-Adresse oder demselben Firmennamen, höchstens zwanzig. Bewusst eng gefasst: verglichen wird exakt, nicht ähnlich, weil über das Zusammenführen ein Mensch entscheidet.

🗂️ Lead-Boards

Berechtigung und Adressierung
Alle Board-Endpunkte erfordern FlowCRM plus die Leads-Unterberechtigung. Boards, Spalten und Karten werden ausschliesslich über ihre UUID adressiert. Pro Team sind maximal 5 Boards erlaubt.

Kanban-Boards für Leads: ein Board enthält Spalten, eine Spalte enthält Karten, jede Karte verweist auf genau einen Lead. Das Verschieben einer Karte in eine status-gemappte Spalte setzt den Status des Leads über denselben Pfad wie das Drag-and-Drop im UI. Karten dieses Endpunkts verweisen immer auf einen Lead, nie auf einen Kunden.

Boards auflisten

GET /api/lead-boards

Listet die Boards des Teams auf. Mit include=columns oder include=columns.cards werden Spalten bzw. Spalten samt Karten mitgeladen.

Query-Parameter:

include string columns oder columns.cards

Board anlegen

POST /api/lead-boards

Legt ein neues Board an. Bei überschrittenem Limit (5 pro Team) antwortet der Endpunkt mit HTTP 422.

Request Body:

name string * Name des Boards

Board abrufen

GET /api/lead-boards/{uuid}

Ruft ein team-eigenes Board ab. Mit include=columns oder columns.cards inklusive Spalten bzw. Karten.

Query-Parameter:

include string columns oder columns.cards

Board umbenennen

PATCH /api/lead-boards/{uuid}

Benennt ein team-eigenes Board um.

Request Body:

name string Neuer Name des Boards

Board löschen

DELETE /api/lead-boards/{uuid}

Löscht ein team-eigenes Board samt seiner Spalten und Karten. Die verknüpften Leads bleiben bestehen.

Spalte anlegen

POST /api/lead-boards/{uuid}/columns

Legt eine Spalte in einem team-eigenen Board an. Ohne sort_order wird die Spalte hinten angefügt.

Request Body:

name string * Name der Spalte
sort_order integer Position der Spalte (Standard: hinten angefügt)

Spalte aktualisieren

PATCH /api/lead-board-columns/{uuid}

Aktualisiert Name oder Position einer team-eigenen Spalte.

Request Body:

name string Neuer Name der Spalte
sort_order integer Neue Position der Spalte

Spalte löschen

DELETE /api/lead-board-columns/{uuid}

Löscht eine team-eigene Spalte. Die Karten der Spalte werden mitentfernt; die verknüpften Leads bleiben bestehen.

Karte anlegen

POST /api/lead-board-columns/{uuid}/cards

Legt eine Karte in einer team-eigenen Spalte an. Genau eine der beiden Varianten angeben: entweder lead_uuid (bestehender Lead) ODER ein verschachteltes lead-Objekt (neuer Lead und Karte in einer Transaktion). Die Karten-eigenen deal_value/deal_currency sind unabhängig von den Deal-Feldern des Leads.

Request Body:

lead_uuid string UUID eines bestehenden team-eigenen Leads (Alternative zu lead)
lead object Neuer Lead (type plus company_name/first_name usw., analog zum Lead-Anlegen; Alternative zu lead_uuid)
deal_value number Auftragswert der Karte
deal_currency string Währung der Karte (3-stelliger ISO-Code)
sort_order integer Position der Karte in der Spalte

Karte aktualisieren

PATCH /api/lead-board-cards/{uuid}

Aktualisiert die Deal-Felder oder die Position einer team-eigenen Karte. Die Verknüpfung zum Lead und die Spalte werden hier nicht geändert (dafür dient der Move-Endpunkt).

Request Body:

deal_value number Auftragswert der Karte
deal_currency string Währung der Karte (3-stelliger ISO-Code)
sort_order integer Position der Karte in der Spalte

Karte verschieben

PUT /api/lead-board-cards/{uuid}/move

Verschiebt eine team-eigene Karte in eine team-eigene Zielspalte. Ist die Zielspalte status-gemappt, wird der Status des Leads über denselben Pfad wie das UI-Drag-and-Drop gesetzt. Die Verknüpfung der Karte kann dabei nicht geändert werden.

Request Body:

target_column_uuid string * UUID der team-eigenen Zielspalte
sort_order integer Zielposition in der Spalte

Karte löschen

DELETE /api/lead-board-cards/{uuid}

Löscht eine team-eigene Karte. Der verknüpfte Lead bleibt bestehen.

📝 FlowDocs

Berechtigung je Eigentümer
FlowDocs an einem Lead erfordern FlowCRM plus die Leads-Unterberechtigung. FlowDocs an Kunden, Kontakten und Projekten benötigen nur den FlowCRM-Modulzugriff.
Versionierung
Jeder Schreibvorgang erzeugt eine neue Version. Die API akzeptiert content ausschließlich als Markdown-String und wandelt ihn serverseitig in TipTap um. content in der Antwort ist das interne TipTap-JSON und die Quelle der Wahrheit. content_html ist eine bereinigte Darstellungsform, die nur ausgegeben und nie entgegengenommen wird.
Technische Markdown-Inhalte
content darf technische Dokumentation mit Backticks, Codebeispielen, SQL-Begriffen, URLs, relativen Pfadsegmenten und Tabellen enthalten. Diese Inhalte werden als Markdown verarbeitet. Pfad, Query-Parameter und alle übrigen API-Felder bleiben durch die Web Application Firewall geschützt.

Jede der vier Eigentümer-Entitäten — Lead, Kunde, Kontakt und Projekt — hat genau ein FlowDoc. Es gibt deshalb weder eine Liste noch einen Anlege-Endpunkt noch eine Dokument-UUID im Pfad: GET /api/{eigentümer}/{uuid}/docs liefert das Dokument und legt es beim ersten Zugriff an, PUT auf denselben Pfad ersetzt den Inhalt vollständig. Breaking Change: POST auf /docs antwortet mit HTTP 405, die früheren Pfade /docs/{docUuid} mit HTTP 404. Der Eigentümer wird per UUID adressiert. title steht aus Kompatibilitätsgründen weiterhin in der Antwort, ist immer Notizen und wird im Request-Body mit 422 abgelehnt; team_id und die Eigentümer-Verknüpfung ebenso. Für Schreibvorgänge wird content ausschließlich als Markdown-String übergeben. TipTap-JSON wird als Eingabe nicht akzeptiert. Die Syntax folgt CommonMark und unterstützt zusätzlich Aufgabenlisten im GitHub-Stil. Verarbeitet werden Überschriften, Absätze, Aufzählungen, nummerierte Listen, Aufgabenlisten, Fett, Kursiv, Links und feste Zeilenumbrüche. Echte LF- und CRLF-Zeilenumbrüche werden verarbeitet und auch einzelne Zeilenumbrüche sichtbar bewahrt; ein vollständig doppelt kodierter Inhalt mit literalen -Sequenzen wird ebenfalls normalisiert. Eingebettetes HTML wird verworfen. Beim Anlegen eines Leads kann das doc-Feld den Inhalt direkt mitliefern (siehe Lead anlegen). Die folgende Referenz zeigt den vollen Payload einmal für Leads; die Endpunkte für Kunden, Kontakte und Projekte sind identisch aufgebaut und unterscheiden sich nur im Pfad und in der Berechtigung.

Lead-Dokument abrufen

GET /api/leads/{uuid}/docs

Ruft das FlowDoc eines team-eigenen Leads ab, inklusive latest_version_number. Existiert noch keines, wird es beim ersten Aufruf angelegt und leer zurückgegeben. documentable.type ist crm_lead.

Lead-Dokument aktualisieren

PUT /api/leads/{uuid}/docs

Ersetzt den Inhalt des FlowDocs vollständig. content muss übergeben werden; null oder ein leerer String leert das Dokument. Jeder Aufruf erzeugt eine neue Version und erhöht latest_version_number. title wird im Body mit 422 abgelehnt.

Request Body:

content string * Markdown-Inhalt, wird serverseitig in TipTap umgewandelt. null oder leerer String leert das Dokument

Kunden-Dokument abrufen

GET /api/clients/{uuid}/docs

Ruft das FlowDoc eines team-eigenen Kunden ab, inklusive latest_version_number. Existiert noch keines, wird es beim ersten Aufruf angelegt und leer zurückgegeben. documentable.type ist cmm_client.

Kunden-Dokument aktualisieren

PUT /api/clients/{uuid}/docs

Ersetzt den Inhalt des FlowDocs vollständig. content muss übergeben werden; null oder ein leerer String leert das Dokument. Jeder Aufruf erzeugt eine neue Version und erhöht latest_version_number. title wird im Body mit 422 abgelehnt.

Request Body:

content string * Markdown-Inhalt, wird serverseitig in TipTap umgewandelt. null oder leerer String leert das Dokument

Kontakt-Dokument abrufen

GET /api/contacts/{uuid}/docs

Ruft das FlowDoc eines Kontakts (ein privater Kontakt ohne Team ist nur für seinen Eigentümer erreichbar) ab, inklusive latest_version_number. Existiert noch keines, wird es beim ersten Aufruf angelegt und leer zurückgegeben. documentable.type ist cmm_contact.

Kontakt-Dokument aktualisieren

PUT /api/contacts/{uuid}/docs

Ersetzt den Inhalt des FlowDocs vollständig. content muss übergeben werden; null oder ein leerer String leert das Dokument. Jeder Aufruf erzeugt eine neue Version und erhöht latest_version_number. title wird im Body mit 422 abgelehnt.

Request Body:

content string * Markdown-Inhalt, wird serverseitig in TipTap umgewandelt. null oder leerer String leert das Dokument

Projekt-Dokument abrufen

GET /api/projects/{uuid}/docs

Ruft das FlowDoc eines Projekts (ein Projekt hat kein eigenes Team und wird über seinen Kunden team-scoped) ab, inklusive latest_version_number. Existiert noch keines, wird es beim ersten Aufruf angelegt und leer zurückgegeben. documentable.type ist cmm_client_project.

Projekt-Dokument aktualisieren

PUT /api/projects/{uuid}/docs

Ersetzt den Inhalt des FlowDocs vollständig. content muss übergeben werden; null oder ein leerer String leert das Dokument. Jeder Aufruf erzeugt eine neue Version und erhöht latest_version_number. title wird im Body mit 422 abgelehnt.

Request Body:

content string * Markdown-Inhalt, wird serverseitig in TipTap umgewandelt. null oder leerer String leert das Dokument