> ## Documentation Index
> Fetch the complete documentation index at: https://docs.telli.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Eigener Kalender

> Verbinde deine eigene Kalender- oder Scheduling-API mit telli, damit Agenten Verfügbarkeiten abrufen und Termine über eigene Endpunkte buchen können.

<img src="https://mintcdn.com/telli/B1-jcrEXZ5AA-uwV/images/integrations/telli-custom-calendar-light.svg?fit=max&auto=format&n=B1-jcrEXZ5AA-uwV&q=85&s=3c4174bb69834968827966a3ab2c9904" className="block dark:hidden" alt="telli + Custom calendar integration" width="1200" height="360" data-path="images/integrations/telli-custom-calendar-light.svg" />

<img src="https://mintcdn.com/telli/B1-jcrEXZ5AA-uwV/images/integrations/telli-custom-calendar-dark.svg?fit=max&auto=format&n=B1-jcrEXZ5AA-uwV&q=85&s=a169c305bf3a522875086a591adbbfd1" className="hidden dark:block" alt="telli + Custom calendar integration" width="1200" height="360" data-path="images/integrations/telli-custom-calendar-dark.svg" />

## Überblick

Mit der Option für einen eigenen Kalender kannst du dein bestehendes Kalender- oder Terminsystem mit telli-Agenten verbinden.

Nutze diese Option, wenn telli mit deiner eigenen Buchungsinfrastruktur statt mit einem eingebauten Anbieter arbeiten soll.

## Bevor du beginnst

* Ein telli-Agent, der Termine buchen soll
* Ein API-Endpoint, der verfügbare Terminslots zurückgibt
* Ein API-Endpoint, der einen ausgewählten Terminslot bucht
* Öffentlich erreichbare Endpunkte, die telli aufrufen kann

## Eigenen Kalender in telli einrichten

<Steps>
  <Step title="Agent öffnen">
    Öffne den Agenten, den du in telli konfigurieren willst.
  </Step>

  <Step title="Generic Calendar auswählen">
    Wähle in **Kalender-Integration** die Option **Generic Calendar**.
  </Step>

  <Step title="Endpoints eintragen">
    Hinterlege die **Available URL** und, wenn telli Buchungen ausführen soll, die **Book URL** deiner Scheduling-API.
  </Step>

  <Step title="Agent speichern">
    Speichere den Agenten, damit telli Verfügbarkeiten abrufen und Termine über deine Endpunkte buchen kann.
  </Step>
</Steps>

## Request-Ablauf

```mermaid theme={null}
sequenceDiagram
    participant Customer
    participant telli Agent
    participant Your System

    Customer->>telli Agent: "I'd like to make an appointment"
    telli Agent->>Your System: Request available slots
    Your System-->>telli Agent: Return list of time slots
    telli Agent->>Customer: Present available appointments
    Customer->>telli Agent: Select preferred time
    telli Agent->>Your System: Book selected slot
    Your System-->>telli Agent: Confirm booking
    telli Agent->>Customer: Confirm appointment
```

## Endpunkte

Für eine vollständige Terminbuchung implementierst du beide Endpunkte unten. Wenn telli nur Verfügbarkeiten abrufen soll, kann der Buchungs-Endpoint leer bleiben.

### Verfügbare Slots abrufen

Dieser Endpoint gibt eine Liste verfügbarer Terminslots zurück.

```json theme={null}
POST /available

{
  "contact": {
    "id": "telli contact identifier",
    "type": "Contact",
    "externalId": "your contact identifier",
    "externalUrl": null,
    "salutation": null,
    "firstName": "Ada",
    "lastName": "Lovelace",
    "phoneNumber": "+4915112345678",
    "timezoneIana": "Europe/Berlin",
    "email": "ada@example.com",
    "createdAt": "2026-03-13T09:00:00.000Z",
    "updatedAt": "2026-03-13T09:00:00.000Z",
    "properties": [
      {
        "key": "appointment_type",
        "value": "demo",
        "dataType": "select",
        "label": "Appointment Type"
      }
    ]
  },
  // Veraltetes Legacy-Feld, bleibt aus Gründen der Rückwärtskompatibilität erhalten.
  "contact_id": "telli contact identifier",
  // Veraltetes Legacy-Feld, bleibt aus Gründen der Rückwärtskompatibilität erhalten.
  "external_contact_id": "your contact identifier",
  // Veraltetes Legacy-Feld, bleibt aus Gründen der Rückwärtskompatibilität erhalten.
  "contact_details": {
    "foobar": "baz"
  }
}
```

```json theme={null}
{
  "available": [
    {
      "start_iso": "2024-01-01T10:00:00.000",
      "end_iso": "2024-01-01T10:30:00.000"
    }
  ]
}
```

### Termin buchen

Dieser Endpoint verarbeitet die eigentliche Buchung des ausgewählten Terminslots.

```json theme={null}
POST /book

{
  "contact": {
    "id": "telli contact identifier",
    "type": "Contact",
    "externalId": "your contact identifier",
    "externalUrl": null,
    "salutation": null,
    "firstName": "Ada",
    "lastName": "Lovelace",
    "phoneNumber": "+4915112345678",
    "timezoneIana": "Europe/Berlin",
    "email": "ada@example.com",
    "createdAt": "2026-03-13T09:00:00.000Z",
    "updatedAt": "2026-03-13T09:00:00.000Z",
    "properties": [
      {
        "key": "appointment_type",
        "value": "demo",
        "dataType": "select",
        "label": "Appointment Type"
      }
    ]
  },
  // Veraltetes Legacy-Feld, bleibt aus Gründen der Rückwärtskompatibilität erhalten.
  "contact_id": "telli contact identifier",
  // Veraltetes Legacy-Feld, bleibt aus Gründen der Rückwärtskompatibilität erhalten.
  "external_contact_id": "your contact identifier",
  "start_iso": "2024-01-01T10:00:00.000",
  // Veraltetes Legacy-Feld, bleibt aus Gründen der Rückwärtskompatibilität erhalten.
  "contact_details": {
    "foobar": "baz"
  }
}
```

```json theme={null}
{
  "status": "success"
}
```

```json theme={null}
{
  "status": "failed",
  "reason": "Appointment slot is no longer available"
}
```

## Hinweise zur Umsetzung

* `contact` ist das bevorzugte Feld für Kontaktdaten und folgt dem V2-Kontaktformat inklusive typisierter `properties`
* `contact_id`, `external_contact_id` und `contact_details` sind veraltete Legacy-Felder, die aus Gründen der Rückwärtskompatibilität weiterhin mitgesendet werden
* `contact_details` enthält die frühere flache Key-Value-Struktur aus den V1-Dynamic-Variables
* Nutze UTC-Zeitstempel im ISO-8601-Format
* telli verwendet `start_iso` als Kennung des Slots
* Gib HTTP-200-Antworten zurück und signalisiere Erfolg oder Fehler im Response-Body
* Stelle sicher, dass telli deine Endpunkte erreichen kann

## Request-Authentifizierung

Wenn du Requests von telli prüfen willst, verifiziere den Header `x-telli-signature`.

```javascript theme={null}
const crypto = require("crypto");

function verifyRequest(payload, signature, apiKey) {
  const expectedSignature = crypto
    .createHmac("sha256", apiKey)
    .update(JSON.stringify(payload))
    .digest("hex");

  return signature === expectedSignature;
}
```
