Documentación técnica
Todo lo que necesitás para integrar VerityFone en tu producto. Guías, referencia completa de endpoints y ejemplos de código en 6 lenguajes.
Quickstart · Primera llamada en 5 minutos
Este ejemplo dispara una verificación 2FA por voz al número de destino. El destinatario escucha un audio, marca 6 dígitos en su teclado, y VerityFone envía un webhook con el resultado.
1. Instalá el SDK
# npm npm install verityfone # yarn yarn add verityfone # pnpm pnpm add verityfone
2. Autenticá con tu API key
import VerityFone from 'verityfone'; const client = new VerityFone({ apiKey: process.env.VERITYFONE_API_KEY, timeout: 30000, maxRetries: 3, });
3. Realizá tu primera llamada
const call = await client.calls.create({ to: '+51987654321', from: '+13025550134', webhookUrl: 'https://tu-app.com/callback', gather: { digits: 6, timeoutSecs: 30, audioUrl: 'https://cdn.tu-app.com/otp-es.mp3', }, }); console.log(call.id); // call_2xR8wNfP... console.log(call.status); // 'queued'
POST /v2/calls responde en <100ms p50. La llamada se procesa asíncrona · recibís eventos vía webhook (call.answered, call.gather.ended, call.hangup, call.cost).Autenticación
VerityFone usa Bearer authentication con API keys. Todas las requests deben incluir tu API key en el header Authorization.
Authorization: Bearer VF_LIVE_a4b2c9d8f1e6...
Podés crear y rotar API keys desde el dashboard en /settings/api-keys. Recomendamos:
- Una API key por ambiente (
VF_LIVE_*producción ·VF_TEST_*testing) - Rotación cada 90 días
- IP allowlist para keys de producción (opcional)
- Scopes granulares para keys de CI/CD
SDKs oficiales
Mantenemos SDKs en 6 lenguajes principales. Todos incluyen retry automático con exponential backoff, verificación de firma Ed25519 nativa, y tipos TypeScript/mypy/PHPDoc completos.
| Lenguaje | Package | Repo | Versión |
|---|---|---|---|
| Node.js · TypeScript | verityfone | github.com/verityfone/node | 3.2.1 |
| Python 3.9+ | verityfone | github.com/verityfone/python | 2.4.0 |
| PHP 8.1+ | verityfone/verityfone-php | github.com/verityfone/php | 2.1.3 |
| Go 1.20+ | github.com/verityfone/go | github.com/verityfone/go | 1.8.2 |
| Ruby 3.0+ | verityfone | github.com/verityfone/ruby | 1.5.0 |
| .NET 6+ | VerityFone.SDK | github.com/verityfone/dotnet | 1.3.4 |
Crear una llamada
POST/v2/calls
Inicia una nueva llamada saliente. La llamada se procesa asíncrona · el response contiene el call.id que podés usar para consultar estado o ejecutar acciones.
Parámetros del body
| Campo | Tipo | Descripción |
|---|---|---|
to * | string | Número destino en formato E.164 · +51987654321 |
from * | string | Uno de tus números VerityFone · +13025550134 |
webhook_url * | string | URL HTTPS que recibirá eventos de esta llamada |
gather | object | Configuración de recolección DTMF · ver abajo |
timeout_secs | integer | Segundos máximos de ring · default 30 · máx 60 |
machine_detection | string | none · detect · hangup_on_machine |
record | boolean | Grabar la llamada · default false (compliance) |
client_state | string | Base64 · datos propios que se devuelven en webhooks · máx 512 chars |
caller_display_name | string | Nombre CNAM que aparece en el teléfono destino (US only) |
Campos con * son requeridos.
Ejemplo request completo
curl -X POST https://api.verityfone.com/v2/calls \ -H "Authorization: Bearer VF_LIVE_a4b2c9d8f1e6..." \ -H "Content-Type: application/json" \ -d '{ "to": "+51987654321", "from": "+13025550134", "webhook_url": "https://tu-app.com/callback", "gather": { "digits": 6, "timeout_secs": 30, "audio_url": "https://cdn.tu-app.com/otp-es.mp3", "invalid_audio_url": "https://cdn.tu-app.com/invalid-es.mp3", "retry": 2 }, "machine_detection": "hangup_on_machine", "client_state": "eyJvcmRlcl9pZCI6IjEyMzQ1In0=" }'
Response · 201 Created
{
"data": {
"id": "call_2xR8wNfPqLk9mYzT4Bv3wCsD",
"object": "call",
"status": "queued",
"to": "+51987654321",
"from": "+13025550134",
"created_at": "2026-08-18T14:23:45.123Z",
"webhook_url": "https://tu-app.com/callback",
"estimated_cost": "0.0245",
"currency": "USD"
},
"request_id": "req_5nHqM3xW9pKzT2LvBcRs"
}
Verificación de firma Ed25519
Todos los webhooks de VerityFone se firman con Ed25519. Debés verificar la firma antes de procesar el evento para evitar spoofing.
La firma llega en el header VerityFone-Signature junto con VerityFone-Timestamp. El payload firmado es {timestamp}.{raw_body}.
import nacl from 'tweetnacl'; function verifySignature(rawBody, signature, timestamp, publicKey) { // 1. Verificar timestamp (previene replay attacks) const age = Math.abs(Date.now() / 1000 - parseInt(timestamp)); if (age > 300) throw new Error('Timestamp too old'); // 2. Verificar firma const signed = Buffer.from(`${timestamp}.${rawBody}`); const sig = Buffer.from(signature, 'base64'); const key = Buffer.from(publicKey, 'base64'); return nacl.sign.detached.verify(signed, sig, key); }
Eventos de webhook
VerityFone emite estos eventos durante el ciclo de vida de una llamada:
| Evento | Descripción |
|---|---|
call.initiated | La llamada fue aceptada por VerityFone y encolada |
call.ringing | El teléfono destino está sonando |
call.answered | El destinatario contestó · empezá gather aquí |
call.gather.ended | El destinatario ingresó DTMF o expiró timeout |
call.playback.started | Empezó a reproducirse un audio |
call.playback.ended | Terminó de reproducirse un audio |
call.machine.detection | Resultado de detección de contestador (si activado) |
call.hangup | La llamada terminó (por vos, por destino, por timeout) |
call.cost | Llega ~1-3s post-hangup con el costo real |
Códigos de error
La API usa códigos HTTP estándar. El body de errores 4xx/5xx tiene esta estructura:
{
"error": {
"code": "invalid_number",
"message": "El número destino no está en formato E.164",
"param": "to",
"docs_url": "https://verityfone.com/documentacion/errors#invalid_number"
},
"request_id": "req_5nHqM3xW9pKzT2LvBcRs"
}
| Código HTTP | Significado |
|---|---|
200 · 201 | Éxito |
400 | Request malformada · revisá param en el error |
401 | API key inválida o faltante |
402 | Saldo insuficiente en tu cuenta |
403 | Sin permisos para ese recurso · revisá scopes de la API key |
404 | Recurso no existe · verificá el ID |
409 | Conflicto · recurso ya existe o estado incompatible |
422 | Validación fallida · destino en blocklist / prefix bloqueado |
429 | Rate limit excedido · esperá Retry-After segundos |
500 | Error interno · reintentá con exponential backoff |
503 | Mantenimiento programado · ver estado |
Rate limits
Los límites son por API key y se resetean cada 60 segundos. Todos los responses incluyen headers X-RateLimit-*.
| Plan | Requests/min | Llamadas concurrentes |
|---|---|---|
| Inicial | 60 | 20 |
| Empresa | 600 | 200 |
| Corporativo | 6,000 | 2,000 |
| Custom | Personalizado | Personalizado |
HTTP 429 con header Retry-After. Los SDKs oficiales respetan este header y reintentan automáticamente con exponential backoff.Changelog
Últimos cambios de la API:
- 2026-08-15 · Nuevo campo
machine_detection.model· algoritmo v3 con 12% mejor precisión - 2026-08-01 · Endpoint
/v2/numbers/availableahora filtra por área code - 2026-07-20 · Firma Ed25519 rotada · nueva public key en portal
- 2026-07-10 · Soporte para números toll-free en 15 nuevos países
- 2026-06-25 ·
gather.retryahora acepta hasta 5 (antes 3)