> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Construir y publicar su primer flujo IVR

> Autoré un flujo IVR en la DSL de Orbit o en el builder visual, valídelo antes de publicar, simule un llamante guionizado, adjúntelo a un DID, entregue a un humano cuando haga falta y revierta con el historial de versiones.

## Nota de idioma

Esta versión en español es una traducción abreviada: las secciones «3. Validate before you publish», «7. The visual builder — the same graph, without the JSON» y «8. Version and roll back» existen solo en inglés: [/en/guides/build-ivr-flow](https://docs.orbit.devotel.io/en/guides/build-ivr-flow). Los bloques de código, rutas de endpoints, nombres de campos de API y códigos de error permanecen en inglés.

# Construir y publicar su primer flujo IVR

Un flujo IVR es un grafo `{ nodes, edges }` que se envía a un único conjunto de endpoints. En la creación y en cada edición, el validador de grafos corre antes de persistir; publicar copia el borrador al snapshot en vivo; y un simulador le permite conducir un llamante guionizado por el grafo antes de que suene un DID.

Construiremos un pequeño flujo de estilo aerolínea: saludar al llamante, enrutar los intents de reserva o reembolso a una cola ACD y caer al buzón de voz si nada coincide.

## 1. Primitivas IVR

Las definiciones de flujo las ejecuta el runtime de voice contra el pipeline de verbos de Jambonz. El validador de grafos — que corre en cada create y update — verifica exactamente un nodo de entrada (tipo `ivrStart`), al menos un nodo terminal (hangup, transfer, voicemail, ringGroup, dialByName, offerCallback, offerSmsCallback, scheduleCallback), que cada edge referencie ids de nodos existentes, que todo nodo sea alcanzable desde la entrada y que ningún nodo alcanzable quede atrapado en un ciclo sin ruta a un terminal. Los bloques reflejan la DSL de voice programmable — vea la [Programmable Voice DSL reference](https://docs.orbit.devotel.io/reference/programmable-voice-dsl.md).

Los intents de voz complementan los menús DTMF: los tipos de nodo `speechInput` y `dialByName` comparan un enunciado libre contra `speechIntents` por nodo. La [página de concepto IVR intents](https://docs.orbit.devotel.io/voice/ivr-intents.md) explica esos buckets.

> [!WARNING]
>
> La terminación saliente es exclusivamente de Devotel. Un nodo `transfer` con destino a una SIP URI cruda es rechazado por el validador con `transfer_external_sip` — use un número E.164 o una extensión interna.

## 2. Construir el flujo

Envíe la definición a `POST /api/v1/voice/ivr-flows`. El cuerpo acepta `name`, el grafo `definition` y un flag `active` opcional. El validador corre antes de persistir, así que un grafo malformado devuelve `422 IVR_FLOW_GRAPH_INVALID` y nada se almacena.

```bash cURL theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/ivr-flows" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Airline inbound router",
    "definition": {
      "nodes": [
        { "id": "start", "type": "ivrStart" },
        { "id": "welcome", "type": "playAudio", "data": { "ttsText": "Welcome to Horizon Airlines." } },
        {
          "id": "menu",
          "type": "menu",
          "data": {
            "menuOptions": [
              { "key": "1", "label": "Bookings" },
              { "key": "2", "label": "Support agent" }
            ]
          }
        },
        { "id": "bookings", "type": "queue", "data": { "label": "Bookings queue" } },
        { "id": "support", "type": "queue", "data": { "label": "Support queue" } },
        { "id": "fallback", "type": "voicemail", "data": { "label": "Voicemail fallback" } },
        { "id": "end", "type": "hangup" }
      ],
      "edges": [
        { "source": "start", "target": "welcome" },
        { "source": "welcome", "target": "menu" },
        { "source": "menu", "target": "bookings", "sourceHandle": "1" },
        { "source": "menu", "target": "support", "sourceHandle": "2" },
        { "source": "menu", "target": "fallback", "sourceHandle": "default" },
        { "source": "fallback", "target": "end" }
      ]
    }
  }'
```

La respuesta lleva el `id` del flujo nuevo (un identificador `ivr_*`) — guárdelo; cada paso posterior lo usa.

Los nodos menu ramifican una edge por tecla DTMF presente en `menuOptions`; el validador rechaza claves vacías, duplicadas o no DTMF con `menu_empty_key`, `menu_duplicate_key` o `menu_invalid_key`.

## 4. Probar con el simulador

Conduzca un llamante guionizado por el grafo antes de adjuntarlo a un DID. `POST /api/v1/voice/ivr-flows/:id/simulate` consume `turns` (cada una con `digits`, `speech` y/o `intent`) y devuelve, por cada paso, el id del nodo visitado, el handle tomado, las categorías de verbos emitidas y el `termination_reason`.

```bash cURL theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/ivr-flows/ivr_01h.../simulate" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "turns": [{ "digits": "1" }],
    "max_steps": 50
  }'
```

**200 OK** — espere `reached_nodes: ["start","welcome","menu","bookings"]`, `emitted_verbs` con `gather` en el menú y `enqueue` en la cola, y `termination_reason: "queue_answered"`. Una simulación en rojo atrapa regresiones de enrutado antes de quemar un minuto en vivo.

Opciones útiles: `source: "published"` recorre el snapshot en vivo, `source: "draft"` (defecto) el borrador sin guardar.

## 5. Adjuntar el flujo a un DID

La configuración de enrutado entrante de cada DID lleva la referencia al flujo. Fije `type: "ivr"` con `config.flowId`:

```bash cURL theme={null}
curl -X PUT "https://api.orbit.devotel.io/api/v1/numbers/+14155550123/routing" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "type": "ivr", "config": { "flowId": "ivr_01h..." } }'
```

La puerta de enlace de voice resuelve el flujo en cada llamada entrante, así que la adjunción es inmediata. Publique antes de adjuntar y el primer llamante tocará el snapshot en vivo.

## 6. Entregar a un agente

Enrute la rama `support` a una cola ACD vía un nodo `queue` para que un miembro del equipo atienda desde el dashboard, o dirija el flujo entero a un [agente de IA](https://docs.orbit.devotel.io/voice/ai-agent-handback.md) cuyo endpoint de handback conserva la conversación y devuelve al AI una vez resuelta la excepción por un humano.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.