LionVoice API v1

URL base: https://api.lionvoice.app/api/v1. Voz, Lion-AI y telefonía — un producto. El cerebro se llama Lion-AI en todo el producto; los ids del motor son internos y opcionales en las respuestas.

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

La API no tiene parámetro de selección de modelo — Lion-AI es el único cerebro expuesto.

Quick Start de 5 minutos para desarrolladores

1. Crea una clave API en el panel (API keys) — el secreto se muestra una vez. 2. Crea un agente. 3. Conecta un número (E.164) y asígnalo. 4. Haz una llamada de prueba saliente.

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}'

Autenticación

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_…"

Límite: 120 peticiones / minuto / clave. 429 reintentable.

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)

Agentes

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"
  }
}

Conocimiento

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.

Teléfonos

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

Paginación: GET /calls: limit y 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

Pon webhook_url en el agente (solo https). Tras cada llamada terminada LionVoice hace un POST JSON (8 s). Verifica X-LV-Signature con el secreto de POST /webhook-secret/regenerate (se muestra una vez; GET /tenant solo el prefix). Evento: 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.

Errores

Forma: {"ok":false,"error":"código","message":"…"}. Reintentables: 429 y 5xx. No: 400, 401, 403, 404. 409 casi nunca.

{
  "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

Los clientes oficiales están en el roadmap. Hoy: copia los ejemplos curl, PHP, JavaScript y Python abajo. Importa el OpenAPI (/openapi.json) en 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,
]);