API und MCP-Server von LKDN Autopilot
Mit der API von LKDN Autopilot nutzen deine Programme, dein CRM, deine Automatisierungen (n8n, Make, Zapier) und deine KI-Assistenten (Claude, ChatGPT) dein verbundenes LinkedIn-Konto, über uns: Sie lesen Gespräche und Kontakte, suchen Personen, senden Nachrichten und Kontaktanfragen, veröffentlichen und kommentieren.
Drei Dinge, die du vorab wissen solltest:
- Ein einziges Konto, deins. Der Schlüssel ist an dein Konto bei LKDN Autopilot und an dein LinkedIn-Konto gebunden. Andere Konten werden nicht verbunden, und man wählt kein Konto aus: Der Schlüssel weiß bereits, welches es ist.
- Ein einziges Budget, geteilt mit dem Autopiloten. Jeder Besuch, jede Kontaktanfrage, jede Nachricht, jeder Kommentar, jede Reaktion und jeder Beitrag über die API verbraucht dasselbe Tagesbudget wie der Autopilot, und dasselbe gilt für jeden Lesevorgang, der bis zu LinkedIn reicht (Profile, Suchen, Listen, Beiträge, Unternehmen), mit denselben Limits und denselben Pausen zwischen zwei Aktionen. Wenn die API 5 Kontaktanfragen verbraucht, hat der Autopilot an diesem Tag 5 weniger. So bleibt dein Konto sicher.
- Gespräche speichern wir nicht. Wenn du einen Chat über die API liest, lesen wir ihn in diesem Moment aus deinem Konto und geben ihn an dich weiter. Wir speichern ihn weder in unseren Archiven noch in unseren Logs: Wenn du ihn aufbewahren musst, bewahrt ihn dein CRM auf.
Der Zugang zur API wird auf Anfrage aktiviert, pro Konto: Schreib uns an info@lkdnautopilot.com.
Inhalt #
- In fünf Minuten loslegen
- Schlüssel und Authentifizierung
- Limits und gemeinsames Budget
- Format der Antworten
- Idempotency-Key
- Simulationsmodus
- Endpoints
- Webhooks
- MCP-Server: Claude, ChatGPT, n8n
- Fertige Prompts
- Fehler
- Datenschutz und Verantwortung
- Was die API nicht tut
In fünf Minuten loslegen #
- Öffne in der App API und Integrationen (oben auf der Startseite).
- Erstelle einen Schlüssel: einen Namen (zum Beispiel „mein CRM“) und was er darf, Nur lesen oder Lesen und schreiben. Der Schlüssel wird nur ein einziges Mal angezeigt: Kopiere ihn und bewahre ihn an einem sicheren Ort auf.
- Probier es aus:
curl https://ayuhpqrbmizgsjstmcrt.supabase.co/functions/v1/public-api/v1/me \
-H "Authorization: Bearer lkdn_sk_LA_TUA_CHIAVE"
Die Antwort ist dein Profil, der Status der Verbindung und der erkannte LinkedIn-Tarif:
{
"data": {
"id": "per_3mQ8...",
"name": "Giulia Demo",
"headline": "Consulente, Demo Consulting Srl",
"public_identifier": "giulia-demo",
"linkedin_status": "connected",
"linkedin_plan": "premium",
"inmail_credits": 5,
"simulated": false
}
}
Die Basisadresse aller Aufrufe lautet:
https://ayuhpqrbmizgsjstmcrt.supabase.co/functions/v1/public-api/v1
Die vollständige OpenAPI-Spezifikation findest du in openapi.json: Importiere sie in Postman, Insomnia oder einen Client-Generator.
Schlüssel und Authentifizierung #
Jede Anfrage trägt den Schlüssel im Header:
Authorization: Bearer lkdn_sk_...
- Ein Schlüssel hat eine oder zwei Berechtigungen:
read(lesen) undwrite(handeln: Nachrichten, Einladungen, Beiträge, Kommentare, Reaktionen, Besuche, Webhooks). Ein Schlüssel nur zum Lesen, der zu handeln versucht, erhält403 insufficient_scope. - Du kannst bis zu 5 aktive Schlüssel haben. Gib jedem Programm einen eigenen Schlüssel: Wenn einer widerrufen werden muss, funktionieren die anderen weiter.
- Der Widerruf wirkt sofort: Die nächste Anfrage mit diesem Schlüssel erhält
401 invalid_api_key. - Wir speichern nur einen Fingerabdruck des Schlüssels (einen Hash), nie den Schlüssel selbst: Wenn du ihn verlierst, erstelle einen neuen.
- Der Schlüssel gehört auf einen Server, nie in eine Webseite oder eine App im Browser. Die API antwortet nicht auf direkte Aufrufe aus einem Browser.
Limits und gemeinsames Budget #
Dein LinkedIn-Konto hat ein Tagesbudget, dasselbe für den Autopiloten und für die API. Wir legen es an einer einzigen Stelle fest, mit denselben Regeln: der erkannte LinkedIn-Tarif, die Aufwärmphase eines neuen Kontos, die Limits, die du selbst gewählt hast, und die, die wir senken können, wenn das Konto gefährdet ist. Die API kann es nicht überschreiten, unter keinen Umständen.
| Aktion | Standardwert pro Tag | Endpoint |
|---|---|---|
| Besuche mit Benachrichtigung | 30 | POST /v1/people/{id}/visits |
| Kontaktanfragen | 20 (etwa 5 bei einem kostenlosen LinkedIn-Konto), 100 in 7 Tagen | POST /v1/invitations |
| Nachrichten | 25 | POST /v1/conversations, POST /v1/conversations/{id}/messages |
| InMail | 3 | POST /v1/conversations mit inmail: true |
| Kommentare | 10 | POST /v1/posts/{id}/comments |
| Reaktionen | 20 | POST /v1/posts/{id}/reactions |
| Beiträge | 2 | POST /v1/posts |
| Zurückgezogene Anfragen | 10 | DELETE /v1/invitations/{id} |
| Summe der Aktionen oben | 70 | |
| Ausgelesene Profile (ohne Benachrichtigung) | 40 | GET /v1/people/{id} |
| Geöffnete Profile insgesamt: Besuche und ausgelesene Profile zusammen | 70 | POST /v1/people/{id}/visits, GET /v1/people/{id} |
| Suchergebnisse | 300 | POST /v1/searches |
| Gelesene Listen, Beiträge und Seiten | 40 | GET /v1/me (bis zu 2), GET /v1/connections, GET /v1/invitations, GET /v1/people/{id}/posts, GET /v1/posts/{id}, GET /v1/posts/{id}/comments, GET /v1/posts/{id}/reactions, GET /v1/companies/{id} |
Gespräche und Nachrichten (GET /v1/conversations...) verbrauchen kein Budget: Sie sind bereits aus deinem Konto synchronisiert, ohne eine Anfrage an LinkedIn für jeden Lesevorgang. Kommentare und Reaktionen lesen zuerst den Beitrag: Dieser Lesevorgang wird immer gezählt, auch wenn die Aktion danach nicht hinausgeht, aber eine Aktion wird nie wegen des Lesebudgets abgelehnt.
Das sind die Standardwerte: Die deines Kontos können niedriger sein (Aufwärmphase, kostenloser Tarif, von dir gewählte Limits). Die tatsächlichen Werte von heute stehen immer in GET /v1/usage, zusammen mit dem, was du schon verbraucht hast, und dem, was die API verbraucht hat.
Keine Zahl, auch nicht die, die wir auf Anfrage erhöhen können, überschreitet 80 % der Limits, die die Infrastruktur, über die wir uns verbinden, für ein LinkedIn-Konto empfiehlt: zum Beispiel höchstens 64 Kontaktanfragen pro Tag und 160 in 7 Tagen, 80 geöffnete Profile pro Tag aus Besuchen und Lesevorgängen, 800 Suchergebnisse pro Tag (2000 mit Sales Navigator oder Recruiter). Die Datenbank lehnt jede Einstellung jenseits dieses Spielraums ab.
Weitere Regeln, die das Konto schützen:
- Die Aktionen haben Abstand. Zwischen einer Aktion und der nächsten auf deinem Profil vergehen einige Minuten, mit einem zufälligen Intervall, so wie bei einem Menschen; ab und zu ist die Pause länger (auch eine Dreiviertelstunde). Wenn du eine Aktion zu früh anforderst, erhältst du
429 action_pacedmitRetry-Afterin Sekunden. - Aktionen gegenüber neuen Personen folgen deinen Arbeitszeiten. Besuche, Kontaktanfragen und InMails gehen nur an den Tagen und zu den Uhrzeiten hinaus, die du in der App gewählt hast (außerhalb dieser Zeiten:
429 outside_activity_window). Nachrichten in Gesprächen, Kommentare, Reaktionen und Beiträge nicht. - Personen, die nicht kontaktiert werden sollen, werden nicht kontaktiert. Bei Personen, die darum gebeten haben, nicht kontaktiert zu werden, die du ausgeschlossen hast oder die zu den Kategorien gehören, die wir nicht kontaktieren (zum Beispiel Network-Marketing), lautet die Antwort
403 person_not_contactable, auch über die API. - Wen du über die API kontaktierst, kontaktiert der Autopilot nicht erneut. Die Person gehört dann dir: Der Autopilot überspringt sie ab diesem Moment.
- Eine endgültige Ablehnung durch LinkedIn bleibt endgültig. Wenn LinkedIn keine Anfragen an eine Person mehr annimmt, antwortet die API mit
422 invitation_not_allowedund versucht es nicht erneut. - Wenn LinkedIn verlangt, langsamer zu werden, wird das Konto langsamer. Wenn LinkedIn zu viele Anfragen meldet (ein
429), stoppen alle Aktionen und Lesevorgänge bis Mitternacht in deiner Zeitzone; passiert das zwei Tage in Folge, werden Kontaktanfragen und Nachrichten 7 Tage lang halbiert. Wenn LinkedIn das Limit der Kontaktanfragen meldet, stoppen die Anfragen bis zum Ende des folgenden Tages. In der Zwischenzeit erhältst du429 account_backoffmitRetry-After, undGET /v1/usagenennt den Grund inbackoff. Das gilt für den Autopiloten und die API zusammen. - Anfragen an die API: höchstens 60 pro Minute und Schlüssel und 2000 pro Tag und Konto, Lesevorgänge eingeschlossen (
429 rate_limited,429 daily_request_limit).
Jedes 429 hat den Header Retry-After: nach wie vielen Sekunden du es erneut versuchen kannst. Wenn das Tagesbudget aufgebraucht ist, zeigt Retry-After auf den Beginn der nächsten Arbeitszeit. Ein KI-Agent, der ein 429 erhält, muss anhalten und es sagen, statt es in einer Schleife erneut zu versuchen.
Format der Antworten #
Die Antworten sind JSON, mit englischen Namen, die immer gleich bleiben:
- ein Objekt:
{"data": {...}} - eine Liste:
{"data": [...], "page": {"next_cursor": "cur_..." }}; für die nächste Seite übergibcursor=cur_...;next_cursorist auf der letzten Seitenull. Cursor laufen nach einer Stunde ab und gelten nur für die Liste, die sie geliefert hat. - Datumsangaben sind ISO 8601 in UTC (
2026-10-25T09:30:00.000Z) und enden auf_at. - die
idsind opak und stabil, mit einem Präfix, das sagt, was sie bezeichnen:per_Person,cnv_Gespräch,msg_Nachricht,inv_Kontaktanfrage,pst_Beitrag,cmt_Kommentar,cmp_Unternehmen,flt_Suchfilter,whk_Webhook. Du kannst sie in deinem CRM speichern: Dieselbe Person hat immer dieselbeid. Sie gelten nur für dein Konto. - wo nötig, gibt es auch die öffentlichen Bezeichner von LinkedIn:
public_identifier(der Teil nachlinkedin.com/in/) undprofile_url. degreeist der Kontaktgrad:1,2,3odernull(außerhalb des Netzwerks oder unbekannt).
Jede Antwort hat den Header X-Request-Id: Nenne ihn, wenn du uns wegen eines Problems schreibst.
Fehler folgen dem Standard RFC 9457 (Content-Type: application/problem+json):
{
"type": "https://lkdnautopilot.com/api-doc/#errori-daily_budget_exhausted",
"title": "The daily budget for this action is used up, by the autopilot and the API together.",
"status": 429,
"code": "daily_budget_exhausted",
"request_id": "req_9f2c...",
"retry_after": 52310
}
Das Feld, das du im Code auswertest, ist code: Es ist stabil und ändert nie seine Bedeutung. Die vollständige Liste steht unter Fehler.
Idempotency-Key #
Jede Anfrage, die handelt (POST und DELETE, außer POST /v1/searches, das ein Lesevorgang ist), braucht den Header Idempotency-Key: einen eindeutigen Wert, den du wählst, von 8 bis 200 Zeichen, zum Beispiel eine UUID.
curl -X POST https://ayuhpqrbmizgsjstmcrt.supabase.co/functions/v1/public-api/v1/invitations \
-H "Authorization: Bearer lkdn_sk_..." \
-H "Idempotency-Key: 6b1f0e5e-2f4a-4c39-9a0e-4d8f2f0a1c11" \
-H "Content-Type: application/json" \
-d '{"person_id": "per_3mQ8...", "message": "Ciao Marco, ho letto il tuo post sulla logistica e mi è piaciuto."}'
- Wenn du dieselbe Anfrage mit demselben Idempotenzschlüssel wiederholst (ein Timeout, ein Netzwerkfehler), erhältst du dieselbe Antwort und die Aktion wird nicht wiederholt. Die Antwort hat
Idempotency-Replayed: true. - Derselbe Idempotenzschlüssel mit einer anderen Anfrage:
422 idempotency_key_reused. - Wenn die erste Anfrage noch läuft:
409 idempotency_in_progress. - Wenn die Anfrage nicht gehandelt hat (ein 429, ein 400, ein 409 wegen des Kontostatus), wird der Idempotenzschlüssel wieder frei, und du kannst es mit demselben erneut versuchen.
- Idempotenzschlüssel werden 24 Stunden lang gespeichert.
502 action_outcome_unknown bedeutet, dass LinkedIn uns keine lesbare Antwort gegeben hat und die Aktion hinausgegangen sein kann oder nicht. Versuch es nicht mit einem neuen Idempotenzschlüssel erneut: Prüf zuerst nach (bei einer Nachricht lies das Gespräch).
Simulationsmodus #
Solange dein Konto in Vorbereitung ist, antwortet die API mit simulierten Daten (erfundene Personen), und keine Aktion geht wirklich hinaus: Die Aktionen laufen trotzdem durch die Limits und werden protokolliert. Simulierte Antworten haben den Header LKDN-Mode: simulated und, bei Aktionen, "simulated": true. So baust und testest du eine Integration, ohne irgendjemanden zu kontaktieren.
Endpoints #
Alle Pfade sind relativ zur Basisadresse. Lesen: Berechtigung read. Aktion: Berechtigung write und Idempotency-Key.
Dein Konto #
GET /v1/me (Lesen). Dein Profil, linkedin_status (connected, reconnect_required, not_connected), linkedin_plan (free, premium, sales_navigator), die InMail-Credits. Verbraucht bis zu zwei Listenabrufe (das Profil und die InMail-Credits).
GET /v1/usage (Lesen). Das Budget von heute, geteilt mit dem Autopiloten:
{
"data": {
"date": "2026-10-25",
"timezone": "Europe/Rome",
"linkedin_plan": "premium",
"invitation_note_available": true,
"requests": { "today": 41, "per_day": 2000, "per_minute": 60 },
"budget": {
"invitations": { "used": 7, "limit": 20, "used_by_api": 2, "weekly_limit": 100 },
"messages": { "used": 3, "limit": 25, "used_by_api": 3 },
"list_reads": { "used": 6, "limit": 40, "used_by_api": 6 },
"profile_retrievals": { "used": 21, "limit": 70, "used_by_api": 4 },
"total_actions": { "used": 18, "limit": 70 }
},
"activity_window": { "open_now": true, "start": "09:00", "end": "18:00", "working_day": true },
"backoff": null,
"shared_with_autopilot": true
}
}
backoff ist null oder sagt, warum das Konto heute langsamer läuft: {"kind": "all_stop" | "invite_stop" | "halved", "reason": "...", "until": "2026-10-26T23:00:00.000Z"} (alles gestoppt, Kontaktanfragen gestoppt, Tempo halbiert) und bis wann.
Personen #
GET /v1/people/{id} (Lesen). Ein Profil: id ist eine per_... oder der public_identifier. Die Person wird nicht benachrichtigt. Verbraucht ein „ausgelesenes Profil“ (und zählt bei den insgesamt geöffneten Profilen). Liefert weder E-Mail-Adressen noch Telefonnummern anderer Personen.
POST /v1/people/{id}/visits (Aktion). Besucht das Profil: Die Person sieht, dass du es angesehen hast. Verbraucht einen Besuch.
GET /v1/people/{id}/posts (Lesen). Die neuesten Beiträge der Person. limit bis 50, cursor. Verbraucht einen Listenabruf pro Seite.
GET /v1/connections (Lesen). Deine Kontakte ersten Grades, die neuesten zuerst. q filtert nach Namen, limit bis 200, cursor. Verbraucht einen Listenabruf pro Seite.
Suche #
GET /v1/search/filters?type=location&q=verona (Lesen). Wandelt einen Ort (type=location) oder eine Branche (type=industry) in einen Filter flt_... für die Suche um. Nutze ihn immer: Die Suche lehnt einen Filter ab, der nicht von hier kommt (400 invalid_filter), weil LinkedIn einen unbekannten Filter stillschweigend ignorieren und dir die Ergebnisse einer anderen Suche liefern würde als der, die du wolltest.
POST /v1/searches (Lesen). Sucht Personen:
{
"keywords": "logistica",
"titles": ["CFO", "direttore finanziario"],
"locations": ["flt_..."],
"industries": ["flt_..."],
"degree": [2, 3],
"limit": 10
}
Mindestens eines von keywords, titles, locations, industries. limit bis 10 (bis 25 mit Sales Navigator); jedes Ergebnis verbraucht ein „Suchergebnis“. Für die nächste Seite wiederholst du dieselbe Suche mit cursor. Die Antwort enthält auch page.total_estimate.
Gespräche #
GET /v1/conversations (Lesen). Deine Gespräche, das neueste zuerst: type (direct oder group), participant_id, kind (message, inmail, sponsored, offer), inbox, unread_count, last_activity_at. unread=true nur für die ungelesenen, limit bis 100, cursor.
GET /v1/conversations/{id} (Lesen). Ein Gespräch.
GET /v1/conversations/{id}/messages (Lesen). Die Nachrichten, die neueste zuerst: text, from_me, sender_id, sent_at, kind, seen, attachments (Typ und Name, ohne Link). Den Text lesen wir in diesem Moment aus deinem Konto und speichern ihn nicht.
POST /v1/conversations/{id}/messages (Aktion). Antworte in einem Einzelgespräch: {"text": "..."} (bis zu 8000 Zeichen). Gruppengespräche werden nicht unterstützt (422 group_conversation_not_supported).
POST /v1/conversations (Aktion). Schreib einer Person: {"person_id": "per_...", "text": "..."}. Funktioniert mit Kontakten ersten Grades (sonst 422 not_first_degree). Für eine InMail an jemanden, der nicht zu deinen Kontakten gehört: {"person_id": "per_...", "text": "...", "inmail": true, "subject": "..."}; verbraucht eine InMail aus dem Budget und einen InMail-Credit deines Tarifs.
Kontaktanfragen #
GET /v1/invitations?direction=sent (Lesen). Die Anfragen, die du gesendet hast und die noch offen sind; direction=received für die erhaltenen. Verbraucht einen Listenabruf pro Seite.
POST /v1/invitations (Aktion). {"person_id": "per_...", "message": "..."}. Die Nachricht (message, bis zu 300 Zeichen) ist bei einem kostenlosen LinkedIn-Konto nicht verfügbar (403 invitation_note_not_available): In diesem Tarif geht die Anfrage ohne Nachricht hinaus. Bereits verbunden: 409 already_connected; Anfrage bereits offen: 409 already_invited.
DELETE /v1/invitations/{id} (Aktion). Zieht eine gesendete, noch offene Anfrage zurück.
Beiträge #
GET /v1/posts/{id} (Lesen). Ein Beitrag: id ist eine pst_..., eine LinkedIn-URN (urn:li:activity:...) oder die Nummer der Aktivität. Verbraucht einen Listenabruf.
GET /v1/posts/{id}/comments, GET /v1/posts/{id}/reactions (Lesen). Kommentare und Reaktionen. Verbraucht einen Listenabruf pro Seite.
POST /v1/posts (Aktion). Veröffentlicht einen Beitrag auf deinem Profil: {"text": "..."} (bis zu 3000 Zeichen).
POST /v1/posts/{id}/comments (Aktion). {"text": "...", "reply_to": "cmt_..."}; reply_to, um auf einen Kommentar zu antworten. Der Autor des Beitrags erhält eine Benachrichtigung.
POST /v1/posts/{id}/reactions (Aktion). {"type": "like"}; type ist einer von like, celebrate, support, love, insightful, funny.
Unternehmen #
GET /v1/companies/{id} (Lesen). Eine Unternehmensseite: id ist eine cmp_... oder der öffentliche Name nach linkedin.com/company/. Verbraucht einen Listenabruf.
Webhooks #
GET /v1/webhooks (Lesen), POST /v1/webhooks (Aktion), DELETE /v1/webhooks/{id} (Aktion). Siehe Webhooks.
Webhooks #
Statt jede Minute zu fragen „Gibt es etwas Neues?“, rufen wir dich auf. Erstelle einen Webhook in der App (API und Integrationen) oder mit:
curl -X POST https://ayuhpqrbmizgsjstmcrt.supabase.co/functions/v1/public-api/v1/webhooks \
-H "Authorization: Bearer lkdn_sk_..." -H "Idempotency-Key: $(uuidgen)" -H "Content-Type: application/json" \
-d '{"url": "https://crm.example.com/hooks/lkdn", "events": ["message.received", "connection.accepted"]}'
Die Antwort enthält das Signaturgeheimnis (whsec_...) ein einziges Mal.
| Ereignis | Wann | data |
|---|---|---|
message.received | eine Person schreibt dir | conversation_id, message_id, sender_id, sent_at |
connection.accepted | eine Kontaktanfrage wird angenommen (kann bis zu 8 Stunden später ankommen) | person_id, public_identifier, accepted_at |
invitation.sent | eine Kontaktanfrage geht hinaus, vom Autopiloten oder über die API | invitation_id, person_id, source (autopilot oder api), sent_at |
account.status_changed | die Verbindung deines Kontos ändert ihren Status | status, previous_status, changed_at; Status: connected, connecting, reconnect_required, disconnected, error |
Der Body des Aufrufs:
{
"id": "evt_0b6f1f40000040008000000000000001",
"type": "message.received",
"created_at": "2026-10-25T10:00:03.000Z",
"data": {
"conversation_id": "cnv_...",
"message_id": "msg_...",
"sender_id": "per_...",
"sent_at": "2026-10-25T10:00:00.000Z"
}
}
Der Text der Nachricht wird nicht im Webhook mitgeschickt: Lies ihn mit GET /v1/conversations/{conversation_id}/messages. So bleibt kein Text in unseren Versandwarteschlangen.
Jeder Aufruf hat die Header LKDN-Event-Id, LKDN-Event-Type und LKDN-Signature: t=<secondi unix>,v1=<firma> (Unix-Sekunden und Signatur). Die Signatur ist der HMAC-SHA256, hexadezimal, der Zeichenkette <t>.<corpo esatto> (der Zeitstempel, ein Punkt, der exakte Body) mit deinem Geheimnis. Prüfe sie immer, und lehne einen Aufruf ab, der älter als 5 Minuten ist:
// Node.js
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(secret, rawBody, header, now = Math.floor(Date.now() / 1000)) {
const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header ?? '');
if (!m || Math.abs(now - Number(m[1])) > 300) return false;
const expected = createHmac('sha256', secret).update(`${m[1]}.${rawBody}`).digest('hex');
return timingSafeEqual(Buffer.from(expected), Buffer.from(m[2]));
}
# Python
import hashlib, hmac, re, time
def verify(secret: str, raw_body: str, header: str) -> bool:
m = re.fullmatch(r"t=(\d+),v1=([0-9a-f]{64})", header or "")
if not m or abs(time.time() - int(m.group(1))) > 300:
return False
expected = hmac.new(secret.encode(), f"{m.group(1)}.{raw_body}".encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, m.group(2))
Antworte innerhalb von 10 Sekunden mit einem 2xx-Code. Sonst versuchen wir es nach 1, 5 und 30 Minuten erneut, dann nach 2, 6, 12 und 24 Stunden; nach 8 Versuchen ist das Ereignis verloren, und nach 20 verlorenen Ereignissen in Folge schaltet sich der Webhook aus (du schaltest ihn in der App wieder ein). Ein 410 Gone schaltet ihn für dieses Ereignis sofort aus. Dasselbe Ereignis kann zweimal ankommen: Nutze id, um es zu erkennen. Wir akzeptieren nur https-Adressen auf Port 443, nie interne oder private Adressen, und wir folgen keinen Weiterleitungen.
MCP-Server: Claude, ChatGPT, n8n #
Der MCP-Server stellt dieselben Funktionen als Werkzeuge für einen KI-Assistenten bereit, mit demselben Schlüssel und denselben Limits: Es gibt keinen Weg, der sie umgeht. Die Werkzeuge, die handeln, sagen das in ihrer Beschreibung und bitten den Assistenten, dir den genauen Text zu zeigen und auf dein Ja zu warten.
Adresse:
https://ayuhpqrbmizgsjstmcrt.supabase.co/functions/v1/mcp
Authentifizierung: Authorization: Bearer lkdn_sk_.... Für Programme, in denen man keinen Header setzen kann, gibt es die Form mit dem Schlüssel in der Adresse, .../functions/v1/mcp/lkdn_sk_...: Behandle sie wie ein Passwort, denn wer die Adresse hat, hat den Schlüssel. Verwende dafür einen eigenen Schlüssel, und zwar einen nur zum Lesen, wenn es dem Assistenten genügt zu lesen.
Werkzeuge: get_my_profile, get_usage, get_person, visit_profile, list_person_posts, list_connections, find_search_filters, search_people, list_conversations, get_conversation_messages, send_message, start_conversation, list_invitations, send_invitation, withdraw_invitation, get_post, list_post_comments, list_post_reactions, publish_post, comment_on_post, react_to_post, get_company.
Claude Code #
claude mcp add --transport http lkdn-autopilot \
https://ayuhpqrbmizgsjstmcrt.supabase.co/functions/v1/mcp \
--header "Authorization: Bearer lkdn_sk_..."
Claude Desktop #
Unter Einstellungen, Entwickler, Konfiguration bearbeiten (claude_desktop_config.json), mit der Brücke mcp-remote (Node.js erforderlich):
{
"mcpServers": {
"lkdn-autopilot": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://ayuhpqrbmizgsjstmcrt.supabase.co/functions/v1/mcp",
"--header", "Authorization:Bearer ${LKDN_API_KEY}"],
"env": { "LKDN_API_KEY": "lkdn_sk_..." }
}
}
}
Starte Claude Desktop neu: Die Werkzeuge erscheinen im Werkzeugmenü.
Claude.ai (benutzerdefinierter Konnektor) #
Unter Einstellungen, Konnektoren, Benutzerdefinierten Konnektor hinzufügen: Als URL verwendest du die Form mit dem Schlüssel in der Adresse, https://ayuhpqrbmizgsjstmcrt.supabase.co/functions/v1/mcp/lkdn_sk_..., und lässt die OAuth-Felder leer. Der benutzerdefinierte Konnektor ist in den Claude-Plänen verfügbar, die ihn vorsehen.
ChatGPT #
Unter Einstellungen, Apps und Konnektoren, mit aktiviertem Entwicklermodus: Erstelle einen Konnektor mit der URL in der Form mit dem Schlüssel in der Adresse und der Authentifizierung „Keine“. Die Werkzeuge, die handeln, bitten um Bestätigung, bevor sie loslegen.
n8n #
Im Knoten MCP Client Tool eines AI Agent: Endpoint https://ayuhpqrbmizgsjstmcrt.supabase.co/functions/v1/mcp, Transport HTTP Streamable, Authentifizierung Bearer mit deinem Schlüssel. Für einen Ablauf ohne KI verwende den Knoten HTTP Request auf der REST-API, mit Zugangsdaten vom Typ „Header Auth“ (Authorization = Bearer lkdn_sk_...), und einen Knoten Webhook als Ziel unserer Webhooks.
Fertige Prompts #
Zum Einfügen in Claude oder ChatGPT, mit verbundenem MCP-Server.
Die Antworten von heute
Sieh dir meine ungelesenen LinkedIn-Gespräche an. Sag mir zu jedem, wer die Person ist (Name, Rolle, Unternehmen),
was sie mir geschrieben hat, in einer Zeile, und ob deiner Meinung nach heute eine Antwort nötig ist. Antworte niemandem:
Schlag mir nur für die wichtigen einen Antwortentwurf vor und warte bei jedem Text auf mein Ja.
Ein Treffen vorbereiten
Lies das LinkedIn-Profil von <Name oder Link> und die letzten Beiträge. Bereite mir fünf Zeilen vor: wer die Person ist,
womit sie sich gerade beschäftigt, was sie zuletzt geschrieben hat, eine Gemeinsamkeit mit mir und eine kluge Frage an sie.
Besuche das Profil nicht mit Benachrichtigung und schreib der Person nicht.
Personen für ein Segment finden
Suche auf LinkedIn Inhaber und Leiter von Zahnarztpraxen in Venetien (nutze find_search_filters für den Ort).
Zeig mir die ersten 10 in einer Tabelle mit Name, Rolle, Stadt und Kontaktgrad. Sag mir vor der Suche,
wie viele Suchergebnisse mir heute noch bleiben (get_usage). Sende keine Kontaktanfragen.
Kontaktanfragen mit einer echten Nachricht
Für jede dieser Personen <Liste von Links>: Lies das Profil und schreib mir eine Nachricht zur Kontaktanfrage mit höchstens
250 Zeichen, auf Deutsch, die etwas Echtes aus dem Profil aufgreift, ohne etwas zu verkaufen und ohne Links.
Zeig mir alle Nachrichten. Sende nur die, die ich freigebe, eine nach der anderen, und hör auf, wenn das Tagesbudget aufgebraucht ist.
Auf Kommentare zu meinen Beiträgen antworten
Nimm meinen letzten Beitrag (such ihn unter den Beiträgen meines Profils) und lies die Kommentare. Für die, auf die ich
noch nicht geantwortet habe, schlag mir eine kurze, freundliche Antwort vor. Veröffentliche nur die Antworten, die ich freigebe.
Die Zusammenfassung der Woche
Sag mir, wie viele Kontaktanfragen seit mehr als zwei Wochen offen sind und an wen sie gingen. Schlag mir vor, welche
ich zurückziehen soll. Zieh keine davon ohne mein Okay zurück.
Regeln, die für jeden Prompt gelten: Der Assistent darf keine Fakten über die Person erfinden, muss dir jeden Text zeigen, bevor er ihn sendet, und muss anhalten und es dir sagen, wenn er action_paced, daily_budget_exhausted oder outside_activity_window erhält.
Fehler #
Jeder Fehler hat einen stabilen code: Er ist ein Vertrag und ändert nie seine Bedeutung. Lies den code, nicht den Text.
| Status | code | Was es bedeutet, was zu tun ist |
|---|---|---|
| 400 | invalid_request | ein Feld fehlt oder ist ungültig (field, detail sagen, welches) |
| 400 | invalid_cursor | Cursor fehlerhaft, abgelaufen oder von einer anderen Liste: Beginne wieder bei der ersten Seite |
| 400 | invalid_filter | ein Suchfilter kommt nicht von GET /v1/search/filters |
| 400 | idempotency_key_required | Idempotency-Key fehlt bei einer Anfrage, die handelt |
| 401 | invalid_api_key | Schlüssel fehlt, ist unbekannt oder widerrufen |
| 403 | insufficient_scope | der Schlüssel hat nicht die Berechtigung write |
| 403 | api_access_not_enabled | die API ist für dein Konto nicht aktiv |
| 403 | subscription_inactive | das Abonnement ist nicht aktiv |
| 403 | person_not_contactable | diese Person darf von diesem Konto aus nicht kontaktiert werden |
| 403 | invitation_note_not_available | in deinem LinkedIn-Tarif kann die Anfrage keine Nachricht enthalten |
| 403 | linkedin_feature_missing | es braucht ein LinkedIn-Produkt, das das Konto nicht hat |
| 404 | not_found | die Ressource existiert nicht oder gehört nicht zu deinem Konto |
| 404 | route_not_found | den Endpoint gibt es nicht |
| 405 | method_not_allowed | den Endpoint gibt es, aber nicht mit dieser Methode |
| 409 | linkedin_not_connected | kein LinkedIn-Konto verbunden: Verbinde es in der App |
| 409 | linkedin_reconnect_required | das Konto muss in der App neu verbunden werden |
| 409 | linkedin_account_unavailable | das Konto ist vorübergehend gesperrt |
| 409 | account_paused | unser Team hat die Aktionen dieses Kontos pausiert |
| 409 | idempotency_in_progress | die Anfrage mit diesem Idempotency-Key läuft noch oder ist nicht abgeschlossen |
| 409 | already_connected | ihr seid bereits verbunden |
| 409 | already_invited | an diese Person gibt es bereits eine offene Anfrage |
| 422 | idempotency_key_reused | derselbe Idempotency-Key mit einer anderen Anfrage |
| 422 | invitation_not_allowed | LinkedIn erlaubt keine weitere Anfrage an diese Person: endgültig |
| 422 | recipient_unreachable | die Person ist nicht erreichbar |
| 422 | not_first_degree | für eine Nachricht braucht es einen Kontakt ersten Grades: Sende eine Kontaktanfrage oder eine InMail |
| 422 | inmail_not_allowed | dieser Person kann man keine InMail senden |
| 422 | insufficient_inmail_credits | InMail-Credits aufgebraucht |
| 422 | comments_disabled | die Kommentare zu diesem Beitrag sind geschlossen |
| 422 | group_conversation_not_supported | Gruppengespräche werden nicht unterstützt |
| 422 | action_refused | LinkedIn hat die Aktion abgelehnt |
| 422 | invalid_webhook | der Webhook kann nicht erstellt werden (reason sagt, warum) |
| 429 | rate_limited | zu viele Anfragen für diesen Schlüssel: Warte Retry-After ab |
| 429 | daily_request_limit | die Anfragen des Tages für dieses Konto sind aufgebraucht |
| 429 | daily_budget_exhausted | das Tagesbudget für diese Aktion ist aufgebraucht, Autopilot und API zusammen |
| 429 | weekly_budget_exhausted | das Wochenbudget für Kontaktanfragen ist aufgebraucht |
| 429 | action_paced | Aktionen mit Abstand, um das Konto zu schützen: Warte Retry-After ab |
| 429 | outside_activity_window | außerhalb deiner Arbeitszeiten |
| 429 | linkedin_rate_limited | LinkedIn schränkt das Konto gerade ein: Versuch es später erneut |
| 429 | account_backoff | LinkedIn hat das Konto gebeten, langsamer zu werden: Diese Aktion pausiert bis Retry-After |
| 500 | internal_error | ein Fehler auf unserer Seite |
| 502 | linkedin_error | LinkedIn hat eine Antwort gegeben, die wir nicht lesen können |
| 502 | action_outcome_unknown | die Aktion kann hinausgegangen sein oder nicht: Prüf das, bevor du es erneut versuchst |
| 503 | api_unavailable | die API ist gerade abgeschaltet |
| 503 | service_not_configured | der Dienst ist noch nicht konfiguriert |
| 503 | linkedin_unavailable | LinkedIn hat nicht rechtzeitig geantwortet: Versuch es später erneut |
Datenschutz und Verantwortung #
- Nachrichten auf der Durchreise. Gespräche und Nachrichten, die über die API gelesen werden, laufen über uns und kommen bei dir an; wir speichern sie weder in unseren Archiven noch in unseren Logs. Dasselbe gilt für die Texte, die du sendest: Nachrichten, Nachrichten zu Kontaktanfragen, Kommentare und Beiträge gehen an LinkedIn und bleiben nicht bei uns. Vom API-Verkehr protokollieren wir nur, was angefordert wurde (die Art der Anfrage, das Ergebnis, die Dauer), 90 Tage lang, und die ausgeführten Aktionen (welche Art, gegenüber welcher Person, wann, ohne Text), 180 Tage lang, weil wir sie brauchen, um die Limits durchzusetzen.
- Verantwortlicher bist du. Für die Daten der Personen, die du liest und kontaktierst, bist du der Verantwortliche für die Verarbeitung, wie beim Autopiloten; wir sind Auftragsverarbeiter gemäß der Vereinbarung zur Auftragsverarbeitung (DPA), die du akzeptiert hast. Wenn du die Gespräche in deinem CRM aufbewahrst, liegt diese Aufbewahrung bei dir.
- Was deine Programme tun, tut dein Konto. Jede Aktion mit einem deiner Schlüssel, durch eines deiner Programme oder durch einen KI-Assistenten, den du verbunden hast, geschieht in deinem Namen und unter deiner Verantwortung, gegenüber LinkedIn und gegenüber den kontaktierten Personen, wie es die Nutzungsbedingungen vorsehen. Bewahre die Schlüssel sicher auf und widerrufe sofort einen Schlüssel, von dem du glaubst, dass er in falsche Hände geraten ist.
- Die Limits von LinkedIn sind real. LinkedIn mag keine automatisierte Nutzung: Unsere Limits gibt es, um das Risiko zu verringern, sie heben es nicht auf. Das Risiko einer Einschränkung des Kontos bleibt bei dir.
Was die API nicht tut #
- Sie verbindet keine anderen Konten und stellt keine Verbindungen zu LinkedIn her oder löscht sie: Die Verbindung erfolgt nur in der App.
- Sie gibt keinen freien Zugang zu LinkedIn: nur die Vorgänge auf dieser Seite, jeder mit seinen Limits.
- Sie überschreitet nie die Limits deines Kontos, auch nicht, wenn du sie darum bittest.
- Sie speichert deine Gespräche nicht.
- Sie arbeitet mit keinen anderen Plattformen als LinkedIn.