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

# Talkturo-Webhooks für Anrufereignisse

> Erhalten Sie HTTP-Rückrufe in Echtzeit für Anrufstatusänderungen und Abrechnungsereignisse. Konfigurieren Sie Webhook-URLs in Ihrem Dashboard und überprüfen Sie Payloads mit Signaturen.

Webhooks liefern Ereignisse in Echtzeit an Ihren Server — ohne API-Polling. Talkturo unterscheidet Anrufstatus-Ereignisse aus der Telefonie und Abrechnungsereignisse vom Abo-Anbieter. Ziel-URLs tragen Sie im Dashboard ein; bei einem Ereignis sendet Talkturo eine signierte `POST`-Anfrage an diese URL.

***

## Configure webhook URLs

<Steps>
  <Step title="Open Integrations settings">
    In the Talkturo dashboard, navigate to **Settings** → **Integrations**.
  </Step>

  <Step title="Enter your endpoint URL">
    In the **Webhooks** section, enter the publicly reachable HTTPS URL of your server for each webhook type. The URL must return a `200` status code within 5 seconds or Talkturo will treat the delivery as failed.
  </Step>

  <Step title="Save and test">
    Click **Save**. Use the **Send test event** button to verify your endpoint receives and processes the payload correctly.
  </Step>
</Steps>

<Warning>
  Your webhook endpoint must be reachable over the public internet. Localhost URLs will not receive events in production.
</Warning>

***

## Webhook security

Talkturo includes a cryptographic signature in the `Telnyx-Signature-Ed25519` header (for call webhooks) and a `X-Signature` header (for billing webhooks). Verify this signature before processing the payload to confirm the request originated from Talkturo and was not tampered with.

The signature is a base64-encoded HMAC or Ed25519 signature computed over the raw request body. Compare it against a signature you compute using your webhook secret, available in the dashboard under **Settings** → **Integrations** → **Webhook Secrets**.

<Note>
  Always verify signatures in production. Skipping signature verification exposes your endpoint to spoofed events.
</Note>

***

## Call status webhook

`POST /api/db/webhook`

Talkturo sends call status events to this endpoint as calls progress through their lifecycle. Configure the URL your server exposes for this path in the dashboard.

### Event types

| Event                          | Description                                            |
| ------------------------------ | ------------------------------------------------------ |
| `call.initiated`               | An outbound call was placed or an inbound call arrived |
| `call.answered`                | The callee picked up                                   |
| `call.completed`               | The call ended normally                                |
| `call.hangup`                  | One party disconnected the call                        |
| `call.machine_detection.ended` | Answering machine detection completed                  |
| `call.recording.saved`         | A call recording was saved to storage                  |

### Payload structure

````json theme={null}
{
  "type": "call.completed",
  "data": {
    "call_control_id": "v2:T02llLL...",
    "call_leg_id": "0cf8a7d8-...",
    "call_session_id": "84a7e8b2-...",
    "client_state": "eyJhc3Npc3RhbnRJZCI6...",
    "connection_id": "1234567890",
    "from": "+14155550101",
    "to": "+12025551234",
    "direction": "outbound",
    "start_time": "2024-01-15T14:00:00Z",
    "end_time": "2024-01-15T14:04:32Z",
    "duration_secs": 272
  }
}
```| Feld | Geben Sie | ein Beschreibung |
|-------|------|-------------|
| „Typ“ | Zeichenfolge | Der Ereignistyp (z. B. „call.completed“) |
| `data.call_control_id` | Zeichenfolge | Telnyx-Anrufsteuerungskennung |
| `data.call_leg_id` | Zeichenfolge | Eindeutiger Bezeichner für diesen Anrufabschnitt |
| `data.call_session_id` | Zeichenfolge | Gruppiert alle Beine in derselben Sitzung |
| `data.client_state` | Zeichenfolge | Base64-codierte Metadaten, die bei der Initiierung des Anrufs übergeben wurden |
| `data.connection_id` | Zeichenfolge | Die vom Anruf verwendete Telnyx-Verbindung |
| `data.from` | Zeichenfolge | Anrufernummer im E.164-Format |
| `data.to` | Zeichenfolge | Angerufene Nummer im E.164-Format |
| `data.direction` | Zeichenfolge | „eingehend“ oder „ausgehend“ |
| `data.start_time` | Zeichenfolge | ISO 8601 Datum und Uhrzeit des Anrufbeginns |
| `data.end_time` | Zeichenfolge | ISO 8601 Datum und Uhrzeit des Endes des Anrufs (nur „call.completed“) |
| `data.duration_secs` | Ganzzahl | Anrufdauer in Sekunden (nur „call.completed“) |

### Beispielhandler```javascript
import { createHmac } from 'crypto';

export async function POST(request) {
  const body = await request.text();
  const signature = request.headers.get('Telnyx-Signature-Ed25519');

  // Verify the signature using your webhook secret from the dashboard
  const isValid = verifyTelnyxSignature(body, signature, process.env.WEBHOOK_SECRET);
  if (!isValid) {
    return new Response('Unauthorized', { status: 401 });
  }

  const event = JSON.parse(body);

  switch (event.type) {
    case 'call.completed':
      await handleCallCompleted(event.data);
      break;
    case 'call.answered':
      await handleCallAnswered(event.data);
      break;
  }

  return new Response('OK', { status: 200 });
}
````

***

## Billing webhook

`POST /api/billing/webhook`

Talkturo sends subscription and payment events to this endpoint when your billing status changes. Talkturo supports both Stripe and LemonSqueezy as billing providers.

### Event types

| Event                    | Description                                |
| ------------------------ | ------------------------------------------ |
| `subscription.created`   | A new subscription was activated           |
| `subscription.updated`   | Subscription plan or status changed        |
| `subscription.cancelled` | Subscription was cancelled                 |
| `subscription.expired`   | Subscription reached end of billing period |
| `invoice.paid`           | A payment was successfully collected       |
| `invoice.payment_failed` | A payment attempt failed                   |

### Payload structure

````json theme={null}
{
  "type": "subscription.created",
  "data": {
    "subscription_id": "sub_...",
    "customer_id": "cus_...",
    "plan_name": "Growth",
    "status": "active",
    "current_period_start": "2024-01-15T00:00:00Z",
    "current_period_end": "2024-02-15T00:00:00Z",
    "metadata": {
      "team_id": "team_01j..."
    }
  }
}
```| Feld | Geben Sie | ein Beschreibung |
|-------|------|-------------|
| „Typ“ | Zeichenfolge | Der Abrechnungsereignistyp |
| `data.subscription_id` | Zeichenfolge | Die Abonnement-ID Ihres Abrechnungsanbieters |
| `data.customer_id` | Zeichenfolge | Die Kundenkennung Ihres Abrechnungsanbieters |
| `data.plan_name` | Zeichenfolge | Der Name des Plans (z. B. „Growth“, „Enterprise“) |
| `data.status` | Zeichenfolge | Abonnementstatus („aktiv“, „abgebrochen“, „überfällig“) |
| `data.current_period_start` | Zeichenfolge | ISO 8601 Beginn des aktuellen Abrechnungszeitraums |
| `data.current_period_end` | Zeichenfolge | ISO 8601 Ende des aktuellen Abrechnungszeitraums |
| `data.metadata.team_id` | Zeichenfolge | Die diesem Abonnement zugeordnete Talkturo-Team-ID |

### Beispielhandler```javascript
export async function POST(request) {
  const body = await request.text();
  const signature = request.headers.get('X-Signature');

  // Verify the signature using your billing webhook secret
  const isValid = verifySignature(body, signature, process.env.WEBHOOK_SECRET);
  if (!isValid) {
    return new Response('Unauthorized', { status: 401 });
  }

  const event = JSON.parse(body);

  switch (event.type) {
    case 'subscription.created':
      await activateTeamFeatures(event.data.metadata.team_id, event.data.plan_name);
      break;
    case 'subscription.cancelled':
      await downgradeTeamFeatures(event.data.metadata.team_id);
      break;
  }

  return new Response('OK', { status: 200 });
}
````

***

## Retry behavior

If your endpoint does not return a `2xx` status code within 5 seconds, Talkturo retries the delivery with exponential backoff. To avoid processing duplicate events, make your webhook handler idempotent — check whether you have already processed a given event ID before acting on it.
