> ## 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.

# İlk IVR akışınızı oluşturun ve yayınlayın

> Orbit DSL'inde veya görsel oluşturucuda bir IVR akışı tasarlayın, yayınlamadan önce doğrulayın, betikli bir arayanı simüle edin, bir DID'e bağlayın, gerektiğinde insana devredin ve sürüm geçmişiyle geri alın.

IVR akışı, tek bir uç nokta kümesine gönderdiğiniz bir `{ nodes, edges }` grafiğidir. Oluşturma ve her düzenleme, kaydetmeden önce grafik doğrulayıcısını çalıştırır; yayınlama taslağı canlı anlık görüntüye kopyalar; simülatör ise bir DID hiç çalmadan önce betikli bir arayanı grafik üzerinde yürütmenizi sağlar. Bu rehber tam döngüyü baştan sona anlatır.

Küçük bir havayolu tarzı akış kuracağız: arayanı karşıla, rezervasyon veya iade niyetlerini bir ACD kuyruğuna yönlendir, hiçbiri eşleşmezse sesli mesaja düş. Adım adım izleyin, sonra grafiği kendi dallarınıza uyarlayın.

## 1. IVR temel kavramları

Akış tanımları, ses çalışma zamanı tarafından Jambonz verb hattına karşı yürütülür. Her oluşturma ve güncellemede çalışan grafik doğrulayıcısı şunları denetler: tam olarak bir giriş düğümü (tür `ivrStart`), en az bir terminal düğüm (hangup, transfer, voicemail, ringGroup, dialByName, offerCallback, offerSmsCallback, scheduleCallback), her kenarın var olan düğüm kimliklerine başvurması, her düğümün girişten erişilebilir olması ve erişilebilir hiçbir düğümün terminale yolu olmayan bir döngüye hapsolmaması. Yapı taşları programlanabilir ses DSL'ini yansıtır — temeldeki `say` / `gather` / `dial` / `enqueue` / `transfer` / `hangup` verb'leri için [Programmable Voice DSL referansına](https://docs.orbit.devotel.io/reference/programmable-voice-dsl.md) bakın.

Konuşma niyetleri DTMF menülerini tamamlar: `speechInput` ve `dialByName` düğüm türleri, serbest biçimli bir ifadeyi düğüm başına `speechIntents` veya tenant başına niyet kovalarıyla eşleştirir. [IVR intents kavram sayfası](https://docs.orbit.devotel.io/voice/ivr-intents.md) bu kovaları açıklar — düğüm düzeyinde bir konuşma menüsünün nasıl tamamen DTMF'siz kalabildiği dahil; bu rehber, her ikisinin çalıştırılabilir bir akışa nasıl girdiğini gösterir.

> [!WARNING]
>
> Giden sonlandırma yalnızca Devotel üzerindendir. Hedefi ham bir SIP URI olan `transfer` düğümü, doğrulayıcı tarafından `transfer_external_sip` ile reddedilir — bir E.164 telefon numarası veya şebeke içi bir dahili kullanın.

## 2. Akışı oluşturun

Tanımı `POST /api/v1/voice/ivr-flows`'a gönderin. Gövde `name`, `definition` grafiği ve isteğe bağlı bir `active` bayrağı kabul eder. Doğrulayıcı kaydetmeden önce çalışır, bu yüzden biçimi bozuk bir grafik `422 IVR_FLOW_GRAPH_INVALID` döndürür ve hiçbir şey kaydedilmez.

SDK'lerden aynı oluşturma çağrısı — gövde özdeş grafik alanlarına eşlenir:

```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" }
      ]
    }
  }'
```

```typescript Node.js theme={null}
import { Orbit } from '@devotel-orbit/node';

const orbit = new Orbit({ apiKey: process.env.ORBIT_API_KEY });

const created = await orbit.request(
  'POST',
  '/voice/ivr-flows',
  {
    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' }
      ]
    }
  },
);
console.log(created.data.id); // ivr_...
```

```python Python theme={null}
created = client.request(
    "POST",
    "/voice/ivr-flows",
    json_body={
        "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" }
            ]
        }
    },
)
print(created["data"]["id"])  # ivr_...
```

Yanıt, yeni akışın `id`'sini taşır (bir `ivr_*` tanımlayıcısı) — saklayın; sonraki her adım bunu kullanır.

Menü düğümleri, `menuOptions` içindeki DTMF tuşu başına bir kenar dallandırır; doğrulayıcı boş, yinelenen veya DTMF olmayan tuşları `menu_empty_key`, `menu_duplicate_key` veya `menu_invalid_key` ile reddeder. Slot doldurma düğümleri serbest konuşmadan yapılandırılmış değerler toplar — her varlık için istemleriyle bir `speechInput` düğümü tanımlayın, sonra toplanan her değeri `POST /api/v1/voice/ivr-slots/extract`'e verin; bu durum tutmayan uç nokta `utterance`, bir `slots` şartnamesi (ad; `account_number` / `order_id` / `date` / `amount` gibi tür; istem; `required`; `enumValues`) ve o ana dek toplanan değerleri alır. Güncellenmiş `filled`'ı, hâlâ eksik olan her şey için `next_prompt`'u ve bir `complete` bayrağı döndürür — `filled` haritasını kendi oturum durumunuzda turlar boyunca taşıyın. Yalnız konuşma yolu için çalışma zamanının niyet sınıflandırmasına ([IVR intents sayfası](https://docs.orbit.devotel.io/voice/ivr-intents.md)) bakın.

## 3. Yayınlamadan önce doğrulayın

Kaydetme anındaki grafik doğrulaması `create`'de zaten çalıştı. Her düzenlemede yeniden çalıştırın — `PUT /api/v1/voice/ivr-flows/:id`, verilen `definition`'ı kalıcılaştırmadan önce aynı şekilde doğrular; böylece bozuk bir geçiş asla üretime ulaşmaz. Yük başarısız olursa API, kod anahtarlı `errors` girdileriyle `422 IVR_FLOW_GRAPH_INVALID` döndürür: şekil sorunları (`no_entry`, `multiple_entries`, `no_exit`), ölü bağlantılar (`orphan_edge_source`, `orphan_edge_target`, `unreachable_node`) ve arayanın, platform çağrıyı sonlandırana dek hatta bekleyeceği döngü tuzakları (`cycle_without_exit`, `dead_end_node`). Örneğin bu akışta bir `menu_invalid_key`, tuş olarak `book` yazdığınız ve siz düzeltene dek her arayanın yedek kenara düştüğü anlamına gelir. Bir CI adımı programlayın: aday tanımınızla bir deneme akışına PUT gönderin ve `422`'yi derlemeye izin vermek yerine yayın öncesi bir başarısızlık olarak ele alın.

## 4. Simülatörle test edin

Bir DID'e bağlamadan önce betikli bir arayanı grafik üzerinde yürütün. `POST /api/v1/voice/ivr-flows/:id/simulate`, `turns`'ü tüketir (her biri `digits`, `speech` ve/veya `intent` içerir) ve her adım için ziyaret edilen düğüm kimliğini, seçilen eşleşen handle'ı, üretilen verb kategorilerini ve `termination_reason`'ı döndürür. Yürüyüş, çalışma zamanının kenar önceliğini yeniden oynatır — handle → `default` → tek varsayılan kenar — ve etkileşimli düğümlerde duraklar (menu, dtmfInput, speechInput, dialByName, offerCallback, offerSmsCallback, scheduleCallback).

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

```typescript Node.js theme={null}
const run = await orbit.request(
  'POST',
  `/voice/ivr-flows/${FLOW_ID}/simulate`,
  { turns: [{ digits: '1' }], max_steps: 50 },
);
console.log(run.data.reached_nodes); // ['start', 'welcome', 'menu', 'bookings']
```

```python Python theme={null}
run = client.request(
    "POST",
    f"/voice/ivr-flows/{FLOW_ID}/simulate",
    json_body={"turns": [{"digits": "1"}], "max_steps": 50},
)
print(run["data"]["reached_nodes"])  # ['start', 'welcome', 'menu', 'bookings']
```

**200 OK** — `reached_nodes: ["start","welcome","menu","bookings"]`, menüde `gather` ve kuyrukta `enqueue` içeren `emitted_verbs` ve `termination_reason: "queue_answered"` (taşma tanımlanmamış bir kuyruk answered olarak biter) bekleyin. Yedek yolu da çalıştırın — `turns: []` boş gönderin, yürüyüş menüde `awaiting_input` ile bitsin; bir `digits: "9"` tuşlaması gönderin, simülatör `default` kenarınızı izleyerek sesli mesaja girsin. Kırmızı bir simülasyon, canlı bir dakika harcamadan önce yönlendirme gerilemelerini yakalar; bu yüzden onu IVR regresyon test düzeneğinizin ürün içi karşılığı olarak ele alın. DSL tanım JSON'u, isteği hangi istemcinin gönderdiğine bakılmaksızın bayt özdeştir — curl, Node SDK veya Python aynı grafiği POST eder.

Kullanışlı seçenekler: `source: "published"` canlı anlık görüntüyü yürütür, `source: "draft"` (varsayılan) kaydedilmemiş taslağı yürütür; `definition` (tam grafik gövdesi) kaydedilmemiş bir adayı satır içinde simüle eder.

Kabul zarfı — geri aldığınız başlıklar, gövde ve durum:

```json Response — 200 OK theme={null}
{
  "data": {
    "flow_id": "ivr_01h…",
    "source": "draft",
    "reached_nodes": ["start", "welcome", "menu", "bookings"],
    "emitted_verbs": ["say", "gather", "enqueue"],
    "termination_reason": "queue_answered"
  },
  "meta": { "request_id": "…", "timestamp": "…" }
}
```

Bir reddediş `422 IVR_FLOW_GRAPH_INVALID` döndürür (`meta.request_id` destek ayıklaması için doldurulur) ve grafiğin doğrulamayı geçemediği anlamına gelir — herhangi bir yürüyüş ilerlemeden düzeltilir.

## 5. Akışı bir DID'e bağlayın

Her DID'in gelen yönlendirme yapılandırması akış referansını taşır. `config.flowId` ile `type: "ivr"` ayarlayın:

```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..." } }'
```

Ses ağ geçidi akışı her gelen çağrıda çözer, bu yüzden bağlama anlıktır — bağlamadan önce yayınlayın, ilk arayan canlı anlık görüntüye düşsün. Bağıntıyı telefon etmeden uçtan uca denemek için simülatörü aynı akış kimliğiyle yeniden çalıştırın; bu, iki rotanın birleşimidir — yönlendirme satırı yürüyüşün hangi tanımı kullanacağını belirler, simülatör ise arayanın ne duyacağını birebir yansıtır.

## 6. Bir temsilciye devredin

`support` dalını bir `queue` düğümüyle (DSL deyimiyle `enqueue` verbi) bir ACD kuyruğuna yönlendirin ki bir ekip üyesi panodan devralsın; ya da tüm akışı, mevcut handback uç noktası konuşmayı koruyan ve bir insan istisnayı çözdükten sonra AI'ye geri eskale eden bir [AI agent'e](https://docs.orbit.devotel.io/voice/ai-agent-handback.md) yöneltin. Her iki yol da orada belgelenmiştir — bu akışın `queue` düğümü, devrin kuyruğu besleyen yarısıdır; handback sayfası niyet döngüsü dönüşünü kapsar.

## 7. Görsel oluşturucu — aynı grafik, JSON olmadan

Yukarıdaki her şey kodunuz için bir `{ nodes, edges }` grafiğidir, ancak operatörler genellikle panonun IVR Builder'ında çalışır — aynı grafiğin görsel bir ağaç olduğu birinci sınıf tasarlama yüzeyi. Bir DTMF menüsü, ürettiği ağaç olarak okunur: seçenek başına bir düğüm, tuş başına bir etiketli kenar. Paletten bir **Menu** düğümü bırakın; tuş başına zaten dallanır; her dalı bir kuyruğa, zil grubuna, sesli mesaja veya başka bir menüye bağlayın — tuval, Auto-layout ile ağacı okunur tutar. Taslaklar taslak olarak otomatik kaydedilir (bir arayanın ulaşamayacağı), Validate ön denetimi yayından önce erişilemez dalları, yinelenen tuşları ve çıkmazları işaretler, yerleşik simülatör ise betikli bir arayanı canlı bir dakika harcamadan taslak üzerinde adım adım yürütür. Publish, grafiği canlı anlık görüntüye çevirir — API'nin yazdığıyla aynı published-definition.

**Nereden açılır:** Panoda Voice → IVR Builder. Bu rehberdeki her düğüm türü palettedir ve aynı DID bağlama oluşturucudan iki tık uzaktadır: bir numaranın gelen yönlendirme sekmesinden akışı seçin (Numbers → *numara* → Routing) veya oluşturucu araç çubuğundan bağlayın. Yayından sonra, düğüm başına huni düşüşünü görmek için bir düğümün analytics panelini oluşturucuda açın — `node-analytics`'in döndürdüğüyle aynı veri — bu da genellikle sırada neyi değiştireceğinizi söyler.

**Hangi yüzeyi seçmelisiniz?** Oluşturucu tasarlama aracıdır; API, CI hatları ve altyapı-olarak-kod için aynı grafiktir. Yukarıdaki API güdümlü döngü tam sözleşmedir; oluşturucu özdeş nesneyi özdeş uç noktalara yazar.

### Oluşturucu panelindeki dil bilgisi düzenleyicisi

Bir **Speech Input** düğümü seçin; sağ panel tam bir konuşma dil bilgisi düzenleyicisine dönüşür — API'nin kabul ettiği aynı düğüm başına `speechIntents` nesnesi, satır satır düzenlenir:

* **Intent adı** — küçük harfler, rakamlar, tireler, alt çizgiler. Her ad, düğümde bir kenar handle'ı olur; o ifade için kuyruğa, AI agent'e veya alt akışa bağlayın. Panel yinelenen ve hatalı biçimli adları satır içinde işaretler; bir kenara zaten bağlanmış ad, alt kenarın başvurduğu şeydir — bağlamadan önce yeniden adlandırın.
* **Phrases** — satır başına bir. Her ifade Deepgram tanıma ipuçlarına düzleştirilir ve çağrı anında transkript, normalizasyondan sonra bunlarla eşleştirilir (büyük/küçük harf, noktalama, sözcük sırası hoş görülür). Eşleştirme deterministik ifade–transkript dizge işlemidir — canlı gather'da hiçbir LLM turu çalışmaz.
* **Keypad digit (optional)** — niyet başına tek bir DTMF rakamı (örn. `1`). Rakam her konuşmadan önce eşleştirilir, böylece sesi duyulmayan bir arayan yine de geçer. Yalnızca tuş takımı yedeği açıkken görünür.
* **Keypad fallback toggle** — rakamlar için aç/kapat anahtarı. Menüyü yalnız konuşma yapmak için kapatın: gather rakam toplamaz ve kimsenin eşleştiremediği bir ifade düğümün `nomatch` handle'ına düşer. Bu kontrol var olmadan önce kaydedilen düğümler açıkmış gibi davranır; yani eski bir konuşma menüsü, siz kapatana dek rakam kabul etmeyi sürdürür.
* **STT provider** — Varsayılan olarak Deepgram; düğüm farklı bir tanıyıcı gerektiriyorsa Google, Microsoft, AWS veya küme varsayılanını seçin.
* **Min confidence** — 0–1 konuşmadan-metne güven eşiği. Boş bırakmak sağlayıcı varsayılanını korur; ayarlarsanız, eşiğin altında tanınan bir transkript eşleşme yok sayılır ve gürültülü bir hat arayanı asla yanlış bir niyete düşürmez.

Çalışılmış örnek — tuş rakamı korunmuş bookings ve yalnız konuşma support ile iki niyetli bir konuşma menüsü:

```json speechInput node theme={null}
{
  "id": "speech-menu",
  "type": "speechInput",
  "data": {
    "prompt": "Say what you need — for example, book a flight, or talk to support.",
    "language": "en-US",
    "dtmfFallbackEnabled": false,
    "confidenceThreshold": 0.6,
    "speechIntents": [
      {
        "name": "bookings",
        "phrases": ["book a flight", "new booking", "reserve a seat"],
        "dtmfKey": "1"
      },
      {
        "name": "support",
        "phrases": ["talk to an agent", "customer support", "help me"]
      }
    ]
  }
}
```

Yukarıdaki düğüm gövdesiyle, transkript güveni 0,82 olan "I'd like to book a flight" diyen bir arayan 0,6 eşiğini aşar, `bookings` normalizasyondan sonra eşleşir ve yürüyüş `bookings` kenarını alır. Güveni 0,41 olan aynı ifade eşiğin altındadır; hiçbir şey eşleşmez ve yürüyüş `nomatch` kenarını alır. Yedek kapalıyken bile `1` rakamı hâlâ `bookings`'e yönlendirir — bir rakam her zaman konuşmadan önce kazanır — ancak `dtmfFallbackEnabled: false` iken gather hiç rakam toplamaz ve yukarıdaki her tuşlu giriş saf konuşmadır. (Gerçekten rakamsız bir varyant için `dtmfKey`'i de kaldırın.)

Panel, her niyetin taslağını kalıcılaştırılmış grafikten ayrı tutar: kayıtlı bir düğümün saklanmış ifadeleri yüklemede satır başına düzenleyiciye geri okunur, düzenlemeler ise kaydetmede wire biçimi olarak geri yazılır; böylece kayıtlı bir Speech Input düğümünü yeniden açmak asla boş satırlar çizmez.

Anahtar sözcük ifadelerinin ötesinde ifade başına yönlendirme (koval açıklamaları, tüm tenant genelinde güven eşikleri) [IVR intents kavram sayfasında](https://docs.orbit.devotel.io/voice/ivr-intents.md) yer alır — DTMF'siz konuşma menüleri bölümüne bakın.

## 8. Sürümleme ve geri alma

Her yayın, akışın sürüm geçmişine bir satır ekler. `GET /api/v1/voice/ivr-flows/:id/versions` her yayınlanmış anlık görüntüyü listeler (`version`, `definition`, `published_at`); `GET /:id/versions/:version/compare` bir anlık görüntüyü geçerli taslağınızla karşılaştırır ve eklenen, kaldırılan, değişen düğüm kimliklerini raporlar — böylece bir düzenlemenin tam olarak ne getirdiğini görürsünüz. Geri almak için `POST /:id/versions/:version/revert` o anlık görüntüyü geri yükler ve yeni baş olarak yeniden yayınlar — eski tanımı getirip yeniden bağlamak yerine tek çağrı. Bağlı DID, geri alınmış baş üzerinde yanıtlamayı sürdürür.

Yukarıdaki akış tamamdır: kaydetmede doğrulanmış, yayından önce simüle edilmiş, bir DID'e bağlanmış ve herhangi bir önceki sürüme kurtarılabilir. Bundan başlayın ve yalnızca dalları değiştirin.


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