Sign inGet Started

Usar datos de la aplicación en una secuencia de voz

Usa voice.webhook cuando tu aplicación tenga datos que la llamada necesita: el estado de un pedido, un equipo o la confirmación de una cita. El paso devuelve esos datos en success, y un paso Branch posterior elige la ruta a partir de ellos.

Definir la respuesta

Asigna a config.output_schema un esquema de objeto para los datos devueltos. El paso tiene dos salidas: success lleva datos con ese esquema y failure gestiona los problemas técnicos. Si omites la configuración, la salida es un objeto vacío.

La entrada contiene un url HTTPS estático y un objeto data opcional. Los datos admiten un objeto literal, $literal o un $expr de objeto completo. No se admiten URL dinámicas, cabeceras personalizadas ni credenciales.

Verificar las solicitudes

Bird firma cada solicitud, incluidas las continuaciones, con el secreto de callback de tu espacio de trabajo mediante las cabeceras de Standard Webhooks: webhook-id, webhook-timestamp y webhook-signature. La firma cubre el ID, la marca de tiempo y el cuerpo exacto. Durante las 24 horas posteriores a la rotación de un secreto, webhook-signature lleva una entrada v1, por cada secreto activo, así que cualquiera de los dos secretos sirve para verificar. Las llamadas pueden tardar alrededor de un minuto en empezar a firmar con el secreto nuevo, así que sigue aceptando el anterior hasta que termine el periodo de solapamiento. Tu respuesta no se firma: Bird valida el certificado TLS de tu receptor, y la conexión HTTPS autentica lo que devuelve.

Verifica las cabeceras sobre el cuerpo sin procesar con la llamada verify de una biblioteca de Standard Webhooks antes de analizarlo. El webhooks.unwrap de los SDK analiza eventos de la plataforma y no sirve para estas solicitudes. Primero rota el secreto en Webhooks → Direct callbacks, porque el inicial nunca se muestra. La firma demuestra que alguien que tiene el secreto de tu espacio de trabajo firmó la solicitud. Tus receptores de callback SMS tienen el mismo secreto y también pueden firmar, así que guárdalo solo en receptores de confianza. Cualquiera que pueda editar las secuencias del espacio de trabajo elige la URL y data. Comprueba que target_url es tu propia URL para rechazar una solicitud que Bird firmó para otro receptor, y autoriza las acciones de negocio en tu receptor. La autorización del cliente nunca se reenvía.

Las solicitudes de webhook de las secuencias no presentan un certificado de cliente, aunque tu espacio de trabajo exija mTLS. Configura el receptor para que acepte conexiones sin certificado de cliente y autentica la solicitud firmada en su lugar.

Compara el webhook-timestamp firmado, una marca de tiempo Unix en segundos, con el reloj sincronizado de un servidor. Rechaza las solicitudes con más de cinco minutos de diferencia en el pasado o en el futuro. Elimina duplicados por webhook-id antes de ejecutar efectos secundarios. Siempre que puedas, guarda el ID y el resultado de forma atómica junto con la acción, y devuelve el resultado guardado si la solicitud se repite. Cada continuación tiene su propio ID. Usa una clave de idempotencia de negocio aparte si solicitudes distintas no deben repetir la misma acción.

Devolver datos

El receptor devuelve esta estructura de respuesta cerrada:

Ejemplo de código
{
  "schema_version": 1,
  "type": "outcome",
  "output": { "status": "confirmed", "message": "Your appointment is confirmed." }
}

output debe coincidir con el esquema configurado. La respuesta devuelve datos, no un puerto, un ID de nodo ni comandos nuevos. Una continuación es {"schema_version":1,"type":"continue","url":"https://example.com/voice/next"} en el origen canónico de la URL original.

Para enrutar según los datos, conecta success a un paso logic.branch con casos como steps.decision.output.status == 'confirmed'. Declara el campo como una cadena enum en el esquema para que el receptor solo pueda devolver esos valores.

La solicitud contiene la identidad de la ejecución, la sesión y la llamada, la identidad de la secuencia admitida y del nodo, tu data materializado y results. Mantén los datos de negocio en data. Un resultado privado directo de Gather puede aparecer una vez en results, y las grabaciones finalizadas incluyen un ID cuando está disponible. Estas observaciones no se convierten automáticamente en campos de expresión.

Leer la solicitud del receptor

Una ejecución de prueba de un borrador envía esta estructura. Los ID y las marcas de tiempo siguientes son ficticios; la solicitud real usa la ejecución admitida y la ocurrencia actual del nodo:

Ejemplo de código
{
  "schema_version": 1,
  "method": "POST",
  "target_url": "https://example.com/decision",
  "run_id": "vsr_01k5yb7p000000000000000000",
  "sequence": {
    "id": "vsq_01k5yb7p000000000000000000",
    "version_id": null,
    "draft_revision": 1,
    "entry_node_id": "start",
    "mode": "test"
  },
  "session_id": "vcs_01k5yb7p000000000000000000",
  "call": { "id": "vcl_01k5yb7p000000000000000000", "orig": null, "dest": null },
  "node": { "id": "decision", "occurrence_id": "00000000-0000-4000-8000-000000000001" },
  "data": { "appointment_id": "appointment_123" },
  "results": []
}

En una ejecución publicada, version_id identifica la versión seleccionada y draft_revision es null. En una llamada que ejecutó una definición en línea, id, version_id y draft_revision son todos null. mode puede ser test o production. El origen y el destino usan objetos de participante de llamada que admiten null. Verifica la firma antes de confiar en estos campos de identidad.

Una observación de Gather en results tiene type: "gather", node_id, el UUID command_id, digits y reason. Una observación de grabación tiene type: "recording", el UUID command_id, state y un recording_id cuando está disponible. Usa los dígitos privados solo para la solicitud prevista; no se convierten en salidas de expresión conservadas. Las solicitudes de continuación mantienen la misma ocurrencia del nodo y eliminan de results las observaciones ya entregadas.

Gestionar los fallos

Un fallo técnico usa el puerto failure con {"code":"webhook_unavailable"} o {"code":"webhook_invalid_response"}. Si el puerto de fallo está conectado, la ejecución sigue la ruta de recuperación; si no lo está, la ejecución falla. Un success sin conectar finaliza la ejecución con normalidad. Las solicitudes de webhook en vivo tienen un plazo de tres segundos y no se reintentan automáticamente. La vista previa no envía ninguna solicitud.

El plazo de tres segundos incluye la lectura del secreto de firma. Conecta failure a una ruta de recuperación para que la llamada pueda continuar cuando el receptor no esté disponible o devuelva una respuesta no válida. Una solicitud que agotó el tiempo de espera puede haber provocado ya un efecto secundario en el receptor.

Trata data, la identidad de la llamada y los dígitos de Gather como datos privados. Restringe el acceso al receptor y al secreto, evita registrar cuerpos o secretos y conserva la entrada de la persona que llama solo el tiempo que tu aplicación la necesite. No pongas datos privados ni credenciales en las URL: el registro de la llamada conserva la URL completa, incluida su consulta. La URL y los datos configurados y la salida validada se conservan en el historial de ejecuciones; los dígitos transitorios de Gather se excluyen de la entrada capturada del paso. Devuelve solo los datos que la secuencia necesita.

Lee los datos devueltos en steps.decision.output y los errores técnicos en steps.decision.error.code. Si ambas rutas se unen, comprueba el contenedor con has(steps.decision.output) o has(steps.decision.error) antes de usarlo. Un HTTP 200 de tu receptor no garantiza un resultado válido hasta que su cuerpo cumpla el contrato.

Continúa con la documentación, guías y ejemplos de este tema.