GoHighLevel (GHL) API V2 - POST/PUT von Business Custom Fields basierend auf External ERP ID nicht möglich (401 & 422 Fehler)

Hallo zusammen,

Ich baue einen Synchronisierungs-Workflow in n8n (Cloud-Version 2.22.6) auf, um Verkaufsdaten aus unserem ERP in GoHighLevel (GHL) Business-Profile (Companies) zu aktualisieren.

Mein Ziel:

Wir verarbeiten eine CSV-Datei mit Rechnungssummen aufgeteilt nach Marke. Wir müssen das Business in GHL anhand einer unveränderlichen externen ERP-ID (codice_cliente) suchen, die in einem Custom Field der GHL Company (business.id_gestionale) gespeichert ist, und dann:

  1. Falls das Business existiert: Seine finanziellen Custom Fields via PUT aktualisieren.

  2. Falls das Business neu ist: Ein neues Business-Profil via POST erstellen.

Das aktuelle Workflow-Setup:

  1. Trigger / Datei lesen: Importiert die CSV mit Spalten wie codice_cliente, ragione_sociale, totale_ecopots, totale_lechuza, usw.

  2. HTTP-Anfrage (GET): Ruft die GHL API V2 mit Header-Authentifizierung auf (Authorization: Bearer pit-... und Version: 2021-04-15).

  3. If-Node: Prüft, ob das Business gefunden wurde.

  4. HTTP-Anfrage (POST / PUT): Standard-HTTP-Nodes, um die Daten weiterzuleiten.

Die Hürden, auf die wir stoßen:

Problem 1: Der GET-Lookup schlägt fehl (API V2 Limitation)

Wir können die GET-Anfrage nicht direkt nach unserem Custom Field filtern.

  • Wenn wir https://services.leadconnectorhq.com/businesses/search?locationId=XXXX&query={{ $json.codice_cliente }} verwenden, gibt es ein leeres Array [] zurück, weil die globale Abfrage von GHL numerische Custom Fields nicht indiziert.

  • Wenn wir versuchen, den Custom-Field-Filter in der Query-Zeichenkette anzuhängen (&customFields=[...]), gibt GHL einen 422-Fehler zurück: "property customFields should not exist".

  • Wenn wir zu einem globalen Abruf wechseln (/businesses/?limit=100), zieht n8n eine riesige 1,2-MB-Last mit allen Unternehmen, was den binären/JSON-Kontext der ursprünglichen eingehenden CSV-Zeilen überschreibt und das nachfolgende Mapping extrem komplex macht.

Problem 2: Die POST-Autorisierungsschleife (401-Fehler)

Wenn ein Datensatz den POST-Pfad hinuntergeht, um ein neues Unternehmen zu erstellen (https://services.leadconnectorhq.com/businesses/), gibt GHL zurück:

401 - {"message":"LocationId is not specified","error":"Unauthorized","statusCode":401}

Obwohl locationId explizit im JSON-Body übergeben wird und das gleiche Bearer pit-...-Token perfekt für GET-Anfragen und PUT-Anfragen auf bestehende IDs funktioniert. Es sieht so aus, als ob GHL Location-Level API-Keys von der Nutzung von POST auf dem /businesses/-Endpoint völlig ausgeschlossen sind, es sei denn, man nutzt eine vollständige OAuth-Marketplace-App.

Vorgeschlagene Alternative (Wechsel zu Contacts + Company Name):

Da das Schreiben auf den /businesses/-Endpoint mit Standard-API-Keys sehr eingeschränkt zu sein scheint, evaluieren wir einen Wechsel zum Contacts-Endpoint.

Wir denken daran, stattdessen Contacts zu erstellen/aktualisieren, die ERP-ID und Rechnungsfelder in den Custom Fields des Kontakts zu übergeben, während wir das companyName-Feld im Kontaktobjekt auf das Business abbilden.

Meine Fragen an die Community:

  1. Was ist die Best Practice in n8n, um ein GHL Business anhand eines Custom Field-Werts abzufragen, ohne die gesamte 1,2-MB-Datenbank abzurufen oder auf 422-Fehler zu stoßen?

  2. Wie können wir das Datenüberschreibungs-Verhalten nach einem HTTP-Request-Node umgehen, damit die finalen PUT/POST-Nodes noch Zugriff auf die ursprünglichen CSV-Zeilendaten (Rechnungssummen) haben? Sollten wir den neuen Compare Datasets / Merge (Vorhandene Daten bereichern)-Node direkt nach der Dateierfassung nutzen?

  3. Hat jemand erfolgreich einen POST auf /businesses/ mit einem Standard-Location-API-Key ausgeführt, oder ist eine vollständige OAuth-Integration zwingend erforderlich, um Unternehmen zu erstellen?

  4. Bezüglich unserer Alternative: Denkt ihr, dass ein Wechsel zum Contacts-Endpoint (während das companyName-Feld im Kontakt gefüllt wird) ein solider, zuverlässiger Workaround ist, um die Business-Endpoint-API-Beschränkungen zu umgehen, oder gibt es bessere architektonische Wege, die ihr vorschlagen würdet?

Jede Hilfe, jeder Workaround oder jedes Workflow-JSON-Snippet würde sehr geschätzt!

Dedi, das sieht nach zwei separaten Blockern aus, nicht nach einem GHL-Bug: das richtige Unternehmen nach codice_cliente zu finden und es dann zu erstellen/zu aktualisieren, ohne dass der POST-Body abgelehnt wird. Wenn diese ERP-ID nur in einem benutzerdefinierten Feld des Unternehmens gespeichert ist, das GHL nicht filtern kann, ist GHL möglicherweise keine zuverlässige Lookup-Quelle; behalte eine kleine codice_cliente -> businessId-Zuordnung in deinen eigenen Daten und aktualisiere nach businessId.

Bevor du um Contacts umgestaltest, führe eine sichere Überprüfung durch: Sende eine minimale create-company-Anfrage mit demselben Token und locationId, aber ohne die Rechnungs-/benutzerdefinierten Feldnutzlast. Gibt es immer noch LocationId is not specified zurück, oder nur wenn der vollständige CSV-zugeordnete Body gesendet wird?

Dies ist ein wirklich gut dokumentiertes Problem und du hast bereits den Großteil der Diagnose selbst durchgeführt. Lass mich auf jedes Problem eingehen.

Zur GET-Lookup-Einschränkung — der saubere Workaround ist, alle Unternehmen einmal am Anfang des Workflows mit Pagination zu fetchen und dann mit einem Code-Node im Speicher nach deinem benutzerdefinierten Feldwert zu filtern. Ja, es ist eine große Payload, aber du rufst sie nur einmal pro Workflow-Ausführung auf, nicht einmal pro CSV-Zeile. Etwa so:

javascript

const allBusinesses = $input.all();
const target = allBusinesses.find(b => 
  b.json.customFields?.find(f => f.key === 'id_gestionale' && f.value === $('CSV Node').item.json.codice_cliente)
);

return target;

Zum Beibehalten von CSV-Daten nach dem HTTP-Request-Node — nutze einen Merge-Node im „Combine

Hier hast du zwei separate Fehler, und es hilft, sie nacheinander zu beheben, denn 401 und 422 bedeuten sehr unterschiedliche Dinge.

Die 401 ist Authentifizierung oder Scope. GHL API V2 Token sind pro Ressource begrenzt, und das Schreiben von benutzerdefinierten Company/Business-Feldern erfordert speziell den businesses write Scope, was man leicht übersehen kann, wenn dein Token für Contacts eingerichtet wurde. Bestätige, dass der Token (oder die OAuth-App dahinter) tatsächlich den businesses (companies) write Scope hat, und dass du auf der V2 Base URL mit dem korrekten Version Header bist, den GHL benötigt – ein fehlender oder falscher API-Version Header allein kann bei V2 einen 401 auslösen.

Die 422 ist die Payload. Benutzerdefinierte GHL-Felder müssen über ihre Feld-ID referenziert werden, nicht über den Feldnamen, und die Body-Struktur für benutzerdefinierte Felder ist spezifisch (ein Array von Objekten mit der Feld-ID und dem Wert, nicht ein flaches Key-Value-Paar). Wenn du codice_cliente nach Name oder als Top-Level-Property sendest, das ist deine 422. Hole zuerst die benutzerdefinierten Company-Field-Definitionen ab, um die exakte Feld-ID zu bekommen, und schreibe dann mit dieser ID. Bestätige auch, dass der Werttyp dem Feldtyp entspricht – wenn du einen String in ein Zahlenfeld sendest, gibt das auch 422.

Also: Behebe den Scope und Version Header für die 401, dann nutze die genaue benutzerdefinierte Feld-ID und Body-Struktur für die 422. Welcher Fehler kommt zuerst, wenn du es ausführst – die 401? Diese muss erst behoben sein, bevor die 422 überhaupt erreichbar ist.

[GELÖST] Inkrementelle Kontakt-Verkaufssynchronisierung von CSV zu GoHighLevel über mehrere/doppelte Kontakte hinweg mit n8n

Hallo zusammen! Ich möchte eine Lösung für ein Problem teilen, auf das wir bei der inkrementellen Aktualisierung von jährlichen markenspezifischen Verkaufsdaten aus einer CSV-Datei in GoHighLevel (GHL) gestoßen sind. Dies ist besonders nützlich, wenn Ihr CRM mehrere oder doppelte Kontakte enthält (z. B. verschiedene Mitarbeiter oder Filialleiter), die zum selben Unternehmenskonto gehören.

Nach mehreren Tests haben wir erfolgreich einen linearen und ausfallsicheren Workflow erstellt, der Daten sauber aggregiert und sie dann weiterverarbeitet, um jeden einzelnen übereinstimmenden doppelten Kontakt in GoHighLevel gleichzeitig zu aktualisieren.

:world_map: Was der Workflow bewirkt und wie er funktioniert

Der Workflow ist so konzipiert, dass er Datenimporte aus Buchhaltungs- oder Dokumentenverwaltungssystemen automatisiert. Er berechnet progressive Jahr-für-Jahr-Verkaufsmetriken, die nach einzelnen Produktmarken aufgeschlüsselt sind, und aktualisiert dynamisch das Feld „Letztes Kaufdatum