Klantly Developers

Website-Formular zum Lead

Legen Sie jede Anfrage über das Formular auf Ihrer Website sofort als Lead in Klantly an, mit der Nachricht als Notiz und auf Wunsch einem Deal.

Ein Besucher füllt das Kontaktformular auf Ihrer Website aus. Mit wenigen Anfragen steht diese Anfrage als Lead in Klantly, mit der Nachricht dazu und einer Karte auf Ihrem Pipeline-Board.

Warnung

Senden Sie das Formular an Ihren eigenen Server und stellen Sie die API-Anfragen von dort. Ein API-Schlüssel gehört nie in den Code Ihrer Website: Dort kann ihn jeder lesen.

Was Sie brauchen

Einen API-Schlüssel mit diesen Scopes:

Scope Wofür
customers.read Prüfen, ob die E-Mail-Adresse schon bekannt ist.
customers.write Den Lead erstellen und die Notiz hinzufügen.
deals.write Optional: den Lead auf das Pipeline-Board setzen.

Schritt 1: Gibt es den Kunden schon?

Eine E-Mail-Adresse ist innerhalb Ihres Unternehmens eindeutig. Prüfen Sie daher zuerst, ob sie schon bekannt ist:

cURL
curl --globoff "https://app.klantly.com/api/v1/customers?filter[email]=jan@example.com" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"

Ist data leer, erstellen Sie den Lead. Andernfalls verwenden Sie die id des Kunden, den Sie zurückbekommen.

Schritt 2: Den Lead erstellen

cURL
curl -X POST "https://app.klantly.com/api/v1/customers" \
  -H "Authorization: Bearer $KLANTLY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: form-8f14e45f" \
  -d '{"email": "jan@example.com", "name": "Jan de Vries", "phone": "+31 6 12345678"}'

Geben Sie jeder Einsendung einen eigenen Idempotency-Key, zum Beispiel die ID der Einsendung in Ihrer eigenen Datenbank. Klickt ein Besucher zweimal auf Senden oder versucht Ihr Server es nach einem Timeout erneut, entsteht trotzdem nur ein Lead. Mehr dazu unter Idempotenz.

Kommen zwei Einsendungen mit derselben E-Mail-Adresse fast gleichzeitig an, kann die zweite einen 422 mit einem Fehler bei email liefern. Suchen Sie den Kunden dann erneut, wie in Schritt 1.

Schritt 3: Die Nachricht als Notiz

cURL
curl -X POST "https://app.klantly.com/api/v1/customers/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/notes" \
  -H "Authorization: Bearer $KLANTLY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: form-8f14e45f-note" \
  -d '{"content": "Über das Website-Formular: Ich hätte gern ein Angebot für eine Terrassenüberdachung von 5 mal 3 Metern."}'

Schritt 4: Ein Deal auf dem Board (optional)

cURL
curl -X POST "https://app.klantly.com/api/v1/deals" \
  -H "Authorization: Bearer $KLANTLY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: form-8f14e45f-deal" \
  -d '{"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70", "title": "Terrassenüberdachung 5x3 m"}'

Ein Kunde hat nur einen offenen Deal gleichzeitig. Gibt es schon einen, erhalten Sie 409 mit dem Code conflict und der ID dieses Deals in deal_id. Das ist kein echter Fehler: Die Anfrage gehört dann zu dem Deal, der bereits läuft.

Alles zusammen

PHP
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;

function sendLeadToKlantly(array $form, string $submissionId): void
{
    $klantly = new Client([
        'base_uri' => 'https://app.klantly.com/api/v1/',
        'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
    ]);

    // 1. Gibt es den Kunden schon?
    $found = json_decode((string) $klantly->get('customers', [
        'query' => ['filter' => ['email' => $form['email']]],
    ])->getBody(), true)['data'];

    // 2. Wenn nicht: den Lead erstellen.
    $customer = $found[0] ?? json_decode((string) $klantly->post('customers', [
        'headers' => ['Idempotency-Key' => "form-{$submissionId}"],
        'json' => array_filter(['email' => $form['email'], 'name' => $form['name'], 'phone' => $form['phone']]),
    ])->getBody(), true)['data'];

    // 3. Die Nachricht als Notiz.
    $klantly->post("customers/{$customer['id']}/notes", [
        'headers' => ['Idempotency-Key' => "form-{$submissionId}-note"],
        'json' => ['content' => $form['message']],
    ]);

    // 4. Ein Deal; 409 bedeutet, dass es schon einen offenen Deal gibt.
    try {
        $klantly->post('deals', [
            'headers' => ['Idempotency-Key' => "form-{$submissionId}-deal"],
            'json' => ['customer_id' => $customer['id'], 'title' => $form['subject']],
        ]);
    } catch (ClientException $e) {
        if ($e->getResponse()->getStatusCode() !== 409) {
            throw $e;
        }
    }
}
Node.js
const BASE_URL = 'https://app.klantly.com/api/v1';

async function klantly(method, path, { body, idempotencyKey } = {}) {
  const response = await fetch(`${BASE_URL}/${path}`, {
    method,
    headers: {
      Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
      ...(body ? { 'Content-Type': 'application/json' } : {}),
      ...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {}),
    },
    body: body ? JSON.stringify(body) : undefined,
  });

  return { status: response.status, json: await response.json() };
}

export async function sendLeadToKlantly(form, submissionId) {
  // 1. Gibt es den Kunden schon?
  const found = await klantly('GET', `customers?${new URLSearchParams({ 'filter[email]': form.email })}`);
  if (found.status !== 200) throw new Error(found.json.detail ?? found.json.title);
  let customer = found.json.data[0];

  // 2. Wenn nicht: den Lead erstellen.
  if (!customer) {
    const created = await klantly('POST', 'customers', {
      body: { email: form.email, name: form.name, ...(form.phone ? { phone: form.phone } : {}) },
      idempotencyKey: `form-${submissionId}`,
    });
    if (created.status !== 201) throw new Error(created.json.detail ?? created.json.title);
    customer = created.json.data;
  }

  // 3. Die Nachricht als Notiz.
  await klantly('POST', `customers/${customer.id}/notes`, {
    body: { content: form.message },
    idempotencyKey: `form-${submissionId}-note`,
  });

  // 4. Ein Deal; 409 bedeutet, dass es schon einen offenen Deal gibt.
  const deal = await klantly('POST', 'deals', {
    body: { customer_id: customer.id, title: form.subject },
    idempotencyKey: `form-${submissionId}-deal`,
  });
  if (deal.status !== 201 && deal.status !== 409) throw new Error(deal.json.detail ?? deal.json.title);
}

Fehler abfangen

  • 422 mit validation_failed: zum Beispiel eine ungültige E-Mail-Adresse. In errors steht pro Feld, was nicht stimmt. Lassen Sie den Besucher es korrigieren.
  • 429 mit rate_limited: Warten Sie die Anzahl Sekunden aus Retry-After ab und versuchen Sie es erneut. Siehe Rate Limits.
  • Ein 5xx-Fehler oder ein Timeout: Versuchen Sie es später erneut mit demselben Idempotency-Key. Stellen Sie die Einsendung am besten in eine Warteschlange, dann geht nie eine Anfrage verloren.

Tipp

Möchten Sie wissen, wann ein Lead zum Kunden wird? Hören Sie mit einem Webhook auf das Event customer.converted.

Zuletzt aktualisiert am 15. September 2026