Skip to main content

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.

Webhooks let Talkturo push events to your server in real time, so you do not need to poll the API for updates. Talkturo fires two categories of webhook: call status events from the telephony layer, and billing events from your subscription provider. You configure the target URL for each webhook type in the Talkturo dashboard, and Talkturo sends a signed POST request to that URL when an event occurs.

Configure webhook URLs

1

Open Integrations settings

In the Talkturo dashboard, navigate to SettingsIntegrations.
2

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.
3

Save and test

Click Save. Use the Send test event button to verify your endpoint receives and processes the payload correctly.
Your webhook endpoint must be reachable over the public internet. Localhost URLs will not receive events in production.

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 SettingsIntegrationsWebhook Secrets.
Always verify signatures in production. Skipping signature verification exposes your endpoint to spoofed events.

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

EventDescription
call.initiatedAn outbound call was placed or an inbound call arrived
call.answeredThe callee picked up
call.completedThe call ended normally
call.hangupOne party disconnected the call
call.machine_detection.endedAnswering machine detection completed
call.recording.savedA call recording was saved to storage

Payload structure

{
  "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
  }
}
FieldTypeDescription
typestringThe event type (for example, call.completed)
data.call_control_idstringTelnyx call control identifier
data.call_leg_idstringUnique identifier for this call leg
data.call_session_idstringGroups all legs in the same session
data.client_statestringBase64-encoded metadata passed when the call was initiated
data.connection_idstringThe Telnyx connection the call used
data.fromstringCaller number in E.164 format
data.tostringCallee number in E.164 format
data.directionstringinbound or outbound
data.start_timestringISO 8601 datetime the call started
data.end_timestringISO 8601 datetime the call ended (call.completed only)
data.duration_secsintegerCall duration in seconds (call.completed only)

Example handler

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

EventDescription
subscription.createdA new subscription was activated
subscription.updatedSubscription plan or status changed
subscription.cancelledSubscription was cancelled
subscription.expiredSubscription reached end of billing period
invoice.paidA payment was successfully collected
invoice.payment_failedA payment attempt failed

Payload structure

{
  "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..."
    }
  }
}
FieldTypeDescription
typestringThe billing event type
data.subscription_idstringThe subscription identifier from your billing provider
data.customer_idstringThe customer identifier from your billing provider
data.plan_namestringThe name of the plan (for example, Growth, Enterprise)
data.statusstringSubscription status (active, cancelled, past_due)
data.current_period_startstringISO 8601 start of the current billing period
data.current_period_endstringISO 8601 end of the current billing period
data.metadata.team_idstringThe Talkturo team ID associated with this subscription

Example handler

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.