LionVoice API v1

Base URL: https://api.lionvoice.app/api/v1. Sprache, Lion-AI und Telefonie — ein Produkt. Das Gehirn heißt überall Lion-AI; Engine-IDs sind intern und in API-Antworten optional.

Base URL: https://api.lionvoice.app/api/v1

Die API hat keinen Modellwahl-Parameter — Lion-AI ist das einzige sichtbare Gehirn.

5-Minuten-Quick-Start für Entwickler

1. Erzeuge im Dashboard einen API-Key (API keys) — das Secret erscheint nur einmal. 2. Lege einen Agenten an. 3. Hänge eine Telefonnummer (E.164) an und mappe sie. 4. Starte einen Test-Outbound-Anruf.

export LV_KEY='lv_live_…'

# 1) who am I
curl -sS https://api.lionvoice.app/api/v1/tenant \
  -H "Authorization: Bearer $LV_KEY"

# 2) create an agent
curl -sS https://api.lionvoice.app/api/v1/agents \
  -H "Authorization: Bearer $LV_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name":"Reception",
    "language":"en",
    "voice":"Lion-AI Warm",
    "prompt":"You are a receptionist. Address the caller as you.",
    "first_message":"Hi, how can I help?",
    "max_duration_sec":600
  }'

# 3) attach a number
curl -sS https://api.lionvoice.app/api/v1/phone-numbers \
  -H "Authorization: Bearer $LV_KEY" \
  -H "Content-Type: application/json" \
  -d '{"number_e164":"+37052636333","label":"Reception"}'

# 4) test outbound (use your own test line)
curl -sS https://api.lionvoice.app/api/v1/calls \
  -H "Authorization: Bearer $LV_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to_number":"+447554522255","agent_id":1}'

Authentifizierung

Create a key in the dashboard (API keys) or POST /keys. The secret is shown once. Send it as Bearer or X-LV-Api-Key. Ping does not need a key. A revoked key returns 401.

curl -sS https://api.lionvoice.app/api/v1/tenant \
  -H "Authorization: Bearer lv_live_…"
# or
  -H "X-LV-Api-Key: lv_live_…"

Limit: 120 Requests / Minute / API-Key. 429 retrybar.

GET /ping

Headers: none. Auth: none.

# Response 200
{
  "ok": true,
  "service": "LionVoice API",
  "version": "v1",
  "time": "2026-09-07T09:00:00+00:00"
}

GET /tenant

Headers: Authorization: Bearer lv_live_…

# Response 200
{
  "ok": true,
  "tenant": {
    "id": 1,
    "name": "AURIUS INTERNATIONAL LTD",
    "email": "ops@example.invalid",
    "plan": "studio",
    "plan_included_minutes": 500,
    "overage_eur_per_min": 0.09,
    "balance_eur": 12.50,
    "webhook_signing": {
      "algorithm": "hmac-sha256",
      "header": "X-LV-Signature",
      "prefix": "269d7d4b"
    },
    "sip": {
      "server": "sip.example.com",
      "username": "200",
      "port": 5060,
      "transport": "udp",
      "caller_id": "+37052636333",
      "status": "registered"
    },
    "features": {
      "callback_memory": 1,
      "named_greeting": 1,
      "cli_identity": 1,
      "birthday_calls": 0,
      "crm_bridge": 0
    }
  }
}
FieldMeaning
planCurrent plan slug
plan_included_minutesMinutes included this month
balance_eurWallet
sipHost / user / port / transport / status — never the password
webhook_signing.prefixFirst 8 chars of the HMAC secret. Full secret is not on GET /tenant.
features.callback_memoryRemind why I already called this number
features.named_greetingGreet first + last name from billing
features.cli_identityAuthorise by caller number
features.birthday_callsBirthday / care campaigns
features.crm_bridgePaid CRM / accounting attach (+€79/mo)

Agenten

GET /agents · POST /agents · GET /agents/{id} · PATCH /agents/{id} · DELETE /agents/{id}

Headers: Authorization: Bearer lv_live_… · Content-Type: application/json

# POST /agents  Body
{
  "name": "Reception",
  "language": "en",
  "voice": "Lion-AI Warm",
  "prompt": "You are a receptionist. Address the caller as you.",
  "first_message": "Hi, how can I help?",
  "max_duration_sec": 600,
  "first_speaker": "assistant",
  "webhook_url": "https://example.com/lionvoice/hooks"
}

# Response 200
{
  "ok": true,
  "agent": {
    "id": 12,
    "name": "Reception",
    "language": "en",
    "voice": "Lion-AI Warm",
    "engine": "Lion-AI",
    "max_duration_sec": 600,
    "webhook_url": "https://example.com/lionvoice/hooks"
  }
}

Wissen

GET /files · POST /files {"filename","content","agent_id?"} · GET/PATCH/DELETE /files/{id}. Max a few pages per file (500 KB).

Tools

GET /tools. Create in the dashboard. The engine POSTs to your url with the secret header you set. CRM bridge add-on €79/mo.

Nummern

POST /phone-numbers

{
  "number_e164": "+37052636333",
  "label": "Reception"
}

PATCH /phone-numbers/{id} {"agent_id","inbound_enabled":true}

SIP / VoIP

Not a public REST write (password stays in the vault). Dashboard → SIP / VoIP: host, port, username, password, transport. LionVoice registers like a softphone. Then map the DID under Phone numbers.

Calls

Pagination: GET /calls: limit und offset.

GET /calls?limit=25&offset=0&agent_id=

# Response
{ "ok": true, "calls": [
  {
    "call_uuid": "6c202718-…",
    "direction": "inbound",
    "from_number": "+447554522255",
    "to_number": "+37052636333",
    "status": "completed",
    "billable_seconds": 42,
    "cost_eur": 0.00
  }
]}

GET /calls/{uuid} — transcript + recording URL.

{
  "ok": true,
  "call": {
    "call_uuid": "6c202718-…",
    "transcript": "Lion-AI: Hi…\nCustomer: …",
    "summary": "Invoice balance question",
    "recording_url": "https://app.lionvoice.app/app/recording.php?uuid=…"
  }
}

POST /calls — outbound. Body field is to_number (E.164), plus agent_id and optional schedule_at (UTC).

curl -sS https://api.lionvoice.app/api/v1/calls \
  -H "Authorization: Bearer lv_live_…" \
  -H "Content-Type: application/json" \
  -d '{"to_number":"+447554522255","agent_id":1}'

POST /calls/{uuid}/transfer {"to"} E.164 or SIP URI.

Schedules

GET /schedules · DELETE /schedules/{id}. schedule_at is UTC.

Analytics / usage / billing

GET /analytics?days=14 · GET /usage?year=&month= · GET /plans · POST /topup {"amount_eur"} · POST /checkout.

API keys

POST /keys {"label"} — full secret only on create. DELETE /keys?id=.

POST /webhook-secret/regenerate

Headers: Authorization: Bearer lv_live_…. Rotates the HMAC key used for X-LV-Signature. The full secret is returned once (same pattern as POST /keys). GET /tenant never returns it. After rotate, the previous secret stops verifying immediately — update your receiver first.

curl -sS -X POST https://api.lionvoice.app/api/v1/webhook-secret/regenerate \
  -H "Authorization: Bearer $LV_KEY"

# Response 201
{
  "ok": true,
  "webhook_secret": "hex64…",
  "prefix": "269d7d4b",
  "note": "Save this secret now — it is not returned on GET /tenant."
}

Webhooks

Setze webhook_url am Agenten (nur https). Nach jedem beendeten Anruf POSTet LionVoice JSON einmal (8 s). Prüfe X-LV-Signature mit dem Secret aus POST /webhook-secret/regenerate (einmal sichtbar; GET /tenant nur Prefix). 2xx = OK. Event: call.completed.

Beginner — Create a webhook

Dashboard → Webhooks: URL + events → Create. Secret shown once. Test webhook. Snippet to verify HMAC.

Dashboard: app.lionvoice.app/app/?page=webhooks — paste https URL, tick events, Create, copy the secret once, then Test webhook.

Intermediate — Events, HMAC, retries

Events: call.started, call.answered, call.ended, call.failed, transcript.completed, recording.ready, tool.executed.

HTTPS only. HMAC header X-LV-Signature: t=<unix>,v1=<hex> over timestamp + "." + raw body. Reject timestamps older than five minutes. Return 2xx. Deduplicate on id (event_id). A retry is a new delivery_id. Delivery logs store status, HTTP, time, event_id, delivery_id — not the payload.

# Headers
X-LV-Event: call.ended
X-LV-Delivery-Id: del_01J…
X-LV-Timestamp: 1757232000
X-LV-Signature: t=1757232000,v1=hexhmac

# Body (no transcript / phone in test events)
{
  "id": "evt_01J…",
  "type": "call.ended",
  "created_at": "2026-09-07T09:00:00+00:00",
  "data": {
    "call_id": "6c202718-…",
    "duration_sec": 84,
    "status": "completed"
  }
}

Developer — Webhook API

POST   /v1/webhooks
GET    /v1/webhooks
DELETE /v1/webhooks/{id}
POST   /v1/webhooks/{id}/test
POST   /v1/webhooks/{id}/rotate-secret
GET    /v1/webhooks/{id}/deliveries
GET    /v1/webhooks/snippet?lang=php

Same engine as the dashboard. Agent webhook_url remains a legacy subscriber for call.ended.

Fehler

Form: {"ok":false,"error":"code","message":"…"}. Wiederholbar: 429 und 5xx. Nicht: 400, 401, 403, 404. 409 meist nicht.

{
  "ok": false,
  "error": "insufficient_balance",
  "message": "Your wallet balance is too low."
}
HTTPRetry?Typical error
400Novalid_to_number_required
401Nounauthorized
403Noplan / cap
404Nonot_found
409Usually noconflict
429Yes, exponential backoffrate_limited
500Yesengine

SDKs

Offizielle Clients sind geplant. Heute: kopiere die curl-, PHP-, JavaScript- und Python-Beispiele unten. Importiere die OpenAPI-Datei (/openapi.json) in Postman.

# JavaScript
const r = await fetch('https://api.lionvoice.app/api/v1/tenant', {
  headers: { Authorization: 'Bearer lv_live_…' }
});

# Python
import requests
requests.get('https://api.lionvoice.app/api/v1/ping').json()

# PHP
$ch = curl_init('https://api.lionvoice.app/api/v1/tenant');
curl_setopt_array($ch, [
  CURLOPT_HTTPHEADER => ['Authorization: Bearer lv_live_…'],
  CURLOPT_RETURNTRANSFER => true,
]);