API PARA DESARROLLADORES

Integra análisis emocional de voz en tu producto

Envía audio, consulta el estado del análisis y descarga el informe normalizado desde tu propio backend. La misma inteligencia que usa el portal de Voice Feeling, expuesta como API REST.

01 / INICIO RÁPIDO

De un archivo de audio a un informe

Cuatro llamadas cubren el flujo completo: crear el análisis, consultar su estado, y descargar el informe normalizado cuando termine.

  1. Crear un análisis subiendo el archivo

    Envía el audio como multipart/form-data. `consentObtained=true` es obligatorio: confirma que cuentas con consentimiento o base legal para analizar la grabación.

    curl https://voice-feeling.pages.dev/api/v1/analyses \
      -H "Authorization: Bearer vf_live_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
      -H "Idempotency-Key: 5f3d2b7e-9c2e-4f6a-9c0e-1a2b3c4d5e6f" \
      -F "file=@call.wav" \
      -F "consentObtained=true" \
      -F "businessContext=sales"
  2. Crear un análisis desde una URL

    Si el audio ya está alojado en tu infraestructura, envía JSON con `audioUrl` en lugar de subir el archivo. El mismo `consentObtained: true` sigue siendo obligatorio.

    curl https://voice-feeling.pages.dev/api/v1/analyses \
      -H "Authorization: Bearer vf_live_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
      -H "Idempotency-Key: 5f3d2b7e-9c2e-4f6a-9c0e-1a2b3c4d5e6f" \
      -H "Content-Type: application/json" \
      -d '{
        "audioUrl": "https://cdn.example.com/calls/2026-09-03-0142.wav",
        "consentObtained": true,
        "businessContext": "service"
      }'
  3. Consultar el estado

    El análisis pasa por varios estados hasta `completed` o `failed`. Haz polling con backoff o regístrate a un webhook para que te avisemos.

    curl https://voice-feeling.pages.dev/api/v1/analyses/an_9f3c2e1a \
      -H "Authorization: Bearer vf_live_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    
    # {
    #   "id": "an_9f3c2e1a",
    #   "status": "completed",
    #   "businessContext": "service",
    #   "createdAt": "2026-09-03T14:02:11Z",
    #   "completedAt": "2026-09-03T14:03:47Z",
    #   "reportUrl": "/api/v1/analyses/an_9f3c2e1a/report"
    # }
  4. Descargar el informe normalizado

    Disponible solo cuando el análisis está en `completed`. El informe sigue el contrato `voicefeeling.report.v1`; nunca contiene la respuesta cruda del proveedor.

    curl https://voice-feeling.pages.dev/api/v1/analyses/an_9f3c2e1a/report \
      -H "Authorization: Bearer vf_live_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Envía una cabecera `Idempotency-Key` en `POST /analyses` para que reintentos de red no dupliquen el análisis.

02 / AUTENTICACIÓN

Llaves de API y scopes

Autentica cada solicitud con una llave de API en la cabecera Authorization. Genera y administra tus llaves desde el portal, en la sección Desarrolladores.

Cabecera requerida
Authorization: Bearer vf_live_<prefix>_<secret>
Formato de la llave
vf_live_<prefijo>_<secreto>. El prefijo identifica la llave en los registros; el secreto solo se muestra una vez al crearla.

Scopes disponibles

Cada llave declara los scopes que necesita. Una solicitud sin el scope requerido recibe `api_scope_missing` con el scope faltante en la respuesta.

03 / IDEMPOTENCIA

Reintentos seguros

Toda entrega puede repetirse: si tu solicitud de red falla sin respuesta clara, reenvíala con la misma cabecera `Idempotency-Key`. Si la llave ya se usó con un cuerpo distinto, la API responde `idempotency_conflict` en lugar de crear un segundo análisis.

04 / WEBHOOKS

Notificaciones de eventos

En vez de hacer polling, registra un endpoint HTTPS para recibir eventos cuando un análisis termine.

Eventos disponibles

Forma del payload

Cada entrega es un POST en JSON con cabeceras de identificación y firma.

POST https://your-domain.com/webhooks/voicefeeling
Content-Type: application/json
Voice-Feeling-Event: analysis.completed
Voice-Feeling-Signature: t=1756900931,v1=4b1f9e...c2a0

{
  "event": "analysis.completed",
  "analysisId": "an_9f3c2e1a",
  "createdAt": "2026-09-03T14:03:47Z"
}

Verifica la firma

Calcula el HMAC-SHA256 del timestamp y el cuerpo con tu secreto, y compáralo con la firma recibida antes de confiar en el payload. Rechaza entregas con más de 5 minutos de diferencia entre el timestamp y tu reloj.

const crypto = require("node:crypto");

function isValidSignature(header, rawBody, secret, toleranceSeconds = 300) {
  const parts = Object.fromEntries(
    header.split(",").map((part) => part.split("="))
  );
  const timestamp = Number(parts.t);
  if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) {
    return false;
  }
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 || ""));
}

Reintentos y desactivación

Las entregas fallidas se reintentan con backoff exponencial durante aproximadamente una hora. Un endpoint con 20 fallos consecutivos se deshabilita automáticamente; vuelve a activarlo eliminándolo y registrándolo de nuevo desde el portal.

05 / ERRORES

Códigos de error

Toda respuesta de error usa el mismo sobre: `{ "error": { "code", "message", "docs_url" } }`. Diseña tu integración contra `code`, no contra `message`, que puede cambiar de idioma según `Accept-Language`.

CÓDIGOHTTPDESCRIPCIÓN
api_key_invalid401La llave no existe o no coincide con ninguna registrada.
api_key_expired401La llave tenía fecha de expiración y ya se cumplió.
api_key_revoked401La llave fue revocada desde el portal.
api_scope_missing403La llave no tiene el scope requerido; la respuesta incluye `required_scope`.
api_rate_limited429Se superó el límite de solicitudes por llave; respeta la cabecera `Retry-After`.
api_disabled503La API pública está deshabilitada en este entorno.
consent_required422Falta `consentObtained: true` en la solicitud de creación del análisis.
audio_url_invalid422`audioUrl` no es una URL https válida o pública.
audio_download_failed422No se pudo descargar el audio desde `audioUrl`.
unsupported_audio_content415El contenido del archivo no corresponde a un formato de audio soportado.
audio_too_large413El archivo supera el límite de tamaño permitido.
monthly_analysis_quota_exceeded429Se alcanzó la cuota mensual de análisis del plan.
daily_upload_quota_exceeded429Se alcanzó la cuota diaria de cargas del plan.
storage_quota_exceeded413El almacenamiento de la organización alcanzó su límite.
idempotency_conflict409La `Idempotency-Key` ya se usó con un cuerpo de solicitud distinto.
analysis_not_found404No existe un análisis con ese id para esta organización.
report_requires_final409El análisis todavía no terminó; el informe no está disponible.
webhook_url_invalid422La URL del webhook no es una URL https pública válida.
webhook_endpoint_not_found404No existe un endpoint de webhook con ese id para esta organización.
webhook_limit_reached409Se alcanzó el número máximo de endpoints de webhook por organización.
06 / LÍMITES

Límites técnicos

Diseña tu integración con estos límites en mente; superarlos produce el código de error correspondiente, no un corte silencioso.

Frecuencia de solicitudes
120 solicitudes / 10 minutos por llave
Tamaño máximo de audio
50 MiB por archivo
Formatos soportados
WAV, MP3, M4A, OGG, FLAC, WebM, AAC, AIFF
Uso responsable de las señales acústicas

Voice Feeling mide señales acústicas (energía, tensión, ritmo) correlacionadas con estados emocionales. No son hechos psicológicos, no determinan intención y en ningún caso constituyen un veredicto de verdad o mentira. Toda decisión que afecte a una persona debe combinarse con contexto humano y las políticas de tu organización.