Klantly Developers

Websiteformulier naar lead

Zet elke aanvraag via het formulier op je website direct als lead in Klantly, met het bericht als notitie en desgewenst een deal.

Een bezoeker vult het contactformulier op je website in. Met een paar verzoeken staat die aanvraag als lead in Klantly, met het bericht erbij en een kaart op je pipelinebord.

Waarschuwing

Stuur het formulier naar je eigen server en doe de API-verzoeken vanaf daar. Een API-sleutel hoort nooit in de code van je website: iedereen kan hem daar lezen.

Wat je nodig hebt

Een API-sleutel met deze scopes:

Scope Waarvoor
customers.read Kijken of het e-mailadres al bekend is.
customers.write De lead aanmaken en de notitie plaatsen.
deals.write Optioneel: de lead op het pipelinebord zetten.

Stap 1: bestaat de klant al?

Een e-mailadres is uniek binnen je bedrijf. Kijk daarom eerst of het al bekend is:

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

Is data leeg, dan maak je de lead aan. Anders gebruik je de id van de klant die je terugkrijgt.

Stap 2: de lead aanmaken

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"}'

Geef elke inzending een eigen Idempotency-Key, bijvoorbeeld de id van de inzending in je eigen database. Klikt een bezoeker twee keer op verzenden, of probeert je server het na een time-out opnieuw, dan ontstaat er toch maar één lead. Meer daarover in Idempotentie.

Komen er twee inzendingen met hetzelfde e-mailadres vrijwel tegelijk binnen, dan kan de tweede een 422 geven met een fout bij email. Zoek de klant dan opnieuw op, zoals in stap 1.

Stap 3: het bericht als notitie

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": "Via het websiteformulier: ik wil graag een offerte voor een veranda van 5 bij 3 meter."}'

Stap 4: een deal op het bord (optioneel)

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": "Veranda 5x3 m"}'

Een klant heeft maar één open deal tegelijk. Heeft hij er al een, dan krijg je 409 met de code conflict en de id van die deal in deal_id. Dat is geen echte fout: de aanvraag hoort dan bij de deal die al loopt.

Alles samen

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. Bestaat de klant al?
    $found = json_decode((string) $klantly->get('customers', [
        'query' => ['filter' => ['email' => $form['email']]],
    ])->getBody(), true)['data'];

    // 2. Zo niet: de lead aanmaken.
    $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. Het bericht als notitie.
    $klantly->post("customers/{$customer['id']}/notes", [
        'headers' => ['Idempotency-Key' => "form-{$submissionId}-note"],
        'json' => ['content' => $form['message']],
    ]);

    // 4. Een deal; 409 betekent dat er al een open deal is.
    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. Bestaat de klant al?
  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. Zo niet: de lead aanmaken.
  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. Het bericht als notitie.
  await klantly('POST', `customers/${customer.id}/notes`, {
    body: { content: form.message },
    idempotencyKey: `form-${submissionId}-note`,
  });

  // 4. Een deal; 409 betekent dat er al een open deal is.
  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);
}

Fouten opvangen

  • 422 met validation_failed: bijvoorbeeld een ongeldig e-mailadres. In errors staat per veld wat er mis is. Laat de bezoeker het verbeteren.
  • 429 met rate_limited: wacht het aantal seconden uit Retry-After en probeer het opnieuw. Zie Rate limits.
  • Een 5xx-fout of een time-out: probeer het later opnieuw met dezelfde Idempotency-Key. Zet de inzending het liefst in een wachtrij, dan gaat er nooit een aanvraag verloren.

Tip

Wil je weten wanneer een lead klant wordt? Luister met een webhook naar het event customer.converted.

Laatst bijgewerkt op 15 september 2026