Klantly Developers

Termine aus Ihrer eigenen Planung

Übertragen Sie Termine aus Ihrer eigenen Planungssoftware in den Klantly-Kalender, halten Sie sie aktuell und lassen Sie Klantly dem Kunden eine Bestätigung senden.

Planen Sie in einem eigenen System, zum Beispiel auf einer Plantafel für Ihre Techniker? Mit der API übertragen Sie diese Termine in den Kalender von Klantly. Sie sehen sie dann beim Kunden und auf Ihrem Pipeline-Board, und Ihr Kunde erhält dieselben Bestätigungen und Erinnerungen wie bei einem Termin, den Sie in Klantly selbst anlegen.

Was Sie brauchen

Einen API-Schlüssel mit diesen Scopes:

Scope Wofür
customers.read Den Kunden anhand der E-Mail-Adresse suchen.
customers.write Einen Kunden anlegen, der noch nicht existiert.
appointments.read Termine und freie Zeitfenster abrufen.
appointments.write Termine planen, verschieben, absagen und abschließen.
appointments.send Dem Kunden eine Bestätigung, Verschiebung, Absage oder Erinnerung per E-Mail senden.

Die Funktion Termine muss für Ihr Unternehmen aktiv sein.

Schritt 1: der Kunde

Ein Termin gehört immer zu einem Kunden; Name, E-Mail-Adresse und Telefon kommen vom Kunden. Suchen Sie den Kunden mit filter[email] oder legen Sie ihn an, wie in Website-Formular zum Lead. Speichern Sie seine id in Ihrem eigenen System, dann müssen Sie ihn beim nächsten Mal nicht erneut suchen.

Schritt 2: den Termin planen

cURL
curl -X POST "https://app.klantly.com/api/v1/appointments" \
  -H "Authorization: Bearer $KLANTLY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: planning-4711" \
  -d '{"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70", "title": "Aufmaß Terrassendach", "starts_at": "2026-10-06T09:00:00+02:00", "ends_at": "2026-10-06T10:30:00+02:00", "location": "Dorfstraße 12, Köln"}'
  • Senden Sie Zeiten immer mit Zeitzone, etwa +02:00 oder Z. In der Antwort stehen sie in UTC.
  • Lassen Sie ends_at weg, dauert der Termin so lange wie die Terminart in appointment_type_id, sonst die Standarddauer aus Ihren Termineinstellungen.
  • Setzen Sie die ID aus Ihrer eigenen Planung in den Idempotency-Key. Versucht Ihr Server es nach einem Timeout erneut, entsteht trotzdem nur ein Termin.
  • Speichern Sie die id aus der Antwort neben Ihrer eigenen ID. Sie brauchen sie, um den Termin später zu bearbeiten.

Ein neuer Termin steht auf confirmed. Muss er noch bestätigt werden, senden Sie "status": "pending" mit. Klantly prüft beim Planen keine Verfügbarkeit: Ihre Planung ist maßgeblich.

Hinweis

Die API sendet dem Kunden beim Planen keine E-Mail. Das tun Sie bewusst, in Schritt 4. Achtung: Hat Ihr Unternehmen Automationen auf „Termin geplant“ (etwa eine WhatsApp-Bestätigung), laufen diese wie bei einem Termin im Kalender. Schalten Sie sie kurz aus, wenn Sie Ihre Planung zum ersten Mal einlesen.

Schritt 3: verschieben, absagen und abschließen

Ändert sich die Zeit in Ihrer Planung, senden Sie nur die neue Zeit:

cURL
curl -X PATCH "https://app.klantly.com/api/v1/appointments/0b6f3a52-7c1d-4e8a-9b2f-5d4c3b2a1f09" \
  -H "Authorization: Bearer $KLANTLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"starts_at": "2026-10-08T13:00:00+02:00"}'

Ohne ends_at bleibt die Dauer gleich, und rescheduled_at zeigt, dass der Termin verschoben wurde. Findet ein Termin nicht statt, sagen Sie ihn ab, statt ihn zu löschen: So bleibt er beim Kunden sichtbar.

cURL
curl -X POST "https://app.klantly.com/api/v1/appointments/0b6f3a52-7c1d-4e8a-9b2f-5d4c3b2a1f09/cancel" \
  -H "Authorization: Bearer $KLANTLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reason": "Kunde ist krank"}'

Hat der Besuch stattgefunden, schließen Sie den Termin mit POST /appointments/{id}/complete ab. Steht Ihre Lead-Umwandlung auf „Termin abgeschlossen“, wird ein Lead dabei zum Kunden, genau wie im Kalender.

Schritt 4: den Kunden informieren

cURL
curl -X POST "https://app.klantly.com/api/v1/appointments/0b6f3a52-7c1d-4e8a-9b2f-5d4c3b2a1f09/notify" \
  -H "Authorization: Bearer $KLANTLY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: planning-4711-confirmation" \
  -d '{"message": "confirmation"}'

Klantly sendet die E-Mail mit der Vorlage, die Sie in Klantly eingerichtet haben. Die Nachricht muss zum Status des Termins passen:

message Möglich bei Status
confirmation confirmed
reschedule pending oder confirmed
cancellation cancelled
reminder confirmed

Passt sie nicht, hat der Termin kein Datum, hat der Kunde keine E-Mail-Adresse oder hat Ihr Unternehmen die Vorlage ausgeschaltet, erhalten Sie 409 mit dem Code invalid_state_transition. In detail steht der Grund.

In die andere Richtung: Buchungen aus Klantly

Kunden können auch selbst über Ihre Buchungsseite buchen. Um diese in Ihrer Planung zu sehen, hören Sie mit einem Webhook auf appointment.created, appointment.updated, appointment.confirmed, appointment.cancelled, appointment.completed und appointment.deleted. Eine Statusänderung kommt als eigenes Event, nicht als appointment.updated. Jedes Event enthält den ganzen Termin.

Diese Events kommen auch für die Termine, die Sie selbst über die API geplant haben. Erkennen Sie sie an der id, die Sie in Schritt 2 gespeichert haben, sonst stehen sie doppelt in Ihrer Planung.

Freie Zeitfenster abrufen

Möchten Sie in Ihrem eigenen System sehen, wo laut Klantly noch Platz ist, rufen Sie die freien Zeitfenster ab:

cURL
curl --globoff "https://app.klantly.com/api/v1/availability?date_from=2026-10-06&date_to=2026-10-10&duration_minutes=90" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"

Das sind fast dieselben Zeitfenster wie auf Ihrer Buchungsseite: Arbeitszeiten, Pausen, gesperrte Tage, wie weit im Voraus gebucht werden darf und die bereits gebuchten Termine zählen mit. Nur Ihr Google-Kalender zählt hier nicht, und die Zeiten gelten für das ganze Unternehmen, nicht pro Mitarbeiter. Sie rufen höchstens 31 Tage auf einmal ab.

Alles zusammen

PHP
use GuzzleHttp\Client;

/**
 * $klantly ist ein Guzzle-Client mit der Basis-URL und Ihrem API-Schlüssel, $job ein Termin aus Ihrer
 * eigenen Planung. Gibt die ID des Termins in Klantly zurück: Speichern Sie sie beim Job.
 */
function syncAppointment(Client $klantly, array $job): string
{
    $body = [
        'title' => $job['title'],
        'starts_at' => $job['start']->format(DATE_ATOM),
        'ends_at' => $job['end']->format(DATE_ATOM),
        'location' => $job['address'],
    ];

    // Neu: planen und dem Kunden eine Bestätigung senden.
    if ($job['klantly_id'] === null) {
        $appointment = json_decode((string) $klantly->post('appointments', [
            'headers' => ['Idempotency-Key' => "planning-{$job['id']}"],
            'json' => $body + ['customer_id' => $job['klantly_customer_id']],
        ])->getBody(), true)['data'];

        $klantly->post("appointments/{$appointment['id']}/notify", [
            'headers' => ['Idempotency-Key' => "planning-{$job['id']}-confirmation"],
            'json' => ['message' => 'confirmation'],
        ]);

        return $appointment['id'];
    }

    // Vorhanden: bearbeiten und bei einer neuen Zeit dem Kunden die Verschiebung mitteilen.
    $klantly->patch("appointments/{$job['klantly_id']}", ['json' => $body]);

    if ($job['time_changed']) {
        $klantly->post("appointments/{$job['klantly_id']}/notify", [
            'json' => ['message' => 'reschedule'],
        ]);
    }

    return $job['klantly_id'];
}
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,
  });

  const json = await response.json();
  if (!response.ok) throw new Error(json.detail ?? json.title);
  return json;
}

// job ist ein Termin aus Ihrer eigenen Planung; start und end sind ISO 8601 mit Zeitzone.
// Gibt die ID des Termins in Klantly zurück: Speichern Sie sie beim Job.
export async function syncAppointment(job) {
  const body = { title: job.title, starts_at: job.start, ends_at: job.end, location: job.address };

  // Neu: planen und dem Kunden eine Bestätigung senden.
  if (!job.klantlyId) {
    const { data } = await klantly('POST', 'appointments', {
      body: { ...body, customer_id: job.klantlyCustomerId },
      idempotencyKey: `planning-${job.id}`,
    });
    await klantly('POST', `appointments/${data.id}/notify`, {
      body: { message: 'confirmation' },
      idempotencyKey: `planning-${job.id}-confirmation`,
    });
    return data.id;
  }

  // Vorhanden: bearbeiten und bei einer neuen Zeit dem Kunden die Verschiebung mitteilen.
  await klantly('PATCH', `appointments/${job.klantlyId}`, { body });
  if (job.timeChanged) {
    await klantly('POST', `appointments/${job.klantlyId}/notify`, { body: { message: 'reschedule' } });
  }
  return job.klantlyId;
}

Fehler abfangen

  • 422 mit validation_failed: zum Beispiel eine Zeit ohne Zeitzone. In errors steht pro Feld, was falsch ist.
  • 409 mit invalid_state_transition: zum Beispiel einen abgesagten Termin abschließen oder eine Nachricht, die nicht zum Status passt.
  • 429 mit rate_limited: Warten Sie die Sekunden aus Retry-After ab und versuchen Sie es erneut. Lesen Sie Ihre Planung zum ersten Mal ein, verteilen Sie die Anfragen über die Zeit. Siehe Rate limits.

Zuletzt aktualisiert am 15. September 2026