Přeskočit na obsah

Napojení

REST API

Kompletní reference API v1. Vše je JSON a chyby mají jednotný tvar. Ceny volai jsou celá čísla v haléřích (přípona Hal); připojené faktury používají nejmenší jednotky uvedené měny.

Autentizace

Každý požadavek nese API klíč v hlavičce Authorization: Bearer vk_... . Klíč získáš v portálu v sekci Přístupy a API (podrobněji v Quickstartu).

bash
curl https://volai.cz/v1/balance \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Celé tohle API má i strojově čitelný popis na GET /openapi.json (OpenAPI 3.1, bez API klíče) - vygeneruj si z něj klienta, kolekci do Postmanu, nebo ho napoj rovnou do n8n.

Formát chyb

Neúspěch má vždy stejný tvar, ať selže cokoli:

json
{
  "error": {
    "code": "insufficient_credit",
    "cause": "account",
    "message": "Insufficient credit. Account balance is 3.00 CZK. Top up at volai.cz/en/credit and try again.",
    "action": "Top up the credit at https://volai.cz/en/credit (or ask the account owner to), then send the request again. Nothing was charged and volai itself is up.",
    "requestId": "req_9f2b7a1c4e6d8f0a",
    "docsUrl": "https://volai.cz/en/docs/api#errors"
  }
}

Těchhle pět kódů se může objevit prakticky kdekoli - u jednotlivých endpointů níž jsou dopsané jen situační kódy navíc:

HTTPKódPříčinaVýznam
401unauthorizedrequestChybí nebo je neplatná hlavička Authorization.
402insufficient_creditaccountNa akci nezbývá dost kreditu.
403account_suspendedaccountProvozovatel volai účet ručně pozastavil - napiš na podpora@volai.cz. Daňové doklady (GET /v1/billing/documents, /{id} a /{id}/pdf, v MCP list_billing_documents a get_billing_document) zůstávají dostupné i během pozastavení.
429rate_limitedbusyPřekročený rate limit - u endpointu s API klíčem počkej podle hlavičky Retry-After; veřejný GET /v1/changelog tuhle hlavičku neposílá.
500internal_errorserviceChyba na naší straně - zkus to prosím znovu.

400 (neplatný vstup) a 404 (záznam neexistuje) taky můžou přijít prakticky kdekoli, ale jejich code je specifický pro dané pole nebo endpoint - přesný výčet je vždy u konkrétního volání níž. Pár akcí navíc může vrátit 502 (chyba u externího poskytovatele - telefonní síť, ElevenLabs) nebo 503 (dočasně nedostupné, zkus to za chvíli).

Kdo za chybu může: cause a action

Každá chyba nese navíc cause (čtyři hodnoty) a action (jedna anglická věta, co udělat, kterou můžeš uživateli přečíst doslova). cause je strojově čitelná odpověď na otázku, jestli má chybu opravit tvůj kód, tvůj účet, čas, nebo my:

PříčinaKdo to opraví
requestTělo, URL, hlavičky požadavku nebo argumenty nástroje - včetně chybějícího nebo neplatného API klíče. Oprav vstup a pošli znovu; volai běží.
accountStav tvého účtu: kredit, limity, vlastnictví čísla nebo agenta, ověření e-mailu, konfigurace, nebo záznam, který na účtu neexistuje. Tělo je v pořádku; změň stav účtu (v portálu nebo jiným voláním), případně vezmi správné id z výpisu, a zkus to znovu.
busyDočasný zámek nebo limit okamžiku (jiná operace nad tím samým záznamem ještě běží, rate limit). Počkej pár sekund a pošli TOTÉŽ znovu, nic neměň.
serviceMy nebo náš dodavatel (operátor, hlasová platforma). Na tvé straně není co opravit; zkus to za chvíli, a když to trvá, napiš na podpora@volai.cz s requestId.

Pro AI agenty (Claude Code, Cursor, vlastní skripty): podle cause řekni uživateli, kdo chybu opraví. U request vyjmenuj pole z message, u account řekni, co v účtu změnit nebo které id zkontrolovat, u busy počkej a zopakuj, a jen u service mluv o problému na straně volai. Nikdy nehlas request, account ani busy jako výpadek volai.

Příčina je určená kódem, ne HTTP statusem: kvóty účtu (agent_limit, tool_limit, api_key_limit, relay_lease_limit) vrací 400, a přesto jsou account; not_found u záznamu je account (z odpovědi nejde poznat, jestli cizí id existuje), u neznámé cesty pod /v1 je request. Úplné přiřazení kódů k příčinám je u každé operace v GET /openapi.json (popis chybových odpovědí).

Hláška u kódu validation říká pole (tečkovaná cesta v těle, například dataFields.0.type), skutečnou hodnotu a přijatý strop, například Field "knowledge" is 210000 characters long, the maximum accepted is 200000. nebo Field "systemPrompt" is required. Produktové limity (60 / 6000 / 300 / 20000 znaků u jména, instrukcí, cíle a podkladů agenta) hlásí vlastní kódy invalid_name, invalid_system_prompt, invalid_goal, invalid_knowledge s přesným číslem. Neznámé pole ve striktních tělech (koncept agenta) vrací Unknown field "revision". Remove it; the documentation lists the accepted fields.

requestId a docsUrl

Úplně každá odpověď v1 (úspěšná i chybová) nese v hlavičce X-Request-Id jedinečné ID požadavku (req_ + 16 hex znaků) - u chyby je stejná hodnota navíc přímo v těle jako requestId. Přehraná idempotentní odpověď (viz Idempotence níž) dostane VLASTNÍ aktuální requestId - hlavička a tělo se u ní nikdy neliší, i když zbytek odpovědi je doslova stejný jako napoprvé. Pošli tohle ID podpoře a najde přesně tenhle jeden požadavek v logu.

Chybové tělo nese navíc docsUrl - odkaz rovnou sem, na tuhle sekci.

U konceptu agenta (viz sekci Koncept agenta níž) chyba revision_conflict nese navíc currentRevision - aktuální revizi. Načti ji přes GET .../draft a porovnej rozdíl, než uložení zopakuješ - neposílej naslepo to samé tělo se stejnou expectedRevision.

json
{
  "error": {
    "code": "revision_conflict",
    "cause": "request",
    "message": "The draft has changed since you last read it. Fetch the latest revision and try again.",
    "action": "Reload the record you are changing - GET /v1/agents/{id}/draft (or the get_agent_draft tool) for an agent draft, GET /v1/tasks/{id} (or the get_task tool) for a task - reapply your change on top of its current revision and send the request again with that value in expectedRevision (agent draft) or revision (task). This is not a volai outage.",
    "currentRevision": 4,
    "requestId": "req_9f2b7a1c4e6d8f0a",
    "docsUrl": "https://volai.cz/en/docs/api#errors"
  }
}

Hlavičky

Po ověření klíče nese KAŽDÁ odpověď (úspěšná i chybová) hlavičky RateLimit-Limit, RateLimit-Remaining a RateLimit-Reset (vteřin do konce aktuálního okna) - u 429 z limitu API klíče navíc Retry-After se stejným počtem vteřin. 429 rate_limited z limitu odchozích hovorů (POST /v1/calls) nese v Retry-After vlastní čekání do uvolnění toho limitu, když je známé.

401 nese hlavičku WWW-Authenticate: Bearer realm="volai", error="invalid_token" (RFC 6750) - HTTP knihovna z ní pozná důvod selhání, aniž by musela parsovat tělo.

Neznámá cesta a špatná metoda

Neznámá cesta pod /v1 (i holé /v1) vrací JSON 404 se stejným tvarem chyby jako kdekoli jinde - {"error":{"code":"not_found", ...}} - pro všechny metody (GET, POST, PUT, PATCH, DELETE, OPTIONS i HEAD). Výjimka: špatná METODA na EXISTUJÍCÍ cestě (například DELETE /v1/balance) zůstává obyčejná Next.js 405 bez JSON těla - tohle je jediné místo v celém API v1, kde neúspěch nemá popsaný tvar chyby.

Idempotence

POST /v1/messages, POST /v1/calls, POST /v1/relay, POST /v1/numbers, POST /v1/numbers/orders, POST /v1/sms-numbers, POST /v1/agents, POST /v1/tools, POST /v1/agents/{id}/draft/publish, POST /v1/agents/{id}/test-call, POST /v1/integrations, POST /v1/tasks, POST /v1/tasks/{id}/start, POST /v1/tasks/{id}/process, POST /v1/credit/topup a POST /v1/credit/auto-topup/setup umí hlavičku Idempotency-Key. Rozsah klíče je globální pro daný API klíč, bez ohledu na HTTP metodu, cestu nebo tělo. Každá odlišná logická akce napříč všemi endpointy a těly proto potřebuje novou hodnotu; stejnou hodnotu použij jen při opakování přesně téhož logického požadavku, typicky po timeoutu, kdy nevíš, jestli první pokus prošel. Do 24 hodin pak dostaneš zpátky přesně tutéž odpověď jako napoprvé, včetně chybové, dopadl-li první pokus chybou, a nic se neodešle ani nezavolá podruhé. Klidně použij ID objednávky ze své appky doplněné o název konkrétní akce. Souběžný požadavek se stejným klíčem, který ještě běží (odpověď se teprve ukládá), vrátí 409 idempotency_in_progress - zkus to prosím za chvíli znovu.

U POST /v1/numbers/orders se klíč trvale spojí s objednávkou ještě před voláním operátora; stejný klíč s jinou adresou vrátí 409 idempotency_conflict. Pokud operátor po nákupu vrátí nejasnou odpověď, může být potřeba kontrola stavu objednávky.

bash
curl -X POST https://volai.cz/v1/messages \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: objednavka-4471" \
  -d '{"to": "+420777123456", "body": "Děkujeme za objednávku."}'

Rate limity

60 požadavků za minutu na jeden API klíč (MCP sdílí stejný limit - používá tentýž klíč). Po překročení přijde 429 a hlavička Retry-After s počtem vteřin do dalšího pokusu.

POST /v1/messages má navíc vlastní strop: nejvýš jedna SMS za 2 vteřiny a 100 SMS denně na účet.

Odchozí hovory (POST /v1/calls, relay i zkušební hovor) mají dohromady vlastní strop: 10 hovorů za minutu a 1000 za den na účet, a souběžně můžou běžet nejvýš 2 hovory zabudovaného agenta.

Kolik ti z limitu 60/min zbývá, zjistíš bez čekání na 429 - v hlavičkách RateLimit-Limit, RateLimit-Remaining a RateLimit-Reset, které nese úplně každá odpověď (viz sekci Formát chyb > Hlavičky výš).

Stránkování

GET /v1/messages a GET /v1/calls berou limit (kolik záznamů max - výchozí 50, strop 200 i při vyšší zadané hodnotě) a before (ms timestamp - vrátí jen starší záznamy).

hasMore a nextBefore

Obě odpovědi nesou navíc hasMore (boolean - existuje starší stránka) a nextBefore (razítko posledního vráceného záznamu, nebo null, když hasMore je false). Pro další stránku pošli nextBefore z předchozí odpovědi jako nový before - žádné ruční počítání offsetu.

before je EXKLUZIVNÍ (vrátí jen záznamy starší, ne stejně staré) - poslední záznam předchozí stránky se v další stránce nikdy nezopakuje. Výjimečně mohou mít dva záznamy úplně stejné milisekundové razítko přesně na hranici stránky - v tom vzácném případě může druhý z nich vypadnout. Deduplikuj proto podle id, nespoléhej na to, že stránky nikdy nic nevynechají.

GET /v1/calls?direction=in nebo ?direction=out prohledává historii po dávkách s tvrdým stropem počtu dávek - hasMore: true proto může znamenat i jen to, že jsme prohledali dávky až do stropu a ještě jsme nenarazili na konec historie, ne jistotu další stránky. false naopak vždy znamená, že historie v daném směru doopravdy skončila.

Verzování

Aktuální verze veřejného rozhraní je 1.34.2 (GET /openapi.json v poli info.version - viz odstavec o strojově čitelném popisu API v úvodu stránky výš). Tři pravidla, podle kterých se verze mění:

  1. V rámci v1 přibývají jen NEPOVINNÁ pole a nové endpointy - stávající pole nikdy nezmizí, nezmění typ ani význam. Integrace, která čte jen pole, o kterých ví, nikdy nic nerozbije. Jedinou výjimkou byla verze 1.5.1, která u PŘÍCHOZÍCH hovorů přestala vracet answeredBy - pole u nich nikdy nemělo smysluplnou hodnotu (viz Historie změn níž).
  2. Povinné pole nebo změna významu existujícího pole přijde vždy jako nová hlavní verze (v2), nikdy jako tichá úprava v1.
  3. U konceptu agenta (Koncept agenta výš) vždy posílej zpátky přesně ten objekt, který jsi přečetl z GET .../draft - tělo PUT/POST je .strict(), takže neznámý klíč (třeba z novější verze, kterou tvá integrace ještě nezná) vrátí 400 validation místo tichého zahození.

Historie změn

  • 9. 10. 2026 (1.34.2): agentCallLimits v lib/service/call-quotas.ts vrací pro rozsah kampaně { concurrent: 8, daily: 6000, perMinute: 15 }; výchozí kvóta účtu (2, 1000, 10), podmínky zapnutí i společné čítače účtu se nemění. Chybová zpráva rate_limited u denního limitu uvádí 6000.
  • 9. 10. 2026 (1.34.1): CAMPAIGN_QUOTA_EXPIRES_AT v lib/service/call-quotas.ts je 2026-10-31T23:00:00Z; dál platí vypínač VOLAI_PETCENTER_CALL_QUOTA_ENABLED, přesná shoda účtu, agenta a interního kontextu odchozí úlohy a alespoň osm různých jmen v ODORIK_RELAY_NAMES. Chování API pro ostatní účty se nemění.
  • 9. 10. 2026 (1.34.0): Nové endpointy GET /v1/sms-numbers, POST /v1/sms-numbers (bez těla, podporuje Idempotency-Key, vrací 201) a DELETE /v1/sms-numbers/{e164} (nevratné, zaplacené období se nevrací) a MCP nástroje list_sms_numbers a buy_sms_number (confirmedMonthlyFeeHal musí odpovídat aktuálnímu poplatku v haléřích, jinak se nic nekoupí). GET /v1/messages a MCP list_messages berou direction (out ve výchozím stavu, in, all) a vrací i zprávy přijaté na SMS čísle; zprávy nově nesou direction, smsNumber a deliveryStatus (delivered nebo undelivered, jen u SMS odeslaných z čísla), status má novou hodnotu received a failReason hodnotu opted_out. POST /v1/messages a MCP send_sms berou fromNumber (aktivní SMS číslo účtu, jen příjemci +420, 1,90 Kč za segment). Nová událost webhooku message.received s daty {id, from, to, body, segments, receivedAt} (receivedAt je Unix ms); text přijaté SMS je cizí obsah a MCP u něj posílá securityNotice. Přijaté SMS se uchovávají 90 dní. Nové chybové kódy: sms_numbers_unavailable, sms_number_limit, sms_number_out_of_stock, invalid_from_number, from_number_unsupported_destination a recipient_opted_out; stávající kódy recipient_rejected, sms_gateway_failed, recipient_is_virtual_number (SMS od sdíleného odesílatele na SMS číslo volai) a release_failed platí i pro SMS čísla. Pole from je u zprávy odeslané bez fromNumber dál interní štítek volai, u zprávy odeslané ze SMS čísla samo číslo a u přijaté zprávy odesílatel.
  • 8. 10. 2026 (1.33.0): Politika callback-routing má volitelné missedFirstMessage (do 500 znaků): použije se místo firstMessage, když předchozí ověřený odchozí hovor neměl rozhovor se zákazníkem (nezvednuto, záznamník, hlasové menu, nikdo nepromluvil, nebo hovor ještě neuzavřený). Initiation posílá motoru volai_callback_kind (missed/contacted) vedle volai_call_mode=campaign_callback. Post-call motoru umí caller_optout_requested (jen odchozí hovory): hovor pak nese optOutRequested: true ve výpisu i detailu, číslo volaného se zapíše do seznamu nevolat účtu (GET /v1/dnc) a politika lorela_enterprise další pokus téže položky nenaplánuje. Hovor motoru s answered_by: unknown zůstává unknown - portál už místo něj nehádá záznamník z krátké odpovědi („Ano.“ po úvodu), takže hasConversation takového hovoru odpovídá přepisu; schránka rozpoznaná motorem (voicemailReason vyplněný) dostane v politice lorela_enterprise další pokus i přes hlášku operátora v přepisu. Nový důvod voicemailReason: digits (hláška operátora čtoucí volané číslo po číslicích). Vše aditivní; stávající politiky, hovory a úlohy se nemění.
  • 8. 10. 2026 (1.32.1): Pole from u zprávy zůstává interní štítek volai a OpenAPI ho nově popisuje; popis pole source říká, že hodnota inbound u zpráv nevzniká. Instrukce MCP serveru, SKILL.md a llms soubory říkají totéž. Chování API se nemění.
  • 8. 10. 2026 (1.32.0): Uloží výslovný seznam vlastních aktivních agentů motoru pro zákazníky s jednoznačným skutečným odchozím hovorem z tohoto čísla během lookbackSecs (60-604800 sekund). Neznámý, nejednoznačný nebo neověřený volající zůstane u výchozího agenta. Prázdné agentIds politiku vypne. Volitelná firstMessage platí jen pro ověřený callback. engineSyncRequired: true znamená, že je nutné samostatně aktivovat webhooks.inbound_route_url v konfiguraci motoru výchozího agenta a ověřit ji čtením; tento požadavek živou konfiguraci motoru nepřepisuje. Detail ověřeného příchozího hovoru obsahuje callbackOf.callId a případně serverové taskId a taskItemId. Nové REST GET/PUT /v1/numbers/{e164}/callback-routing a MCP get_callback_routing / set_callback_routing. Podepsaný kontext nástroje příchozího hovoru nese direction=in a prázdný dispatch/task/item. Serverový volai_call_mode=campaign_callback se nastaví jen při ověřené vazbě. Ověřený příchozí lidský kontakt ukončí další plánované pokusy stejné položky úlohy, bez přidání odchozího pokusu nebo ceny. Již běžící hovor a jeho účtování doběhnou. Serverová vazba dispatch/task/item se uchovává osm dní pro celé callback okno.
  • 8. 10. 2026 (1.31.0): POST /v1/tasks, PATCH /v1/tasks/{id}, create_task a update_task přijímají nepovinné retryPolicy: "lorela_enterprise" a expiresAt (kladné bezpečné celé číslo, Unix ms); obě pole se vracejí ve veřejném úkolu. Bez politiky zůstává maxAttempts 1-3, při založení výchozí 1; s politikou 1-4, při založení výchozí 4. PATCH bez limitu jej nemění. Politiku lze zapnout jen před prvním pokusem o dispatch. Druhý pokus je nejdřív osm minut od začátku prvního skutečného pokusu, v 08:00-22:00 Europe/Prague; třetí v dalším jiném okně 08:00-09:00 nebo 17:00-19:00; čtvrtý v opačném okně následující kalendářní den. Užší callingWindow včetně dnů platí dál. Opakovat lze jen no_answer, missed, busy nebo dokončenou hlasovou schránku bez kontaktu s člověkem; nejistý výsledek a chyba motoru vyžadují kontrolu. Politika nepovoluje ruční resolve s action: "retry" a po pěti dnech od prvního skutečného pokusu už nevolá. expiresAt platí i bez politiky a brání novému vytočení od termínu včetně prvního pokusu; probíhající hovor doběhne. DNC ani automatická blokace po třech neúspěších za 24 hodin se neobcházejí, takže čtvrtý pokus nemusí proběhnout.
  • 5. 10. 2026 (1.30.2): Hovor, u kterého motor caller_loop do post-callu nepošle (volající nejdřív mluvil s agentem a teprve potom na lince zůstala jen opakovaná hláška), má endReason: "completed" a běžnou cenu jako každý jiný dokončený hovor. Záložní synchronizace ztraceného post-callu (GET /v1/calls motoru) nově čte metrics.caller_loop_reported: hodnota 1 znamená, že motor caller_loop do post-callu poslal, a caller_loop se převezme jako dosud; hodnota 0 znamená běžný hovor, který se uzavře a naúčtuje s endReason: "completed" bez ohledu na metrics.caller_loop_released. U staršího motoru bez tohoto pole platí dosavadní pravidlo s metrics.caller_loop_released (kladná hodnota znamená běžný hovor). Tvary odpovědí REST a MCP, hodnoty endReason ani filtr outcome se nemění.
  • 4. 10. 2026 (1.30.1): endReason: "caller_loop" a priceHal: 0 se nově přidělují jen příchozímu hovoru s kladným durationSecs; odchozí hovor nebo hovor s nulovou délkou, ke kterému motor pošle termination_reason: "caller_loop", jde běžnou cestou (endReason: "completed" a běžné účtování, hovor s nulovou délkou zůstává neuzavřený jako dosud). Záložní synchronizace ztraceného post-callu (GET /v1/calls motoru) převezme caller_loop jen tehdy, když metrics.caller_loop_released chybí nebo je 0; při kladné hodnotě (během čekání se ozval člověk) se hovor uzavře a naúčtuje jako každý jiný dokončený hovor s endReason: "completed" a call.completed nese cenu, stejně jako když post-call dorazí sám. Hovor s endReason: "caller_loop" se v portálu nikdy nezařadí mezi hovory k pozornosti (Přehled a filtr v Hovorech) a týdenní e-mailový souhrn ho nepočítá mezi vyřízené hovory ani volající a neuvádí ho mezi hovory, které potřebují reakci. Tvary odpovědí REST a MCP, hodnoty endReason a filtr outcome se nemění.
  • 4. 10. 2026 (1.30.0): Nová hodnota endReason: "caller_loop" (REST GET /v1/calls a GET /v1/calls/{id}, MCP list_calls a get_call, webhook call.completed): příchozí hovor, kde volající byl automat opakující pořád tutéž hlášku (fronta, vytáčecí systém) a který motor ukončil pojistkou. Zdrojem je metadata.engine.termination_reason: "caller_loop" v post-callu motoru (u ztraceného post-callu totéž pole z GET /v1/calls motoru). Hovor má status: "completed", kladné durationSecs a priceHal: 0, z kreditu se nic nestrhne; cena 0 platí do pěti minut délky hovoru, delší hovor se účtuje podle běžného ceníku a endReason zůstává caller_loop. Webhook call.completed nese endReason: "caller_loop". Hovor, ve kterém se během čekání ozval člověk a motor s ním pokračoval, caller_loop nedostane. Ve filtru outcome (GET /v1/calls, list_calls) takový hovor patří do agent.
  • 4. 10. 2026 (1.29.6): PATCH /v1/agents/{id}, update_agent i publikace konceptu: změna language z jazyka, ve kterém noví agenti vznikají na ElevenLabs (dnes en, de, pl), na jazyk, ve kterém vznikají na motoru (cs, sk), spustí u agenta s provider: "elevenlabs" po uložení stejný pokus o převod na motor jako zapnutí transferTo. Při úspěchu má agent provider: "engine" a výchozí hlas motoru pro nový jazyk (milena, katarina), při selhání zůstává na ElevenLabs. Změna mezi cs a sk nic nepřesouvá. voicemail_requires_engine má nové znění: při založení radí useForOutboundTasks: true, u existujícího agenta podporu.
  • 4. 10. 2026 (1.29.5): POST /v1/agents a create_agent vracejí v warnings nový kód elevenlabs_by_explicit_choice ({code, message: {cs, en}}), když požadavek poslal useForOutboundTasks: false, výchozí nastavení nasazení by pro jazyk agenta zvolilo motor a agent na ElevenLabs opravdu zůstal (přepojení transferTo ho mohlo na motor přepnout). Agent se založí jako dřív, kód nic neblokuje. PATCH /v1/agents/{id}, publikace ani rollback konceptu ho nevracejí.
  • 3. 10. 2026 (1.29.4): Jen dokumentace, chování API se nemění. maxAttempts (1-3) je nejvyšší počet automatických vytočení jednoho kontaktu včetně prvního; 1 znamená bez opakování. Kontakt, kterému po vyčerpání pokusů dáš resolve s action: "retry", dostane ještě jedno vytočení navíc. Počítá se každý pokus, při kterém hovor mohl proběhnout (nezvednutý, odmítnutý volaným i s nejistým výsledkem). Nepočítá se pokus odmítnutý před vytočením, který má items[].attempts[].countsAsAttempt: false.
  • 3. 10. 2026 (1.29.3): Pro pokusy zaznamenané od této verze je items[].attempts[].countsAsAttempt rovno false pro všechny kódy, kdy hovor prokazatelně nevznikl, takže se nezapočítají do maxAttempts: agent_not_found, agent_no_number, insufficient_credit, invalid_number, unsupported_country, blocked_destination, on_dnc, destination_auto_blocked, cannot_call_own_number, cannot_call_volai_number, capacity_busy, call_rejected, validation, rate_limited, relay_lease_limit, invalid_workflow, engine_not_configured a destination_busy. Dřív se trvalé překážky v čísle (invalid_number, unsupported_country, blocked_destination, on_dnc, destination_auto_blocked, cannot_call_own_number, cannot_call_volai_number) počítaly, takže po zrušení blokace a dalším odmítnutí ukazoval kontakt více pokusů, než kolik jich bylo povoleno, přestože se nevolalo. Pokusy zapsané před touto verzí se zpětně nepřepisují (výjimkou je pokus, jehož záměr vytočení uzavře oprava účtování). Nejisté chyby vytáčení (například engine_unavailable) se počítají dál, protože hovor mohl vzniknout. Trvalá překážka dál posílá kontakt do review (rozhoduješ ty), opravitelný kód (chybějící kredit, agent bez čísla a podobně) úkol pozastaví; rate_limited a relay_lease_limit úkol nechají běžet a jen počkají. Nic se kvůli tomu nevytáčí dokola. Kontakt, který po vyčerpání pokusů vrátíš přes resolve s retry, nově po opravitelné chybě vytočení bez hovoru (například insufficient_credit) zůstane pending a po nápravě se znovu vytočí; dřív zůstal failed a úkol nešel spustit (task_not_startable).
  • 3. 10. 2026 (1.29.2): destination_busy při vytáčení z úkolu (hovor prokazatelně nevznikl): kontakt čeká dál (failed, když už měl započítaný pokus a má volný další, jinak pending) s items[].lastError: "destination_busy" a items[].nextAttemptAt podle zbývající doby zámku čísla (nejméně 30 s, nejvýš 16 min, bez zámku 90 s), pokus má countsAsAttempt: false, rezervace v reservedBudgetHal se uvolní a ostatní kontakty běží dál; lastError úkolu se nenastavuje, jen se smaže zastaralý čekací rate_limited nebo relay_lease_limit, protože obsazení dokazuje, že limit prošel. Po 6. obsazení za sebou (tedy po 5 čekáních) jde kontakt do review s reviewReason: "dial:destination_busy". Sedm trvalých kódů (on_dnc, destination_auto_blocked, cannot_call_own_number, cannot_call_volai_number, invalid_number, unsupported_country, blocked_destination) pošle kontakt do review s reviewReason: "dial:<kód>", ale úkol zůstává running a do needs_attention přejde, až nezbude jiná práce (dřív hned). V API a MCP se blokace ruší dvěma voláními: POST /v1/dnc/{e164}/unblock (unblock_destination), pak resolve s action: "retry" (resolve_task_item); u vlastního čísla, čísla jiného účtu volai, neplatného čísla, čísla mimo Česko a Slovensko a prémiové linky pomůže jen skip. Kontakt v review se může objevit i u běžícího úkolu; resolve pak vrací 409 task_not_editable, takže počkej na needs_attention, nebo úkol pozastav. POST /v1/calls se nemění.
  • 2. 10. 2026 (1.29.1): items[].providerCallId ukazuje jen na hovor posledního pokusu: kontakt ve stavu failed, který čeká na další pokus, ho nenese; hovor každého pokusu zůstává v items[].attempts[].providerCallId. Dřív na položce zůstal hovor prvního pokusu, další vytočený hovor se do úkolu nezapsal a kontakt se vytáčel znovu bez započtení pokusu a bez promítnutí ceny do spentHal. Chyby on_dnc, destination_auto_blocked, cannot_call_own_number a cannot_call_volai_number při vytáčení z úkolu jsou prokazatelně nevytočené: pokus má outcome: "failed", rezervace v reservedBudgetHal se uvolní hned a kontakt jde do review s reviewReason: "dial:<kód>" (dřív outcome: "unknown" a držená rezervace). POST /v1/tasks/{id}/start už nevrací 409 task_needs_attention kvůli rezervaci, kterou nedrží žádný kontakt; POST /v1/tasks/{id}/reconcile takovou rezervaci srovná a uzavře i starší otevřený záměr po nevytočeném pokusu.
  • 2. 10. 2026 (1.29.0): Nový endpoint POST /v1/numbers/orders/{id}/cancel (bez těla) a MCP nástroj cancel_number_order: objednávka pending přejde do nového stavu cancelled (nová hodnota status objednávky v GET /v1/numbers/orders a list_number_orders); provisioning vrací 409 in_progress, done a failed 409 invalid_transition, cizí nebo neznámé id 404 not_found; opakované zrušení vrací objednávku beze změny. Zrušená objednávka se nepočítá do brány prvního dobití (jedno číslo na účet bez dobití). callingWindow.end u úkolů je včetně celé minuty (current <= end): okno 00:00-23:59 pokrývá celý den a window_closed v minutě end už nepřijde.
  • 30. 9. 2026 (1.28.1): PATCH /v1/numbers/{e164} (MCP set_number_routing) s mode: "forward" vrací 402 insufficient_credit, pokud účet ještě nikdy nedobil kredit a číslo zatím není přesměrované. Číslo, které už ve forward je, jde přesměrovat na jiný cíl i bez dobití. POST /v1/numbers (MCP buy_number) a POST /v1/numbers/orders (MCP order_number_from_region) vrací 402 insufficient_credit při pokusu o další číslo, když účet bez dobití už drží aktivní číslo nebo má otevřenou objednávku (pending, provisioning). Stejný kód a příčina (account) jako u SMS, jiný text hlášky - říká, co udělat (první dobití kreditu); nic se neúčtuje. Po prvním dobití kreditu (karta, automatické dobití) omezení mizí.
  • 25. 9. 2026 (1.28.0): Nové pole suspended: boolean u agenta (REST i MCP, GET/POST/PATCH /v1/agents). Nové chybové kódy agent_suspended a account_suspended (403, cause: "account") - account_suspended na každém REST volání s API klíčem a v každém MCP nástroji (po rate limitu) kromě čtení daňových dokladů (GET /v1/billing/documents, /{id} a /{id}/pdf, MCP list_billing_documents a get_billing_document), které zůstávají dostupné; agent_suspended na POST /v1/calls, POST /v1/agents/{id}/test-call a POST /v1/tasks/{id}/start. Příchozí hovor zablokovaný pozastavením agenta nebo účtu nese endReason: "suspended" (nová hodnota vedle credit_blocked, cena 0). Odpověď account_suspended/agent_suspended se nikdy neukládá pod Idempotency-Key, ať stejný klíč po obnovení projde. Klíče vlastních proměnných (variables u POST /v1/calls a make_call) nově nesmí začínat předponou volai_ - je vyhrazená pro proměnné, které doplňuje volai (například volai_block_reason), a takový požadavek vrátí 400 validation; u úkolů se takový klíč odmítne až při vytáčení kontaktu. Pozastavení a obnovení dělá výhradně provozovatel v adminu - REST ani MCP pro to vlastní nástroj nemá. Věta, kterou volající slyší na zablokované lince (dluh i pozastavení), teď zní v jazyce agenta (cs, sk, en, de, pl).
  • 25. 9. 2026 (1.27.3): Beze změny rozhraní: stejný kód warnings agent_misuse_suspected ve stejných odpovědích REST i MCP, jen širší rozpoznávání.
  • 24. 9. 2026 (1.27.2): Odchozí úkoly: relay_lease_limit z vytáčení (kvóta 2 souběžných hovorů agenta na účet) se chová jako rate_limited - úkol zůstává running, nastaví lastError: "relay_lease_limit", pokus se nezapočítá (countsAsAttempt: false) a čekající kontakty dostanou items[].nextAttemptAt za minutu. Dřív šla položka do review s reviewReason: "dial:relay_lease_limit" a úkol do needs_attention. Synchronizace s motorem (GET /v1/calls) čte nové pole attempt_id odchozího hovoru a páruje jím záznam initiated po nejistém vytočení (bez call_sid, i u agenta bez vlastního čísla); po spárování přijde call.completed s cenou a uvolní se relay linka i kvóta. Vyžaduje motor s attempt_id ve výpisu hovorů; starší výpis se chová jako dřív.
  • 24. 9. 2026 (1.27.1): POST /v1/calls s agentId na agentovi vlastního motoru, zkušební hovor se systemPrompt, MCP make_call a odchozí úkoly: když vytočení u motoru skončí timeoutem, přerušeným spojením, odpovědí 500/504 nebo nečitelnou 2xx, vrací se dál 502 engine_unavailable, ale hovor zůstává initiated (bez priceHal), relay linka se neuvolní a stejné to je 15 minut zamčené (destination_busy); kvóta souběžných hovorů se uvolní hned. Když hovor proběhl, výsledek a cenu doplní post-call nebo synchronizace s motorem (post-call ho nově najde i bez call_sid podle identifikátoru pokusu, i u agenta bez vlastního čísla) a přijde call.completed; když ne, uzavře ho po 2 h watchdog jako failed s priceHal: 0. Webhook call.failed se u nejistého vytočení neposílá. Jisté selhání (spojení s motorem vůbec nevzniklo nebo vypršelo jeho navazování, 4xx, 502/503) se chová jako dřív: failed, priceHal: 0, call.failed.
  • 24. 9. 2026 (1.27.0): Nový kód warnings agent_misuse_suspected ({code, message: {cs, en}}). Objevuje se v POST/PATCH /v1/agents, PUT /v1/agents/{id}/draft, POST /v1/agents/{id}/simulate, POST .../draft/publish, POST .../draft/rollback, POST /v1/calls (prompt zkušebního hovoru nebo variables), POST/PATCH /v1/tasks, POST/PATCH /v1/tools a odpovídajících MCP nástrojích (create_agent, update_agent, save_agent_draft, simulate_agent_draft, publish_agent_draft, rollback_agent_draft, make_call, create_task, update_task, create_tool, update_tool). Pole warnings je nepovinné, chybí, když není co hlásit, a /openapi.json ho popisuje u všech těchto odpovědí.
  • 24. 9. 2026 (1.26.3): POST /v1/calls s from (bridge) a MCP make_call: když objednávka spojení u telefonního operátora skončí síťovou chybou, timeoutem nebo odpovědí 429/5xx, vrací se úspěch se status: "initiated" místo 502 callback_failed a hovor zůstává initiated, dokud ho cdr-sync nespáruje s CDR (a nezaúčtuje), nebo po 24 h uzavře watchdog jako failed s priceHal: 0. callback_failed teď znamená jen prokazatelné odmítnutí operátorem (chybová odpověď nebo 4xx). Webhook call.failed se u nejisté objednávky neposílá; stejné číslo to zůstává asi 10 minut zamčené (destination_busy).
  • 24. 9. 2026 (1.26.2): Strop hovorů na účet za 24 hodin (sdílený POST /v1/calls s agentem i mostem, relay a zkušebními hovory, MCP make_call i odchozími úkoly) je nově 1000 místo 200. Po jeho vyčerpání dál 429 rate_limited s hláškou o denním limitu; 10/min, souběžnost a rozpočet motoru se nemění.
  • 24. 9. 2026 (1.26.1): Odchozí úkoly: záznam hovoru ve stavu failed bez conversationId a bez priceHal (vytočení motor odmítl nebo nepotvrdil) se v reconcile usadí na 0 - dřív položka zůstala reviewReason: "billing_unknown" a úkol needs_attention navždy. resolve se skip u položky billing_unknown přepne úkol se zbývající prací do paused (rezervace původního hovoru se dál počítá do reservedBudgetHal a start ji toleruje); retry dál čeká na cenu. Rozhodnutí hned po chybě vytočení (reviewReason: "dial:...") už neztratí vazbu na hovor ani rezervaci. rate_limited při vytáčení (limit motoru, 10 hovorů za minutu, 200 za den) nechá úkol running s lastError: "rate_limited" a čekajícím položkám nastaví nextAttemptAt na konec limitu (podle Retry-After, jinak za 5 minut, nejvýš 24 hodin) místo paused; pokus se nezapočítá. invalid_workflow a engine_not_configured při vytáčení jsou prokazatelně bez hovoru (úkol paused, pokus se nezapočítá). Každý hovor agenta na vlastním motoru, který motor nevytočil (odchozí úkoly i POST /v1/calls, MCP make_call a portál), má v GET /v1/calls priceHal: 0 a durationSecs: 0 místo chybějících hodnot; webhook call.failed se nemění.
  • 24. 9. 2026 (1.26.0): POST /v1/calls s agentId na agentovi vlastního motoru (a MCP make_call): odmítnutí limitem odchozích minut motoru (hodinový a denní koš na účet) nebo plnou souběžností motoru je nově 429 rate_limited místo 502 engine_unavailable; u limitu minut s hlavičkou Retry-After (vteřiny do konce hodinového okna, u denního do půlnoci UTC). Nic se nevytočilo ani neúčtovalo; hovor v historii zůstává jako failed a webhook call.failed nese reason: "rate_limited". Odchozí úkoly berou rate_limited (i z limitu 10 hovorů za minutu a 200 za den) jako prokazatelně bez hovoru: položka failed bez započtení pokusu, úkol paused místo needs_attention. Chybová tabulka POST /v1/calls nově uvádí i 502 engine_unavailable - u agenta na vlastním motoru znamená jen skutečný výpadek motoru, ne limit.
  • 23. 9. 2026 (1.25.0): Nové volitelné pole event_id v obálce webhooku ({event, data, ts, event_id}) u call.completed/call.failed/call.no_answer/call.missed - message.sent a testovací webhook.test beze změny. Neúspěšné doručení těchto čtyř událostí po 3 pokusech (3 s odstup) nekončí: zůstává ve frontě a doručí se v následujících minutách, nejvýš do 5 kol; opakované doručení nese bajtově stejné tělo. GET /v1/webhook/deliveries (i MCP list_webhook_deliveries) teď ukazuje i mezistavy před posledním kolem, attempts je součet pokusů napříč všemi koly, a nově nese volitelné pole eventId.
  • 23. 9. 2026 (1.24.1): Dva nové kódy warnings: prompt_variables_unavailable_inbound (agent s numberE164, jehož systemPrompt/firstMessage obsahuje {{promennou}} mimo sadu, kterou příchozí hovor doopravdy dodává) a first_message_empty (firstMessage, ve které po odstranění všech {{...}} bloků nezůstane žádné písmeno ani číslice mimo proměnné). REST (POST/PATCH /v1/agents, publikace a návrat konceptu) i MCP (create_agent, update_agent, publish_agent_draft, rollback_agent_draft) čtou stejný zdroj jako editor agenta v portálu (agentWarnings).
  • 20. 9. 2026 (1.24.0): Nové nepovinné pole busyCalendarIds u agenta (POST/PATCH /v1/agents, MCP create_agent/update_agent, koncepty) i u hledání volna a zakládání události bez agenta (POST /v1/integrations/{id}/availability, POST /v1/integrations/{id}/events, MCP find_free_slots, create_calendar_event). Kalendáře se čtou paralelně; selhání kteréhokoli z nich je chyba celého hledání, aby se nenabídl termín přes neviditelnou schůzku. Nejvýš čtyři, bez duplicit a bez calendarId; jinak 400 calendar_invalid_rules, neznámý kalendář 400 calendar_invalid_calendar.
  • 20. 9. 2026 (1.23.4): Chybové tabulky POST /v1/agents, PATCH /v1/agents/{id} a publikace i návratu konceptu doplňují šestici, kterou vrací validateCalendar: 404 calendar_integration_not_found a 400 calendar_invalid_calendar, calendar_invalid_timezone, calendar_invalid_windows, calendar_no_windows, calendar_invalid_rules. Propisuje se do openapi.json, reference /docs/api, SKILL.md i llms-full.txt.
  • 20. 9. 2026 (1.23.3): maybeQueueMissedCallSms spouští SMS i pro odchozí hovor s answeredBy: "voicemail" (dosud jen status: "no_answer" a příchozí missed). Hlasové menu (ivr) ani unknown SMS nespouští. Ostatní podmínky (jen CZ/SK číslo, DNC, blokace, jedna SMS na číslo za 24 h, noční klid, poslední pokus úkolu) platí beze změny; CallRecord.missedSms se zapisuje stejně.
  • 19. 9. 2026 (1.23.2): Čas s milisekundami se formátoval s posunem zóny o minutu menším (+01:59 místo +02:00) - týkalo se to pole now v nástrojích agenta i začátku zakládané události, když klient poslal start se zlomky sekund. Náhradní termíny u 409 calendar_slot_taken (POST /v1/integrations/{id}/events, MCP create_calendar_event) se počítají od požadovaného termínu, stejně jako find_free_slots se zadaným časem.
  • 19. 9. 2026 (1.23.1): Pole calendar u agenta (POST/PATCH /v1/agents, MCP create_agent/update_agent a koncepty) odmítne kalendář s accessRole reader nebo freeBusyReader chybou 400 calendar_invalid_calendar - agent do téhož kalendáře schůzky zapisuje. Roli vrací list_calendars jako accessRole, pokud ji poskytovatel uvádí; kalendář bez uvedené role projde jako dřív. Ostatní chybové kódy se nemění.
  • 19. 9. 2026 (1.23.0): Nové pole CallRecord.rating (value: "good" | "bad", reason?, note?, at, source: "portal" | "email" | "api" | "mcp") a CallRecord.missedSms; PATCH /v1/calls/{id} a MCP annotate_call přijímají rating/ratingReason/ratingNote (patchCallAnnotation volá rateCall se source: "api"/"mcp"). Nové pole AgentRecord.missedCallSms { enabled, text? } (proměnné {firma}, {cislo}, {agent}) v POST/PATCH /v1/agents, GET /v1/agents/{id} a MCP create_agent/update_agent/get_agent/save_agent_draft. GET/PATCH /v1/account a MCP get_account/update_account mají companyName (max 80 znaků, prázdný řetězec maže) a GET nově vrací notifications.weeklySummary. Zkušební hovor v editoru agenta ukazuje výsledek podle cíle hovoru (call.evaluation.goal) a zapsaná data.
  • 18. 9. 2026 (1.22.0): Nové endpointy POST /v1/integrations/{id}/availability (volné sloty bez uloženého agenta - windows chybí = 24 hodin každý den) a POST /v1/integrations/{id}/events (založení schůzky, vyžaduje confirm: true, nepovinný idempotencyKey pro bezpečné opakování; 409 calendar_slot_taken při kolizi), MCP nástroje find_free_slots a create_calendar_event se stejným tvarem. DELETE /v1/integrations/{id} nově vrací affectedAgents - agenty, kterým odpojení integrace vypnulo pole calendar. Nové pole calendar u POST/PATCH /v1/agents, GET /v1/agents/{id} a MCP create_agent/update_agent/get_agent: integrationId, calendarId, timezone, slotMinutes, minNoticeMinutes, horizonDays, bufferMinutes, windows (pole oken {from,to} pro každý den v týdnu). Funguje jen na vlastním motoru volai (provider: "engine") - u agenta na ElevenLabs se pole uloží a čeká na varování calendar_pending_engine_switch, dokud agent nepřepne. Jedenáct nových chybových kódů s cause/action: calendar_slot_taken, calendar_confirmation_required, calendar_invalid_range, calendar_integration_not_found, calendar_invalid_calendar, calendar_invalid_timezone, calendar_invalid_windows, calendar_no_windows, calendar_invalid_rules, calendar_too_many_events, calendar_unavailable.
  • 18. 9. 2026 (1.21.0): Nový typ pohybu ledgeru setup_revision (GET /v1/credit/ledger, MCP) pro kolo úprav objednávky Na klíč nad rámec balíčku, částka SETUP_EXTRA_REVISION_HAL (490 Kč bez DPH). Daňové doklady (GET /v1/billing/documents, .../{id}, list_billing_documents, get_billing_document) mají nově vždy vyplněné pole kind ("invoice" - výchozí, nebo "credit_note") a k němu volitelná pole correctsNumber a correctionReason; dobropis k vratce zřízení Na klíč se čísluje vlastní řadou OD a nese kladné částky (znaménko dává PDF). credit() (lib/service/balance.ts) dostal interní parametr notifyTelegram - platba Na klíč posílá jen jednu Telegram hlášku ("Na klíč: zaplaceno"), ne dvě. Nová veřejná stránka /recepcni-na-klic (anglicky /en/receptionist-done-for-you).
  • 18. 9. 2026 (1.20.0): Nové pole laughter (auto/on/off) v POST/PATCH /v1/agents a MCP create_agent/update_agent: auto nechává rozhodnutí na detekci citlivého tématu motoru (dluhy, zdravotnictví, úřady, pohřebnictví), on tuhle detekci přebije, off ho vždy potlačí; vynechaný klíč se uloží i vrací jako auto, nikdy jako chybějící pole. GET /v1/agents/{id} a nový MCP nástroj get_agent (dřív chybějící parita pro detail jednoho agenta) přidávají laughterEffective ({active, reason, sensitiveCategory} | null) uvnitř vráceného objektu agenta, spočtené čerstvě pro publikovanou konfiguraci; null znamená, že se to teď nestihlo zjistit (motor neodpověděl do 3 vteřin, výpadek, nebo tichá 404 u starší verze) - nikdy odhad. MCP update_agent vrací týž objekt jako pole vedle agent, protože PATCH /v1/agents/{id} ho nevrací vůbec. Pole se přijímá a ukládá i u agenta na ElevenLabs, ale bez slyšitelného efektu - přehraje ho jen český hlas Cartesia na vlastním motoru volai.
  • 18. 9. 2026 (1.19.0): Nová oblast REST API v1 a MCP - zdroj setup-orders, capability oblast setup (za credit). Sedm endpointů (POST /v1/setup-orders, GET /v1/setup-orders, GET/POST /v1/setup-orders/{id}, .../messages, .../checkout, .../launch, .../cancel) a sedm odpovídajících MCP nástrojů (create_setup_order, list_setup_orders, get_setup_order, message_setup_order, checkout_setup_order, launch_setup_order, cancel_setup_order) nad stejnou servisní vrstvou lib/service/setup-orders.ts. Nabídku počítá vždy katalog v kódu (lib/setup-catalog.ts), model z rozhovoru jen vybírá položky. Nové chybové kódy: setup_order_not_found, order_closed, invalid_transition, quote_pending_confirmation, checkout_open, turn_in_progress, advisor_no_quote, advisor_unavailable, setup_orders_disabled. Dokumentace na /docs/na-klic (anglicky /docs/done-for-you).
  • 17. 9. 2026 (1.18.0): Koncept agenta (PUT/GET /v1/agents/{id}/draft, POST /v1/agents/{id}/draft/publish) i MCP save_agent_draft mají teď tříhodnotové numberE164: chybějící klíč nechá číslo agenta beze změny, null ho odpojí, řetězec připojí uvedené číslo - dorovnání parity s PATCH /v1/agents/{id}, který tuhle sémantiku měl už dřív. Čtení konceptu navíc srovnává published.numberE164 vždy se skutečným číslem živého agenta a draft.numberE164 jen tehdy, když koncept o čísle nerozhoduje, takže kontrolní tabulka v editoru i odchozí úkoly vidí pravdivý stav i po připojení nebo přesunu čísla mimo editor; "nerozhoduje" platí i pro řetězec, který se shoduje se ŽIVÝM číslem agenta - taková hodnota se chová jako chybějící klíč, a to znovu při každém čtení konceptu proti tehdy aktuálně živému číslu; přesune-li se číslo později na jiného agenta mimo koncept, stejná uložená hodnota se stane výslovnou volbou a další publikace nebo rollback konceptu ho tomuhle agentovi vezme zpátky - ne potichu: warnings u publikace i rollbacku (REST i obou MCP nástrojů) v tom případě nese nový kód number_changed_by_publish s from/to (E.164, nebo null bez čísla), a ani ten operaci neblokuje. Pozor na zpětnou kompatibilitu čtení: draft.numberE164 teď může v odpovědi přijít jako null (výslovná volba "bez čísla"), kde dřív klíč jen chyběl - klient ho má číst stejně jako chybějící klíč, tedy "agent nemá číslo". Značka "otestováno" a otisk agenta připnutý běžícímu odchozímu úkolu se navíc počítají BEZ telefonního čísla (behaviorFingerprint, ne draftFingerprint), takže připojení ani přesun čísla mimo editor už nevyžaduje nový test a nezastaví běžící kampaň; koncepty s číslem uložené PŘED touhle změnou mají starou značku spočítanou ještě SE číslem, takže si o ni řeknou jednorázově znovu - jeden test nebo publikace po aktualizaci ji obnoví a dál platí nové pravidlo.
  • 15. 9. 2026 (1.17.0): Nový stav hodnocení cíle GoalDisplayState (success/failure/unclear/no_conversation/not_evaluated) nahrazuje trojici AgentGoalResult v zobrazení portálu, ve filtrech /hovory a v přehledu. REST a MCP dostaly answeredBy: "ivr", goal.reason ("no_caller_speech"/"evaluation_error") a hasConversation (boolean | null). Parametr goal (GET /v1/calls, MCP list_calls) přijímá pět nových hodnot navíc ke stávající unknown, která zůstává zpětně kompatibilní - dál zahrnuje záznamy bez jasného výsledku (goal.result chybí nebo je unknown): vždy unclear a not_evaluated, u no_conversation jen když má taky výsledek unknown.
  • 13. 9. 2026 (1.16.0): Nová hodnota endReason: "engine_error" (status failed) nastává, když motor pošle metadata.engine.termination_reason s hodnotou engine_stall nebo engine_error u hovoru, který se obsluhoval - dřív se pole u takového hovoru zahazovalo. priceHal je 0, jen když hovor ještě žádnou cenu nemá (jinak zůstává, ALERT log). Zákaznický webhook call.failed dostal čtyři nová volitelná pole: durationSecs, priceHal, transcript, summary - vyplněná jen u téhle technické větve, jinde chybí jako dřív. GET /v1/calls/{id} a MCP get_call vrací stejnou hodnotu endReason. Odchozí úkoly (POST /v1/outbound-tasks, MCP) berou engine_error jako opakovatelný pokus (stejná cesta jako no_answer), ne jako důvod k zastavení kampaně, a nezapisují ho do automatické 30denní blokace destinace.
  • 13. 9. 2026 (1.15.0): POST/PATCH /v1/agents a MCP create_agent/update_agent/save_agent_draft berou nové pole silencePromptSecs: number | null (celé číslo 3 až 25, null vypíná), meze shodné s motorem (engine/types.py, U1). Chybějící pole při POST/create_agent uloží výchozích 10 - na rozdíl od voicemail se pole přijímá a vrací u OBOU poskytovatelů (GET /v1/agents vrací uloženou hodnotu i u provider: "elevenlabs"). Hodnota mimo meze padá na obecnou validační chybu (validation, 400), žádný nový kód. Existující agenty na motoru dostanou výchozích 10 jednorázovým re-synchronizačním skriptem (scripts/resync-silence-prompt.mts), který mění výhradně tohle jedno pole.
  • 13. 9. 2026 (1.14.7): OpenAPI doplňuje popisy všech operací a polí požadavků včetně vnořených variant. Publikuje produktové limity agentů, nástrojů, SMS a hovorů ze společných konstant servisní vrstvy, místo volných technických stropů parseru. Automatická kontrola ověřuje všechny ukázky požadavků a odpovědí proti schématům. Chování API, chybové kódy a nastavení poskytovatelů se nemění.
  • 13. 9. 2026 (1.14.6): POST /v1/messages a MCP send_sms vrací insufficient_credit (HTTP 402, cause account), dokud účet nemá kladný pohyb topup (karta i automatické dobíjení) nebo admin (ruční korekce majitele). Končí dřívější strop 3 SMS pro účty bez vlastního aktivního čísla a aktivní číslo už výjimku nedává. První dobití se drží jako trvalý příznak účtu, takže dlouhá historie pohybů zákazníka nikdy nezablokuje.
  • 13. 9. 2026 (1.14.5): Nový kód workflow_node_tool_missing ve warnings u POST/PATCH /v1/agents, u publikace i vrácení konceptu a u MCP create_agent, update_agent, publish_agent_draft a rollback_agent_draft. Platí pro oba poskytovatele, protože smazaný nástroj se do fáze nepřenese ani na vlastním motoru. workflow_node_tool_not_synced zůstává jen pro nástroj, který na účtu je, ale agent ho nemá mezi svými. Šablona Recepce nově doporučuje dvě věty do základního promptu: neodhadovat provozní údaje a ptát se jen na jednu věc najednou.
  • 12. 9. 2026 (1.14.4): Průvodce rozlišuje identifikátory OAuth souhlasů od API klíčů i při obnově starší uložené volby. Nedostupné ověření není považováno za odvolaný přístup.
  • 12. 9. 2026 (1.14.3): Doplněny katalogové soubory katty-studio.mp3 a katty-telefon.mp3 stejným generátorem a obsahem jako ostatní hlasy. Hlas agenta, poskytovatel ani nastavení telefonního provozu se nemění.
  • 12. 9. 2026 (1.14.2): Popisky kopírování a dalšího kroku odpovídají zvolenému způsobu připojení. Codex desktop při použití API klíče vede ke konfiguraci config.toml. Zavření mobilního menu zachovává návrat fokusu.
  • 12. 9. 2026 (1.14.1): Přihlášení zachovává cíl průvodce. Volba prostředí se ukládá k účtu odděleně od skutečného MCP ověření. Ruční návody zahrnují Claude Code, Codex CLI i desktop a API klíč jako alternativu. Nápověda a názvy Hlasoví agenti, Telefonní čísla a Data a integrace jsou sjednocené.
  • 12. 9. 2026 (1.14.0): Při vynechaném useForOutboundTasks záleží na VOLAI_ENGINE_DEFAULT_FOR_NEW, dostupnosti motoru a seznamu VOLAI_ENGINE_DEFAULT_FOR_NEW_LANGUAGES (bez nastavení cs,sk). Samotný jazyk motor nezaručuje. Explicitní true žádá motor, false začne na ElevenLabs. Platí pro portál, POST /v1/agents i MCP create_agent. Po založení ověř provider a voiceId; přepojení může vyvolat další pokus o změnu poskytovatele.
  • 12. 9. 2026 (1.13.0): Pole workflow je v POST a PATCH /v1/agents, v konceptu agenta i v MCP nástrojích create_agent, update_agent a save_agent_draft. Hovor, který fázemi prošel, nese workflowPath v GET /v1/calls a GET /v1/calls/{id}. Nová varování workflow_*, workflow_not_synced_to_provider a workflow_node_tool_not_synced upozorní na problémy s nastavením fází.
  • 12. 9. 2026 (1.12.5): voiceId pro agenta na ElevenLabs: katty (výchozí, hlas šablony) nebo syrové ElevenLabs id; jana už v katalogu ani u ElevenLabs není (dva agenti z 12. 9. odpoledne přepnuti na Katty, hlas odstraněn z workspace). GET /v1/voices?provider=elevenlabs a MCP list_voices s provider: elevenlabs vrací katty. Záskok motoru na ElevenLabs pro Milenu, Terezu a Katarinu je také Katty.
  • 12. 9. 2026 (1.12.4): Pomalé čtení stavu už nepřerušuje pravidelný polling. Evidence úspěšného MCP čtení se dohledá podle aktivního přístupu i při chybě zápisu pomocného indexu. Ověření nadále vyžaduje get_balance nebo list_agents přes MCP.
  • 12. 9. 2026 (1.12.3): Opraven anglický odkaz na ochranu kalendářových dat a zobrazení aktivního filtru cíle i v účtu bez dosavadního vyhodnocení. Obě jazykové varianty mají regresní testy odkazů a nedostupných integrací.
  • 12. 9. 2026 (1.12.2): MCP průvodce používá existující evidenci úspěšných čtení aktivním API klíčem nebo OAuth souhlasem, bez simulovaného ověření. Filtr goal=unknown v GET /v1/calls a MCP list_calls zahrnuje i záznamy bez hodnocení cíle. Technické dokončení neznamená splněný cíl. REST a MCP nadále vracejí jednotlivé záznamy; portál je seskupuje do konverzací.
  • 12. 9. 2026 (1.12.1): voiceId pro agenta na ElevenLabs: jana (výchozí, hlas šablony) nebo syrové ElevenLabs id; anet projde jen agentovi, který ji už má, jinak 400 validation s vysvětlením (dřív 502 eleven_labs_error od ElevenLabs voice_live_moderated_not_allowed). GET /v1/voices?provider=elevenlabs a MCP list_voices s provider: elevenlabs vrací jana. Agent na ElevenLabs bez voiceId dostane Janu výslovně (záznam nese její id). Registrace SIP: operátor vracel 403 INVALID_USER u linek koupených přes API, dokud se u linky jednou nepřepne SIP protokol vypnout/zapnout; opraveno u všech linek a při nákupu nové linky se dělá automaticky.
  • 12. 9. 2026 (1.12.0): OAuth authorization code + PKCE S256, DCR, resource-bound opaque tokens, refresh rotation/replay detection, OIDC discovery/JWKS/userinfo a revokace. Access token 10 minut, refresh nejvýše 30 dní, souhlas nejvýše 90 dní. Změna hesla zneplatní OAuth přístup. MCP annotations výslovně rozlišují přepisy, placené akce, zprávy a externí vedlejší efekty.
  • 12. 9. 2026 (1.11.0): GET /v1/numbers/{e164}/sip a MCP get_sip_credentials: outboundTrunkAddress = sip.volai.cz (vlastní SIP proxy volai s digest ověřením, realm sip.volai.cz; naměřeno: platforma odmítne INVITE větší než UDP MTU, přes TCP tentýž hovor projde), transport = tcp (schéma nově enum [tcp, udp], UDP funguje také), port 5060 beze změny. Watchdog hlídá healthz proxy (SIP_PROXY_HEALTHZ_URL): ops e-mail při nedostupnosti, zastavené synchronizaci ověřovacích dat nebo certifikátu pod 14 dní. Post-call webhook ElevenLabs má v produkci HMAC tajemství, zapíná se per agent (ELEVENLABS_POST_CALL_WEBHOOK_ID).
  • 12. 9. 2026 (1.10.0): GET /v1/numbers/{e164}/sip a MCP get_sip_credentials: outboundTrunkAddress se řídí samostatnou konfigurací (může být shodná se server), popisy polí bez zmínek o realmu; inboundSignallingCidrs beze změny. Post-call webhook ElevenLabs páruje příchozí hovory přes metadata.phone_call.call_sid (dřív četl SIP Call-ID call_id, který se nikdy neshodoval s klíčem call:sid z initiation webhooku) sdílenou findExistingCallByKeys s cronem conversations-sync (pořadí call:conv -> call:attempt -> call:sid). Per-agent nastavení post-call webhooku (platform_settings.workspace_overrides.webhooks) migruje cron, jakmile je nastavené ELEVENLABS_POST_CALL_WEBHOOK_ID; do té doby hovory dál uzavírá cron. Post-call z vlastního motoru přijímá phone_call.external_number: null a caller_presentation; prefix uri_ u volajícího se odstraňuje jako v initiation webhooku. Cíl přepojení hovoru (transfer_to_number) se skládá ze samostatné VOLAI_SIP_TRANSFER_HOST, ne z veřejné SIP domény. Nový interní zdroj ověřovacích dat pro SIP proxy (GET /api/internal/sip-subscribers, mimo v1, bearer + IP allowlist + rate limit).
  • 12. 9. 2026 (1.9.0): POST/PATCH /v1/agents a MCP create_agent/update_agent teď mohou vrátit druhý kód ve warnings: first_message_too_long, nezávisle na first_message_no_ai_disclosure - a stejné warnings od teď vrací i publikace konceptu agenta (POST /v1/agents/{id}/draft/publish, MCP publish_agent_draft) a jeho vrácení na dřívější revizi (POST /v1/agents/{id}/draft/rollback, MCP rollback_agent_draft), kde dřív chyběly úplně - obojí totiž propisuje první větu na živého agenta. Odhad počítá slova, číslice po znacích a velké zkratky do čtyř znaků; delší velké slovo se čte jako jedno slovo, ať firma psaná kapitálkami odhad uměle nenafoukne. Jediný práh (odhadovaná doba nad 7 sekund) se měří nad tím, co volající doopravdy slyší - pozdrav plus věta o nahrávání, když je zapnuté. Nezměněná výchozí šablona pozdravu varování nikdy nedostane, jen text, který zákazník upravil.
  • 12. 9. 2026 (1.8.2): V dokumentaci, integračních návodech i chybových hláškách najdeš přístupy pod názvem AI asistent. MCP jasně říká, že hlasový motor je potřeba až pro spuštění odchozího úkolu; založit lze i úkol s agentem na ElevenLabs.
  • 12. 9. 2026 (1.8.1): OpenAPI doplňuje Idempotency-Key pro 14 podporovaných operací, response hlavičky a nepovinné JSON tělo s revision při smazání úkolu. Oprava popisů bez změny chování placených akcí: useForOutboundTasks rozlišuje true/false/vynechání, úkoly mají limit 1000 příjemců a billing_unknown zachovává původní hovor i rezervaci do zjištění ceny. Daňové doklady kreditu jsou dostupné v REST a MCP, API klíč může přes REST odvolat jen sám sebe, odpojení integrace je REST/portál. Oprava údaje z 1.6.1: preview_task_csv není MCP nástroj; CSV náhled je POST /v1/tasks/csv-preview nebo portál. Instalační příkazy sdílejí generátor s průvodcem, přepínače příkladů mají unikátní kotvy a Python zachovává JSON hodnoty.
  • 11. 9. 2026 (1.8.0): Nové REST POST /v1/dnc/{e164}/unblock a odpovídající MCP nástroj unblock_destination ruší automatickou blokaci destinace; fungují i pro blokace založené před tímhle nasazením. Starší blokace mohou ve výpisu GET /v1/dnc a list_dnc chybět, protože vznikly před jeho indexem, ale přímé odblokování podle čísla funguje. Indexované blokace se vracejí vedle beze změny zachovaného numbers v poli blocked: [{e164, blockedAt, expiresAt}], kde jsou časy unix milisekundy. Po odblokování se čítač neúspěšných pokusů počítá od nuly - tři nové neúspěšné pokusy na stejné číslo blokaci vrátí a odblokovat lze opakovaně. groupCallsByConversation (sdílený primitiv pro /hovory, export CSV, navigaci Předchozí/Další i statistiky) řadí podle representative.startedAt sestupně, dřív podle key, tedy podle abecedy identifikátoru konverzace. TestCallOutcome má novou hodnotu no_answer oddělenou od failed, ať panel netvrdí příčinu, kterou nezná. Vokativ jména psaného celými verzálkami je nově taky verzálkami (RADEK -> RADKU) v e-mailu i v hlavičce Přehledu.
  • 11. 9. 2026 (1.7.1): agent_busy (HTTP 409, cause: "busy") přibyl do sdílené chybové mapy PUBLIC_ERROR_HTTP/PUBLIC_ERROR_CAUSE (lib/types.ts). Vrací ji switchAgentProvider (lib/service/engine-provider.ts) - jediná admin akce portálu, která poskytovatele agenta přepíná, REST v1 ani MCP takový vstup nemají (agents.switch_provider v katalogu schopností, lib/capabilities.ts). Kontrolu dělá nový klient GET /v1/calls/active (lib/engine.ts listActiveCalls): hovor agenta se páruje PRIMÁRNĚ podle pole agent_id, které motor u běžícího hovoru nese od nasazení verze s tímhle polem, číslo zůstává záloha pro starší motor bez něj.
  • 11. 9. 2026 (1.7.0): POST/PATCH /v1/agents a MCP create_agent/update_agent/save_agent_draft berou nové pole voicemail: {enabled, action: "mark"|"hangup"|"message", message?} - jen u agenta s provider: "engine", jinak voicemail_requires_engine; message je povinná při action: "message", do 400 znaků (invalid_voicemail_message). GET /v1/agents vrací nastavenou hodnotu. Post-call z motoru nese answered_by/voicemail_reason/voicemail_message_left/voicemail_detected_at_secs; answered_by má přednost před dosavadním odhadem z přepisu hovoru (lib/answered-by.ts) jen hodnotami human a voicemail - unknown se bere jako "motor nerozhodl" a použije se odhad z přepisu. GET/list_calls/get_call u odchozích hovorů motoru navíc vrací voicemailReason (čtvrtá hodnota human_reply - krátká odpověď člověka), voicemailMessageLeft a voicemailDetectedAtSecs.
  • 11. 9. 2026 (1.6.5): Motor u odchozího hovoru ukončeného před zvednutím smaže místnost a pošle post-call: prázdný přepis, call_duration_secs: 0 a nové aditivní pole metadata.engine.termination_reason (no_answer nebo disconnected_before_answer). Hovor dostane status: no_answer, endReason: no_answer a cenu 0 jako dřív, jen hned. Rekonciliace GET /v1/calls páruje odchozí hovory motoru přes call_sid a u nespojených posílá nulovou délku. GET /openapi.json: answeredBy má description o omezení na odchozí hovory.
  • 11. 9. 2026 (1.6.4): POST /v1/tasks a PATCH /v1/tasks/{id} (i create_task/update_task v MCP) přijímají recipients do 1000 položek, POST /v1/tasks/csv-preview do 1000 řádků. Nová kontrola velikosti záznamu úkolu (2 MB serializovaného JSONu) vrací validation s polem recipients.
  • 11. 9. 2026 (1.6.3): Initiation webhook ElevenLabs: agent_id z payloadu se teď porovnává s elevenAgentId NEBO id agenta dohledaného přes routing čísla - cizí agent_id dostane jen minimální odpověď bez conversation_config_override; stejná kontrola chrání uzavírání konverzace v post-call webhooku i cronu synchronizace konverzací. Cron synchronizace CDR: nerozpoznaný odchozí CDR záznam z vlastní linky (mimo demo číslo), starší než 600 s a bez nejasně spárovatelného existujícího záznamu, se naúčtuje jako kind: "sip" odchozí sazbou bez agentní přirážky (v GET /v1/calls se objeví s kind: "sip"; čítač chargedSip je jen v odpovědi cronu, ne ve veřejném API); mladší nebo nejasně spárovatelný záznam se dál jen podrží pro ruční kontrolu. CallKind rozšířen o "sip". Tvar CDR záznamu skutečného SIP klienta zatím nemá produkční ověření (žádný registrovaný softphone) - proto obě pojistky.
  • 11. 9. 2026 (1.6.2): Nový endpoint POST /v1/tasks/{id}/items/{itemId}/resolve s tělem { action: "retry" | "skip", revision } a MCP nástroj resolve_task_item; nový chybový kód item_not_resolvable (409, account), task_not_editable u běžícího nebo hotového úkolu. Synchronizace hovorů motoru páruje vlastní odchozí nohu podle relay linky a času začátku, nezvednutý odchozí hovor zavírá jako no_answer, po uzavření uvolňuje tenant kvótu souběžných hovorů. relay_lease_limit při vytáčení úkolu je dočasný stav bez započítání pokusu.
  • 11. 9. 2026 (1.6.1): POST /v1/tasks/csv-preview a portál rozpoznávají sloupec telefonu bez ohledu na velikost písmen, mezery a koncovou dvojtečku a přijímají aliasy (telefon, tel, mobil, phone number); v odpovědi je sloupec vždy phone. Jednosloupcový vstup bez hlavičky, jehož první hodnota je telefon, se bere jako data. Bez sloupce telefonu vrací náhled jedinou chybu phone u hlavičky, ne Phone is required na každém řádku. Kontrakt agent_not_ready se nemění. Oprava popisu 12. 9. 2026: původní zmínka nástroje MCP byla nesprávná; náhled CSV je pouze REST/portál.
  • 11. 9. 2026 (1.6.0): GET /v1/numbers/{e164}/sip a MCP get_sip_credentials vrací navíc outboundTrunkAddress, port, transport a inboundSignallingCidrs vedle server/username/password; MCP create_relay_lease a popis nástroje get_sip_credentials teď výslovně rozlišují server (registrace) od outboundTrunkAddress (odchozí trunk platformy). Dokumentace na /docs/vlastni-agent, integrace ElevenLabs i blog už do outbound_trunk_config.address nedosazují server, jen outboundTrunkAddress.
  • 11. 9. 2026 (1.5.1): GET /v1/calls, GET /v1/calls/{id}, webhook call.completed a MCP list_calls/get_call: answeredBy je přítomné jen při direction: "out" (i u starších záznamů). POST /v1/messages a send_sms: účet bez aktivního čísla a bez kladného pohybu topup/refund/admin v ledgeru dostane po třech odeslaných SMS insufficient_credit (HTTP 402, cause account).
  • 8. 9. 2026 (1.5.0): Nové REST endpointy: GET/PATCH /v1/account, PUT /v1/account/billing, GET /v1/api-keys, DELETE /v1/api-keys/{id} (self-revoke); PATCH /v1/calls/{id} a rozšířené query GET /v1/calls o from, to, agentId, flagged, goal, outcome; POST /v1/tools/test, POST /v1/tools/{id}/test, DELETE /v1/webhook a GET /v1/tools/{id} navíc vrací agents; POST /v1/agents/{id}/draft/rollback; GET /v1/numbers/{e164}, GET/POST /v1/numbers/waitlist, DELETE /v1/numbers/waitlist/{offerId}, GET /v1/numbers/{e164}/sip/status; POST /v1/integrations, DELETE /v1/integrations/{id}, POST /v1/integrations/{id}/events/{eventId}/propose; GET /v1/changelog (bez klíče, x-volai-version na všech v1 odpovědích včetně chyb, 429 a /openapi.json). Odpovídajících 16 nových MCP nástrojů: get_account, update_account, update_billing_details, list_api_keys, annotate_call, test_tool, remove_webhook, list_webhook_deliveries, rollback_agent_draft, reconcile_agent_draft_operation, test_call, get_number, join_number_waitlist, leave_number_waitlist, get_sip_status, get_changelog - odvolání cizího API klíče, tvorba API klíče a odpojení integrace/karty zůstávají REST-only nebo jen v portálu (spec §3). Nový katalog schopností nese capability pole u každého endpointu a nástroje; strážné testy ověřují, že chybová tabulka a ukázka odpovědi na /docs/api sedí na skutečných chybových kódech u všech endpointů. Aditivní, žádný existující status ani kód se nemění. Nový soubor content/changelog.json je jediný zdroj pravdy: /novinky, sekce Historie změn na /docs/api, sekce Versioning v SKILL.md, RSS (/en/feed/<oblast>), GET /v1/changelog a MCP get_changelog jsou nad ním jen generované pohledy. Aditivní, žádný existující obsah nezmizel. Nové REST endpointy: GET/POST /v1/tasks, GET/PATCH/DELETE /v1/tasks/{id}, POST /v1/tasks/{id}/start, POST /v1/tasks/{id}/pause, POST /v1/tasks/{id}/reconcile, POST /v1/tasks/csv-preview. Odpovídajících 7 nových MCP nástrojů: list_tasks, get_task, create_task, update_task, pause_task, reconcile_task, start_task (ten vyžaduje zopakovat skutečný počet příjemců a rozpočet, jinak vrátí chybu s vypsanými skutečnými čísly) - smazání úkolu a náhled CSV zůstávají jen REST a portál (spec §3). Portál: tlačítko Smazat úkol v detailu úkolu s opsáním názvu. Aditivní, žádný existující status ani kód se nemění. Nové REST endpointy: GET /v1/credit/ledger, POST /v1/credit/topup, GET/PATCH /v1/credit/auto-topup, POST /v1/credit/auto-topup/setup, POST/DELETE /v1/credit/auto-topup/card, GET /v1/billing/documents, GET /v1/billing/documents/{id}, GET /v1/billing/documents/{id}/pdf; GET /v1/balance navíc vrací runway a notice. Odpovídajících 6 nových MCP nástrojů: list_ledger, create_topup_link, get_auto_topup, disable_auto_topup, list_billing_documents, get_billing_document (get_balance rozšířen o stejné runway/notice) - get_billing_document nevrací PDF, to zůstává jen přes REST GET /v1/billing/documents/{id}/pdf (binární výstup); zapnutí automatického dobíjení, změna částky a karty zůstávají jen přes odkaz na Stripe Checkout, MCP nemá ani jedno z toho (spec §2, §3). Aditivní, žádný existující status ani kód se nemění. GET /v1/account vrací nové pole notifications.productUpdates (výchozí zapnuto); přepínač je i v Nastavení portálu. Denní e-mailový souhrn (cron 0 6 * * * UTC) posílá jen účtům s ověřeným e-mailem a productUpdates !== false, nejvýš jeden e-mail na účet a den. Hlavička List-Unsubscribe a jednoklikové odhlášení bez cookies platí od teď i pro reaktivační e-mail, pokud je na serveru nastavené UNSUBSCRIBE_SECRET - bez něj e-mail odejde i tak, jen bez odkazu na odhlášení. Aditivní, žádný existující status ani kód se nemění.
  • 8. 9. 2026 (1.4.3): Každá vlastnost vstupního schématu všech 44 MCP nástrojů nese vlastní description (vidí ji klient v tools/list; u těla PATCH .../events/{eventId} a PUT /v1/agents/{id}/draft i openapi.json). Fakturační endpointy GET /v1/integrations/{id}/invoices[/{invoiceId}] mají v dokumentaci a v OpenAPI seznam chyb podle skutečné dosažitelnosti - integration_storage_error z nich zmizel, z API nikdy nepřicházel - a validace jejich query vrací lidskou hlášku jako zbytek v1 (stejně GET .../events). Aditivní, žádný status ani kód se nemění.
  • 8. 9. 2026 (1.4.2): Oprava čtení a potvrzených úprav Apple událostí: iCloud odmítá hledání podle UID, proto výpis vrací neprůhledné identifikátory zdrojových souborů CalDAV. externalId předávej beze změny jako eventId; Apple identifikátory uložené ze starší verze načti znovu. Neplatný Apple identifikátor vrací 400 calendar_invalid_query. Google identifikátory se nemění.
  • 8. 9. 2026 (1.4.1): POST /v1/agents a create_agent berou useForOutboundTasks (volba vlastního hlasového motoru volai při zakládání, stejná jako zaškrtávátko „Používat v odchozích úkolech“ v portálu; bez pole platí výchozí nastavení, dnes ElevenLabs). Aditivní, žádný status ani kód se nemění.
  • 8. 9. 2026 (1.4.0): Apple Calendar používá stejný kalendářový tok v REST i MCP jako Google. Fakturoid a ABRA Flexi přinesly endpointy GET /v1/integrations/{id}/invoices[/{invoiceId}] a nástroje list_invoices a get_invoice (rozhraní faktury jen čte). GET /v1/integrations a list_integrations vrací navíc providers s připraveností konfigurace a odkazem pro připojení. Přihlašovací údaje poskytovatele je pořád nutné připojit v portálu. Aditivní, žádný status ani kód se nemění.
  • 7. 9. 2026 (1.3.0): Přibyly kalendářové endpointy GET /v1/integrations, GET /v1/integrations/{id}/calendars, GET /v1/integrations/{id}/events, GET/PATCH /v1/integrations/{id}/events/{eventId} a šest MCP nástrojů (list_integrations, list_calendars, list_calendar_events, get_calendar_event, propose_calendar_update, confirm_calendar_update) - Google Kalendář tak vstoupil do veřejného rozhraní (čtení a potvrzená úprava události), REST je na 50 endpointech a MCP na 42 nástrojích. Každá chybová odpověď nese navíc cause (request / account / busy / service - kdo chybu opraví) a action (jedna věta, co udělat). Hlášky kódu validation říkají pole, skutečnou hodnotu a přijatý strop místo surového textu validátoru; stropy vstupu u jména, instrukcí, cíle a podkladů agenta jsou desetinásobkem produktových limitů, takže limit hlásí vždy produktová chyba s přesným číslem. MCP chyby mají stejná pole ve structuredContent.error (včetně requestId) a text s prefixem podle příčiny. Vše je aditivní, statusy ani kódy se nemění.
  • 6. 9. 2026 (1.2.0): Endpointy /v1/agents/{id}/draft* a /simulate ztratily obal ok a chybový prostý řetězec - chyby mají od teď stejný tvar {error:{code,message,requestId,docsUrl}} jako zbytek v1; revision v tělech požadavků se přejmenovala na expectedRevision; přibylo hasMore/nextBefore u stránkování, hlavičky X-Request-Id a RateLimit-*, POST /v1/webhook/test, GET /v1/webhook/deliveries a GET /openapi.json. Endpointy konceptu byly veřejné už od 5. 9. 2026, ale bez stabilního obalu - jsou proto ze záruky (pravidlo 1 sekce Verzování) do verze 1.2.0 vyjmuté.

Úplný a průběžně aktualizovaný seznam je na stránce Novinky (i jako RSS kanál).

Konvence

Zvyklosti, které platí napříč endpointy - ne jen u jednoho z nich.

Identifikátory

Každé ID nese prefix podle typu záznamu a je jinak neprůhledné (dál ho neparsuj):

vk_ API klíč, ag_ agent, ad_ koncept agenta, ado_ operace nad konceptem, c_ hovor, msg_ SMS zpráva, tl_ nástroj, whd_ doručení webhooku, rl_ relay lease, int_ připojení (integrace), inv_ daňový doklad (GET /v1/billing/documents), l_ položka historie kreditu, task_ úkol, item_ příjemce v úkolu, attempt_ pokus o vytočení položky úkolu, req_ ID požadavku (viz Formát chyby výš).

Časy

Časová razítka jsou skoro všude unixové milisekundy (createdAt, startedAt, boughtAt, issuedAt, taxableAt) - VÝJIMKA jsou pole napojeného kalendáře A dokladů: start, end, fetchedAt (kalendář) a parametry timeMin/timeMax jsou řetězce RFC 3339 (2026-09-08T10:00:00.000Z), nikdy číslo; GET /v1/integrations/{id}/invoices a .../invoices/{externalId} jsou taky řetězce - fetchedAt stejně RFC 3339, dueDate je čisté datum (2026-09-08) přímo od Fakturoidu/ABRA Flexi, nebo null, když ho poskytovatel nedal.

Stránkování - tři schémata a výpisy bez

GET /v1/messages, GET /v1/calls a GET /v1/billing/documents berou limit + before (ms razítko, exkluzivní) a vrací hasMore/nextBefore - podrobně viz Stránkování výš. Události napojeného kalendáře (GET /v1/integrations/{id}/events) místo toho používají neprůhledný pageToken/nextPageToken - timeMin/timeMax/search musí mezi stránkami zůstat stejné, pageToken sám o sobě to nezajistí. GET /v1/credit/ledger bere jen limit (1-200, výchozí 50), žádný kurzor - vždy začíná od nejnovější položky. GET /v1/tasks taky nemá kurzor a vrací nejvýš 100 nejnovějších úkolů. GET /v1/webhook/deliveries taky nemá kurzor a vrací nejvýš posledních 50 doručení (úspěšná, neúspěšná i testovací). Hledání v napojených dokladech se taky tiše ořízne - prvních 40 řádků u Fakturoidu nebo 50 u ABRA Flexi, bez kurzoru na další; místo stránkování dotaz zúži. Všechno ostatní, co vrací seznam (agenti, čísla, nástroje, API klíče, seznam nevolat, hlasy), vrací celou množinu najednou - není co stránkovat.

Neznámé a české parametry

Neznámý query parametr se skoro všude tiše ignoruje (ať starší klient nespadne na jeden navíc poslaný parametr) - výjimkou jsou endpointy napojeného kalendáře a napojených dokladů (/v1/integrations/{id}/...), které neznámý parametr odmítnou chybou 400 validation, protože ho posílají dál poskytovateli a tiše zahozený filtr by tam vypadal jako chybějící data, ne jako chyba; a z jiného důvodu i GET /v1/credit/ledger a GET /v1/billing/documents, jejichž query schéma je schválně striktní, ať překlep v limit/before spadne hlasitě místo tichého ignorování.

Pár polí schválně nese české jméno: tok adresy k číslu (GET /v1/numbers/address-options, pak POST /v1/numbers/orders - prosté POST /v1/numbers bere jen e164/region a adresu ignoruje) posílá psc, obec, cobce, ulice (jen tam, kde adresa ulici má) a cp - přesně jak je pojmenuje číselník RÚIAN (vlastní adresní registr operátora), odkud se číslo objednává, ať se hodnota mezi nimi nikdy tiše nepřeloží špatně. GET /v1/calls/{id}/recording?stahnout=1 je jediný český parametr mimo tenhle tok (stáhnout jako přílohu místo zobrazení v prohlížeči). Všechno ostatní - identifikátory, pole i parametry - je anglicky.

Kvóty na jednom místě

Každá kvóta je zmíněná i u svého endpointu výš - tady je jen souhrn:

  • 60 požadavků/min na API klíč (REST a MCP si limit dělí, viz Rate limits výš)
  • SMS: 1 za 2 s a 100/den na účet, až po prvním dobití kreditu
  • SMS odesílané ze SMS čísla (fromNumber): navíc 20 zpráv za den na účet v prvních 30 dnech od koupě čísla, potom 50; nejvýš dvě SMS čísla na účet
  • Odchozí hovory (agent, most, zkušební): 10/min a 1000/den na účet, nejvýš 2 souběžné hovory přes vestavěného agenta
  • Zkušební hovor bez vlastního čísla (POST /v1/calls se systemPrompt místo agentId/from): navíc 3/24 h na účet A sdílený strop 30/hod napříč VŠEMI účty dohromady - do sdíleného stropu narazíš i bez vlastního zavinění.
  • POST /v1/agents/{id}/test-call: 3/den na účet
  • POST /v1/tools/test a .../{id}/test: 10/min na účet
  • POST /v1/agents/{id}/simulate: 20/hod na účet
  • POST /v1/webhook/test: 10/hod na účet
  • GET /v1/changelog: 30/min na IP adresu (bez API klíče)
  • Úkoly: běžící úkol vytočí nejvýš JEDNOHO příjemce za jeden běh cronu (*/2 * * * *) a jeden běh se dotkne nejvýš 20 úkolů napříč VŠEMI účty - i s kreditem a otevřeným oknem volání jsou tak volání z jednoho úkolu od sebe zhruba dvě minuty, a při vytížení (přes 20 běžících úkolů) se úkoly střídají spravedlivě, ne jeden po druhém do konce.

Opakování webhooku

Neúspěšné doručení dostane až 3 pokusy po 3 sekundách v rámci téhož odeslání. U hovorových událostí (call.completed, call.failed, call.no_answer, call.missed) a u message.received tím doručování nekončí: událost zůstává ve frontě a doručení se v následujících minutách ještě zopakuje, nejvýš do 5 kol - dedupuj podle event_id v těle, kdyby ti výjimečně dorazila víckrát. message.sent a testovací webhook.test frontu nemají: po neúspěchu se jen zaloguje a znovu se neposílá. Průběh (včetně mezistavů před posledním kolem, attempts je součet pokusů napříč VŠEMI koly) uvidíš v GET /v1/webhook/deliveries (nebo list_webhook_deliveries).

Zastarávání

v1 zatím jen přibývá (viz Verzování výš) - jedinou výjimkou byla verze 1.5.1, která u PŘÍCHOZÍCH hovorů přestala vracet answeredBy (pole u nich nikdy nemělo smysluplnou hodnotu, u odchozích hovorů se nemění). Skutečné zrušení pole v v1 volai zatím neudělalo. Až k tomu dojde, pole dostane deprecated: true v GET /openapi.json a odpověď ponese hlavičky Deprecation/Sunset aspoň 90 dní předem, MCP odpověď, která se pole dotkne, navíc dostane položku ve warnings (stejné pole, jaké dnes nese upozornění na chybějící informaci o AI podle Aktu o umělé inteligenci) - samo odebrání přijde vždy jen jako nová hlavní verze (v2), nikdy tiše uvnitř v1.

Hovory vs. konverzace - viz stejnojmenný odstavec v sekci Hovory níž, než budeš sčítat počty.

Přihlašovací údaje (heslo, token, aplikační heslo) pošli jen tehdy, když ho uživatel už má někde, odkud ho umíš přečíst (proměnná prostředí nebo soubor, který sám pojmenoval) - nikdy si o něj neříkej v konverzaci a nikdy ho neopakuj zpátky; platí dnes pro Apple Kalendář a ABRA Flexi (viz sekce o kalendáři a dokladech níž).

Co přes API nejde

Telefonii ovládáš přes API a MCP celou - čísla, hovory, SMS, agenty, webhooky, relay i seznam nevolat. Profil účtu, fakturační adresa a firemní údaje jdou přes API taky (sekce Účet výš). Pár věcí ale zůstává jen v portálu, záměrně:

CoKde to je a proč
Dobití kredituPortál /kredit (jednorázově i automatické dobíjení). Platba kartou je krok člověka - agent, který si sám doplní kredit, aby mohl dál volat, je přesně to, co nechceme. Když akce narazí na insufficient_credit, řekni to uživateli a pošli ho sem.
Vytvoření nového API klíčePortál /api-a-mcp. Výpis (GET /v1/api-keys) a odvolání VLASTNÍHO klíče (DELETE /v1/api-keys/{id}) jdou přes API - jen vytvoření nového klíče ne: klíčem by se vyráběl další klíč, únik jednoho by pak znamenal trvalý přístup, který se nedá odebrat.
Změna heslaPortál /nastaveni. Jméno, jazyk účtu i fakturační adresa jdou přes PATCH /v1/account a PUT /v1/account/billing (sekce Účet výš) - heslo ne, je to čistě prohlížečová akce s vlastním potvrzením.
Daňové dokladyPortál /kredit, sekce Doklady - ke každému dobití kreditu tam čeká PDF ke stažení. Přes API se doklad nedá vystavit ani stáhnout: je to účetní dokument s vlastní číselnou řadou, který se nikdy nemění a nesmí vzniknout dvakrát.
SMS na hlasových číslechNikde - hlasová čísla volai SMS nepřijímají. SMS přijímá jen doplněk SMS číslo, viz sekce SMS čísla níž.

Opačným směrem toho v MCP schválně chybí víc: uvolnění zakoupeného čísla (DELETE /v1/numbers/{e164}) je v REST API i v portálu, ale ne mezi MCP nástroji - nevratná akce, na kterou je jedna špatně pochopená věta málo. Stejně schválně chybí i výdej nahrávky (binární tělo, GET /v1/calls/{id}/recording), detail jedné zprávy nebo jednoho nástroje (výpisy list_messages/list_tools nesou stejná pole, jen ne pro jediné ID) a samostatný nástroj pro zkušební hovor - make_call s agentId vytočí stejný hovor jako POST /v1/agents/{id}/test-call, jen bez jeho přísnějšího denního limitu 3 hovorů.

Účet

Profil, tísňová adresa, fakturační údaje a přehled API klíčů. Tvorba nových API klíčů (POST /v1/api-keys) se nevystavuje - klíč, který razí klíče, by mohl ze ztráty jednoho udělat trvalý přístup. Nový klíč vytvoříš v portálu (sekce Přístupy a API). E-mailová upozornění (callSummary, lowCredit, reactivation, productUpdates) jdou přes tohle rozhraní jen ČÍST - zápis je výhradně v portálu, protože lowCredit je jediná brzda mezi docházejícím kreditem a tichým vyčerpáním.

GET/v1/account

Profil, tísňová adresa, fakturační údaje a stav e-mailových upozornění (jen čtení).

Požadavek

bash
curl https://volai.cz/v1/account \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "account": {
    "id": "u_8f2a1c9d3b47",
    "email": "radek@example.com",
    "name": "Radek Klein",
    "locale": "cs",
    "emailVerified": true,
    "createdAt": 1755000000000,
    "companyName": "Ukázková s.r.o.",
    "address": { "street": "Na Zámku 636", "city": "Nehvizdy", "zip": "250 81" },
    "billing": null,
    "notifications": { "callSummary": true, "lowCredit": true, "reactivation": true, "productUpdates": true, "weeklySummary": true }
  }
}

Chybové stavy

  • 404user_not_foundUživatel, kterému patří API klíč, na účtu chybí - ojedinělý stav, obrať se na podporu.

PATCH/v1/account

Mění jméno a jazyk účtu. locale řídí jazyk e-mailů a daňových dokladů, NE jazyk téhle odpovědi - REST a MCP mluví vždy anglicky. Aspoň jedno pole musí být přítomné.

Požadavek

bash
curl -X PATCH https://volai.cz/v1/account \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"locale":"en"}'

Odpověď

json
{
  "account": {
    "id": "u_8f2a1c9d3b47",
    "email": "radek@example.com",
    "name": "Radek Klein",
    "locale": "en",
    "emailVerified": true,
    "createdAt": 1755000000000,
    "companyName": "Ukázková s.r.o.",
    "address": { "street": "Na Zámku 636", "city": "Nehvizdy", "zip": "250 81" },
    "billing": null,
    "notifications": { "callSummary": true, "lowCredit": true, "reactivation": true, "productUpdates": true, "weeklySummary": true }
  }
}

Chybové stavy

  • 400validationTělo požadavku není platný JSON objekt, neobsahuje ani jedno pole k úpravě, name je prázdný řetězec nebo přesahuje 200 znaků, nebo locale není cs ani en.
  • 400invalid_nameJméno po odstranění mezer na začátku a na konci zůstalo prázdné.
  • 404user_not_foundUživatel, kterému patří API klíč, na účtu chybí - ojedinělý stav, obrať se na podporu.

PUT/v1/account/billing

Ukládá tísňovou adresu a firemní údaje. address, když je přítomné, nese ulici, město i PSČ najednou - žádné se nedá poslat samostatně. company, ico a dic se slučují s tím, co je uložené: chybějící pole se NEMĚNÍ, jen prázdný řetězec ("") pole smaže (u dic i jeho ověření ve VIES). Zahraniční DIČ z EU se ověří v registru VIES; vies v odpovědi je vyplněné jen tehdy, když výsledek nese ověřené DIČ (čerstvě, nebo z podrženého staršího ověření). Tuzemské DIČ (CZ...) se neověřuje - na sazbu nemá vliv.

Požadavek

bash
curl -X PUT https://volai.cz/v1/account/billing \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"company":"Ukázková s.r.o.","dic":"SK9999999999","address":{"street":"Na Zámku 636","city":"Nehvizdy","zip":"250 81"}}'

Odpověď

json
{
  "billing": {
    "company": "Ukázková s.r.o.",
    "ico": null,
    "dic": "SK9999999999",
    "dicCountry": "SK",
    "dicVerified": true,
    "dicName": "Ukázková s.r.o."
  },
  "address": { "street": "Na Zámku 636", "city": "Nehvizdy", "zip": "250 81" },
  "vies": { "dic": "SK9999999999", "country": "Slovakia", "name": "Ukázková s.r.o." }
}

Chybové stavy

  • 400validationTělo požadavku není platný JSON objekt, obsahuje neznámé pole (schéma je striktní), company/ico/dic přesahuje 120/20/20 znaků, address obsahuje neznámé pole, address.street nebo address.city je prázdné nebo přesahuje 200 znaků, nebo address.zip má méně než 3 nebo víc než 10 znaků.
  • 400invalid_addressUlice nebo město po odstranění mezer na začátku a na konci zůstaly prázdné (třeba pole složené jen z mezer), nebo PSČ neprojde normalizací - nevypadá jako poštovní směrovací číslo.
  • 400invalid_icoKontrolní součet IČO nesedí.
  • 400invalid_dicDIČ nemá platný tvar (tuzemské CZ a 8-10 číslic, zahraniční kód země a číslo registru).
  • 400foreign_vat_unsupportedKód země u DIČ není členský stát EU - přenesení daňové povinnosti platí jen uvnitř EU.
  • 400vat_id_not_foundEvropský registr plátců (VIES) tohle DIČ nezná. Bez ověření se účtuje česká DPH.
  • 503vat_registry_unavailableRegistr VIES je momentálně nedostupný - zkus to znovu později; starší ověření (pokud existuje) zůstává v platnosti.
  • 404user_not_foundUživatel, kterému patří API klíč, na účtu chybí - ojedinělý stav, obrať se na podporu.

GET/v1/api-keys

Seznam API klíčů na účtu. id je jen prvních 12 znaků otisku klíče - plný otisk ven nejde nikdy.

Požadavek

bash
curl https://volai.cz/v1/api-keys \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "apiKeys": [
    {
      "id": "3f9a2c8e1b04",
      "label": "Production integration",
      "prefix": "vk_8h2j4kx",
      "createdAt": 1750000000000,
      "lastUsedAt": 1757280000000
    }
  ]
}

DELETE/v1/api-keys/{id}

Self-revoke: přes API smí klíč odvolat JEN SÁM SEBE, ne cizí klíč na účtu - jinak by ukradený klíč odstřihl všechny ostatní integrace a sám zůstal aktivní. Odvolání ostatních klíčů je jen v portálu. Po odvolání jde majiteli e-mail bez ohledu na to, kdo odvolání zadal.

Požadavek

bash
curl -X DELETE https://volai.cz/v1/api-keys/3f9a2c8e1b04 \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "revoked": true
}

Chybové stavy

  • 404not_foundKlíč s tímhle id na účtu není.
  • 403api_key_self_onlyid v cestě patří jinému klíči, než kterým je požadavek autentizovaný - přes API lze odvolat jen sám sebe.

Kredit

balanceHal i všechny sazby v tomhle API jsou bez DPH - volai je plátce DPH, ale DPH se řeší až při dobíjení kreditu kartou v portálu (přehled na stránce /cenik), API samotné s ní nepočítá. Podrobný průvodce s příklady toku je na /docs/kredit.

GET/v1/balance

Aktuální zůstatek kreditu na účtu, plus runway (odhad, na kolik dní kredit vydrží při současném tempu útraty) a notice (stejné upozornění jako na /prehled v portálu, null když žádné není) - obojí ADITIVNĚ, existující klient čtoucí jen balanceHal/balanceCzk/currency se nerozbije.

Požadavek

bash
curl https://volai.cz/v1/balance \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "balanceHal": 8730,
  "balanceCzk": 87.30,
  "currency": "CZK",
  "runway": { "dailyAverageHal": 2910, "runwayDays": 3 },
  "notice": { "kind": "runway", "days": 3 }
}

Chybové stavy

  • 503ledger_unavailableZůstatek nebo historii pohybů se momentálně nedaří přečíst - zkus to znovu za chvíli.

GET/v1/credit/ledger

Historie pohybů kreditu, nejnovější první, BEZ kurzoru (jen limit, 1-200, výchozí 50). description u každého záznamu je anglický popis SLOŽENÝ z type - uložená česká poznámka ven nejde nikdy.

Požadavek

bash
curl "https://volai.cz/v1/credit/ledger?limit=3" \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "ledger": [
    { "id": "l_9f2a1c8e", "ts": 1757280000000, "type": "call_out", "amountHal": -420, "grossHal": null, "description": "Outbound call" },
    { "id": "l_7c1e9a2f", "ts": 1757193600000, "type": "number_fee", "amountHal": -9900, "grossHal": null, "description": "Monthly number fee for +420601234567" },
    { "id": "l_3b7e1c9a", "ts": 1757107200000, "type": "topup", "amountHal": 50000, "grossHal": 60500, "description": "Card top-up" }
  ]
}

Chybové stavy

  • 400validationlimit je mimo rozsah 1-200, nebo požadavek nese neznámý parametr navíc.
  • 503ledger_unavailableZůstatek nebo historii pohybů se momentálně nedaří přečíst - zkus to znovu za chvíli.

POST/v1/credit/topup

Vytvoří odkaz na Stripe Checkout pro jednorázové dobití pevnou částkou v korunách. Nic se nestrhne, dokud platbu sám nedokončí ČLOVĚK v prohlížeči. Podporuje Idempotency-Key.

Požadavek

bash
curl -X POST https://volai.cz/v1/credit/topup \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: topup-2026-09-08-01" \
  -d '{"amountCzk": 500}'

Odpověď

json
{
  "checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3d4"
}

Chybové stavy

  • 400validationamountCzk chybí, není celé kladné číslo, nebo tělo obsahuje neznámé pole.
  • 400invalid_amountČástka není jedna z pevně nabízených hodnot.
  • 400billing_address_requiredFakturační adresa na účtu chybí nebo je neúplná - bez ní nejde vystavit platný daňový doklad. Doplň ji přes PUT /v1/account/billing.
  • 404user_not_foundUživatel, kterému patří API klíč, na účtu chybí - ojedinělý stav, obrať se na podporu.
  • 503stripe_not_configuredPlatby jsou pro tenhle běh volai vypnuté - netýká se konkrétně tvého požadavku.
  • 502stripe_errorStripe odmítl vytvořit platební relaci - zkus to znovu za chvíli.

GET/v1/credit/auto-topup

Stav automatického dobíjení: zapnuto/vypnuto, práh, částka, uložená karta (jen značka a poslední čtyři číslice), útrata a strop za tenhle kalendářní měsíc.

Požadavek

bash
curl https://volai.cz/v1/credit/auto-topup \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "autoTopup": {
    "enabled": true,
    "thresholdHal": 10000,
    "amountHal": 50000,
    "card": { "brand": "visa", "last4": "4242" },
    "spentThisMonthHal": 50000,
    "monthlyCapHal": 1000000,
    "disabledReason": null
  }
}

PATCH/v1/credit/auto-topup

VÝHRADNĚ vypnutí ({"enabled": false}) a/nebo změna prahu ({"thresholdHal": ...}) - zapnutí a změna částky jde jen přes POST .../setup (Stripe Checkout), nikdy přímým zápisem, ať s uloženou kartou nemůže holé enabled: true spustit strhávání bez člověka u karty.

Požadavek

bash
curl -X PATCH https://volai.cz/v1/credit/auto-topup \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"thresholdHal": 20000}'

Odpověď

json
{
  "autoTopup": {
    "enabled": true,
    "thresholdHal": 20000,
    "amountHal": 50000,
    "card": { "brand": "visa", "last4": "4242" },
    "spentThisMonthHal": 50000,
    "monthlyCapHal": 1000000,
    "disabledReason": null
  }
}

Chybové stavy

  • 400validationTělo obsahuje enabled: true nebo amountHal (obojí patří jen do POST .../setup), thresholdHal je mimo nabízenou řadu, nebo tělo neobsahuje ani enabled, ani thresholdHal.
  • 400autotopup_not_configuredAutomatické dobíjení ještě nikdy neprošlo POST .../setup - není co vypnout, změnit ani jakou kartu vyměnit.
  • 400invalid_amountČástka není jedna z pevně nabízených hodnot.

POST/v1/credit/auto-topup/setup

Vytvoří Stripe Checkout, jehož dokončení OKAMŽITĚ strhne amountHal jako první dobití a teprve tím uloží kartu pro budoucí automatická dobití - funguje i s kartou už uloženou z dřívějška. Jediná cesta, jak dobíjení zapnout nebo mu změnit částku; pouhou změnu prahu bez platby dělá PATCH výš. Fakturační adresa je povinná. Podporuje Idempotency-Key.

Požadavek

bash
curl -X POST https://volai.cz/v1/credit/auto-topup/setup \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: autotopup-setup-2026-09-08-01" \
  -d '{"thresholdHal": 10000, "amountHal": 50000}'

Odpověď

json
{
  "checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3d4"
}

Chybové stavy

  • 400validationthresholdHal nebo amountHal chybí, nejsou celá nezáporná/kladná čísla, nebo tělo obsahuje neznámé pole.
  • 400invalid_amountČástka není jedna z pevně nabízených hodnot.
  • 400billing_address_requiredFakturační adresa na účtu chybí nebo je neúplná - bez ní nejde vystavit platný daňový doklad. Doplň ji přes PUT /v1/account/billing.
  • 404user_not_foundUživatel, kterému patří API klíč, na účtu chybí - ojedinělý stav, obrať se na podporu.
  • 503stripe_not_configuredPlatby jsou pro tenhle běh volai vypnuté - netýká se konkrétně tvého požadavku.
  • 502stripe_errorStripe odmítl vytvořit platební relaci - zkus to znovu za chvíli.

POST/v1/credit/auto-topup/card

Vytvoří Stripe Checkout v režimu výměny karty pro automatické dobíjení - nic se nestrhne, jen se uloží nová karta.

Požadavek

bash
curl -X POST https://volai.cz/v1/credit/auto-topup/card \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3d4"
}

Chybové stavy

  • 400autotopup_not_configuredAutomatické dobíjení ještě nikdy neprošlo POST .../setup - není co vypnout, změnit ani jakou kartu vyměnit.
  • 404user_not_foundUživatel, kterému patří API klíč, na účtu chybí - ojedinělý stav, obrať se na podporu.
  • 503stripe_not_configuredPlatby jsou pro tenhle běh volai vypnuté - netýká se konkrétně tvého požadavku.
  • 502stripe_errorStripe odmítl vytvořit platební relaci - zkus to znovu za chvíli.

DELETE/v1/credit/auto-topup/card

Odpojí uloženou kartu OKAMŽITĚ - smaže se CELÁ konfigurace automatického dobíjení (práh i částka), ne jen karta. Další zapnutí jde jen znovu přes POST .../setup, který rovnou strhne první dobití.

Požadavek

bash
curl -X DELETE https://volai.cz/v1/credit/auto-topup/card \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "removed": true
}

GET/v1/billing/documents

Seznam vystavených daňových dokladů k dobitím kreditu, nejnovější první. before je kurzor (Unix ms, exkluzivní) - pošli nextBefore z předchozí stránky. Úložiště samo nikdy nevrátí víc než 100 záznamů na jedno volání, i když limit požaduje víc.

Požadavek

bash
curl "https://volai.cz/v1/billing/documents?limit=2" \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "documents": [
    {
      "id": "inv_4kX9mQ2pRtLz",
      "number": "VF2026000042",
      "issuedAt": 1757280000000,
      "taxableAt": 1757280000000,
      "netHal": 50000,
      "vatHal": 10500,
      "grossHal": 60500,
      "vatRatePercent": 21,
      "reverseCharge": false,
      "kind": "invoice",
      "description": "Prepaid credit for volai telecommunications services",
      "customer": { "name": "Radek Klein", "street": "Na Zámku 636", "city": "Nehvizdy", "zip": "250 81" },
      "paymentRef": "pi_3PQr7s2eZvKYlo2C1a2b3c4d",
      "pdfUrl": "https://volai.cz/v1/billing/documents/inv_4kX9mQ2pRtLz/pdf"
    }
  ],
  "hasMore": false,
  "nextBefore": null
}

Chybové stavy

  • 400validationlimit nebo before je mimo povolený rozsah, nebo požadavek nese neznámý parametr navíc.

GET/v1/billing/documents/{id}

Data jednoho daňového dokladu - částky, DPH, zákazník, platební reference a odkaz na PDF.

Požadavek

bash
curl https://volai.cz/v1/billing/documents/inv_4kX9mQ2pRtLz \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "document": {
    "id": "inv_4kX9mQ2pRtLz",
    "number": "VF2026000042",
    "issuedAt": 1757280000000,
    "taxableAt": 1757280000000,
    "netHal": 50000,
    "vatHal": 10500,
    "grossHal": 60500,
    "vatRatePercent": 21,
    "reverseCharge": false,
    "kind": "invoice",
    "description": "Prepaid credit for volai telecommunications services",
    "customer": { "name": "Radek Klein", "street": "Na Zámku 636", "city": "Nehvizdy", "zip": "250 81" },
    "paymentRef": "pi_3PQr7s2eZvKYlo2C1a2b3c4d",
    "pdfUrl": "https://volai.cz/v1/billing/documents/inv_4kX9mQ2pRtLz/pdf"
  }
}

Chybové stavy

  • 404invoice_not_foundDoklad s tímhle id na účtu není (cizí i neexistující id hlásí stejnou chybu).

GET/v1/billing/documents/{id}/pdf

Stáhne stejný doklad jako application/pdf - sází se čerstvě při každém stažení, neukládá se.

Požadavek

bash
curl https://volai.cz/v1/billing/documents/inv_4kX9mQ2pRtLz/pdf \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -o invoice.pdf

Chybové stavy

  • 404invoice_not_foundDoklad s tímhle id na účtu není (cizí i neexistující id hlásí stejnou chybu).

Úkoly

Úkol vytočí seznam příjemců jedním agentem podle předem nastavených pravidel (rozpočet, volací okno, počet pokusů) - nic se nevytáčí, dokud ho sám nespustíš (POST .../start). Kompletní průvodce s CSV importem, stavy a globální propustností je na /docs/ukoly.

GET/v1/tasks

Seznam úkolů na účtu, bez stránkování (strop 100 úkolů).

Požadavek

bash
curl https://volai.cz/v1/tasks \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "tasks": [
    {
      "id": "task_9f3a2c1d",
      "name": "Payment reminder - September",
      "agentId": "ag_4e91a2f0",
      "taskType": "custom",
      "source": "manual",
      "callingWindow": { "timezone": "Europe/Prague", "start": "09:00", "end": "18:00", "days": [1, 2, 3, 4, 5] },
      "maxAttempts": 2,
      "maxDurationSecs": 120,
      "budgetHal": 50000,
      "reservedBudgetHal": 0,
      "spentHal": 0,
      "status": "draft",
      "revision": 0,
      "createdAt": 1757280000000,
      "updatedAt": 1757280000000,
      "items": [
        { "id": "item_1a2b3c", "phone": "+420777123456", "variables": { "jmeno": "Jana Novakova" }, "paymentStatus": "not_applicable", "status": "pending", "attempts": [] }
      ]
    }
  ]
}

POST/v1/tasks

Založí úkol ve stavu draft - nic se nevytáčí. agentId může být jakýkoli aktivní agent, i na ElevenLabs - požadavek na motor (provider: "engine", volba při založení agenta přes useForOutboundTasks: true) se kontroluje až u POST .../start, kde pak selže s engine_required. Nejvýš 1000 recipients. Bez retryPolicy má maxAttempts rozsah 1-3 a výchozí 1; s volitelným retryPolicy: "lorela_enterprise" rozsah 1-4 a výchozí 4. Volitelné expiresAt (kladné bezpečné celé číslo, Unix ms) brání novým hovorům od konce kampaně, včetně prvního. Politika i konec se vracejí v detailu úkolu. Podporuje Idempotency-Key.

Požadavek

bash
curl -X POST https://volai.cz/v1/tasks \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: task-2026-09-08-01" \
  -d '{
    "name": "Payment reminder - September",
    "agentId": "ag_4e91a2f0",
    "taskType": "custom",
    "source": "manual",
    "maxAttempts": 2,
    "budgetHal": 50000,
    "recipients": [
      { "phone": "+420777123456", "variables": { "jmeno": "Jana Novakova" } }
    ]
  }'

Odpověď

json
{
  "task": {
    "id": "task_9f3a2c1d",
    "name": "Payment reminder - September",
    "agentId": "ag_4e91a2f0",
    "taskType": "custom",
    "source": "manual",
    "callingWindow": { "timezone": "Europe/Prague", "start": "09:00", "end": "18:00", "days": [1, 2, 3, 4, 5] },
    "maxAttempts": 2,
    "maxDurationSecs": 120,
    "budgetHal": 50000,
    "reservedBudgetHal": 0,
    "spentHal": 0,
    "status": "draft",
    "revision": 0,
    "createdAt": 1757280000000,
    "updatedAt": 1757280000000,
    "items": [
      { "id": "item_1a2b3c", "phone": "+420777123456", "variables": { "jmeno": "Jana Novakova" }, "paymentStatus": "not_applicable", "status": "pending", "attempts": [] }
    ]
  }
}

Chybové stavy

  • 400validationTělo nesedí na schéma (chybějící pole, špatný typ, víc než 1000 příjemců nebo úkol větší než 2 MB, neplatné telefonní číslo u některého příjemce...).
  • 404agent_not_foundAgent s tímhle agentId na účtu není, nebo není aktivní.
  • 400budget_too_lowbudgetHal nepokryje ani jeden pokus v maximální délce hovoru (maxDurationSecs).

GET/v1/tasks/{id}

Detail úkolu VČETNĚ issues - dry-run validace toho, co úkolu dnes brání ve startu (počítá se čerstvě, nic se neukládá).

Požadavek

bash
curl https://volai.cz/v1/tasks/task_9f3a2c1d \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "task": {
    "id": "task_9f3a2c1d",
    "name": "Payment reminder - September",
    "agentId": "ag_4e91a2f0",
    "taskType": "custom",
    "source": "manual",
    "callingWindow": { "timezone": "Europe/Prague", "start": "09:00", "end": "18:00", "days": [1, 2, 3, 4, 5] },
    "maxAttempts": 2,
    "maxDurationSecs": 120,
    "budgetHal": 50000,
    "reservedBudgetHal": 0,
    "spentHal": 0,
    "status": "draft",
    "revision": 0,
    "createdAt": 1757280000000,
    "updatedAt": 1757280000000,
    "items": [
      { "id": "item_1a2b3c", "phone": "+420777123456", "variables": { "jmeno": "Jana Novakova" }, "paymentStatus": "not_applicable", "status": "pending", "attempts": [] }
    ],
    "issues": []
  }
}

Chybové stavy

  • 404task_not_foundŽádný úkol s tímhle id na účtu není.

PATCH/v1/tasks/{id}

Upraví pravidla editovatelného úkolu (name, recipients, callingWindow, maxAttempts, retryPolicy, expiresAt, maxDurationSecs, budgetHal) - agentId, taskType a source po založení měnit nejdou. revision je POVINNÁ (optimistická souběžnost). Politiku lze zapnout jen před prvním pokusem; stejnou lze znovu poslat i později. Vynechané maxAttempts, retryPolicy a expiresAt se nemění. maxAttempts: 4 vyžaduje politiku v tomto požadavku nebo už uloženou u úkolu.

Požadavek

bash
curl -X PATCH https://volai.cz/v1/tasks/task_9f3a2c1d \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"revision": 0, "budgetHal": 80000}'

Odpověď

json
{
  "task": {
    "id": "task_9f3a2c1d",
    "name": "Payment reminder - September",
    "agentId": "ag_4e91a2f0",
    "taskType": "custom",
    "source": "manual",
    "callingWindow": { "timezone": "Europe/Prague", "start": "09:00", "end": "18:00", "days": [1, 2, 3, 4, 5] },
    "maxAttempts": 2,
    "maxDurationSecs": 120,
    "budgetHal": 80000,
    "reservedBudgetHal": 0,
    "spentHal": 0,
    "status": "draft",
    "revision": 1,
    "createdAt": 1757280000000,
    "updatedAt": 1757280060000,
    "items": [
      { "id": "item_1a2b3c", "phone": "+420777123456", "variables": { "jmeno": "Jana Novakova" }, "paymentStatus": "not_applicable", "status": "pending", "attempts": [] }
    ]
  }
}

Chybové stavy

  • 400validationTělo nesedí na schéma, nebo neobsahuje revision.
  • 404task_not_foundŽádný úkol s tímhle id na účtu není.
  • 409revision_conflictÚkol se mezitím změnil - načti ho znovu (GET) a zopakuj s aktuální revision.
  • 409task_not_editableÚkol je running/completed, nebo needs_attention bez možnosti obnovy - pravidla teď měnit nejdou.
  • 409items_immutableSeznam příjemců (recipients) nejde nahradit poté, co se aspoň jeden pokus o vytočení odehrál nebo byl kontakt auditovaně přeskočen po nákupu.

POST/v1/tasks/{id}/start

Spustí (nebo po pauze obnoví) vytáčení příjemců - KAŽDÝ zvednutý hovor se účtuje ihned, rozpočet se rezervuje před vytočením. Podporuje Idempotency-Key.

Požadavek

bash
curl -X POST https://volai.cz/v1/tasks/task_9f3a2c1d/start \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: task-start-2026-09-08-01" \
  -d '{"revision": 1}'

Odpověď

json
{
  "task": {
    "id": "task_9f3a2c1d",
    "name": "Payment reminder - September",
    "agentId": "ag_4e91a2f0",
    "taskType": "custom",
    "source": "manual",
    "callingWindow": { "timezone": "Europe/Prague", "start": "09:00", "end": "18:00", "days": [1, 2, 3, 4, 5] },
    "maxAttempts": 2,
    "maxDurationSecs": 120,
    "budgetHal": 80000,
    "reservedBudgetHal": 0,
    "spentHal": 0,
    "status": "running",
    "revision": 2,
    "createdAt": 1757280000000,
    "updatedAt": 1757280120000,
    "items": [
      { "id": "item_1a2b3c", "phone": "+420777123456", "variables": { "jmeno": "Jana Novakova" }, "paymentStatus": "not_applicable", "status": "pending", "attempts": [] }
    ]
  }
}

Chybové stavy

  • 400validationTělo nesedí na schéma, neobsahuje revision, nebo už uplynulo expiresAt.
  • 404task_not_foundŽádný úkol s tímhle id na účtu není.
  • 409revision_conflictÚkol se mezitím změnil - načti ho znovu (GET) a zopakuj s aktuální revision.
  • 409task_not_startableÚkol není ve stavu draft/ready/paused, nebo nemá žádného čekajícího příjemce.
  • 409task_needs_attentionPřed dalším startem je potřeba POST .../reconcile - něco se za běhu úkolu změnilo.
  • 404agent_not_foundAgent s tímhle agentId na účtu není, nebo není aktivní.
  • 403agent_suspendedAgenta úkolu ručně pozastavil provozovatel volai - dokud pozastavení trvá, úkol nejde spustit ani znovu spustit po pauze. Pozastavený celý účet vrací místo toho account_suspended.
  • 409engine_requiredAgent úkolu není na motoru volai - odchozí kampaně dnes vyžadují motor, ne ElevenLabs.
  • 409agent_not_readyKoncept agenta buď není publikovaný, nebo publikovaná verze není otestovaná - publikuj ho (POST /v1/agents/{id}/draft/publish) a pak otestuj publikovanou verzi (POST /v1/agents/{id}/simulate) před startem.
  • 503engine_contract_unavailableMotor momentálně neinzeruje podporu pro proměnné a stropy odchozích úkolů - zkus start znovu později.
  • 409window_closedMimo nastavené volací okno (callingWindow) - počkej na otevření, nebo ho uprav přes PATCH.

POST/v1/tasks/{id}/pause

Zastaví další vytáčení PŘED dalším pokusem; hovor, který se právě volá, pauza nepřerušuje. Bez Idempotency-Key - opakování se stejnou revizí je bez rizika.

Požadavek

bash
curl -X POST https://volai.cz/v1/tasks/task_9f3a2c1d/pause \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"revision": 2}'

Odpověď

json
{
  "task": {
    "id": "task_9f3a2c1d",
    "status": "paused",
    "revision": 3,
    "budgetHal": 80000,
    "reservedBudgetHal": 0,
    "spentHal": 684,
    "name": "Payment reminder - September",
    "agentId": "ag_4e91a2f0",
    "taskType": "custom",
    "source": "manual",
    "callingWindow": { "timezone": "Europe/Prague", "start": "09:00", "end": "18:00", "days": [1, 2, 3, 4, 5] },
    "maxAttempts": 2,
    "maxDurationSecs": 120,
    "createdAt": 1757280000000,
    "updatedAt": 1757280180000,
    "items": [
      { "id": "item_1a2b3c", "phone": "+420777123456", "variables": { "jmeno": "Jana Novakova" }, "paymentStatus": "not_applicable", "status": "completed", "attempts": [{ "id": "attempt_5d4c3b", "startedAt": 1757280100000, "finishedAt": 1757280160000, "providerCallId": "c_7a2b9c1d", "outcome": "completed", "costHal": 684 }], "actualCostHal": 684 }
    ]
  }
}

Chybové stavy

  • 400validationTělo nesedí na schéma, nebo neobsahuje revision.
  • 404task_not_foundŽádný úkol s tímhle id na účtu není.
  • 409task_not_runningPauza jde jen u běžícího (running) úkolu.
  • 409revision_conflictÚkol se mezitím změnil - načti ho znovu (GET) a zopakuj s aktuální revision.

POST/v1/tasks/{id}/items/{itemId}/resolve

Samostatná varianta po ověřeném nákupu přijímá action: skip, reason: customer_purchased, revision, operationId a evidenceRef (oba neprůhledné identifikátory do 128 znaků). Přeskočí jen neaktivní pending položku v draft úkolu nebo pending/failed v paused úkolu bez nevypořádaných hovorů a rezervací. Zachovává historii, ceny i DNC; GET vrací neměnný doklad items[].resolution. Stejnou operaci lze zopakovat po nejisté odpovědi bez nového zápisu; jiný doklad už vyřízené položky je 409 revision_conflict. Původní větev bez reason: Rozhodne o kontaktu čekajícím na kontrolu (items[].status: "review"): běžný retry ho vrátí mezi čekající bez započítání dalšího pokusu, skip ho uzavře jako přeskočený. S retryPolicy: "lorela_enterprise" vrací ruční retry chybu validation; povolené jsou jen automaticky naplánované pokusy. Kontakt ve stavu review se může objevit už u běžícího úkolu, ale resolve jde jen u úkolu, který neběží - u běžícího vrací 409 task_not_editable, takže počkej na needs_attention, nebo úkol pozastav (pause). U reviewReason: "dial:destination_auto_blocked" nejdřív zruš blokaci (POST /v1/dnc/{e164}/unblock) a teprve potom dej retry; u dial:cannot_call_own_number, dial:cannot_call_volai_number, dial:invalid_number, dial:unsupported_country a dial:blocked_destination pomůže jen skip. Výjimkou je reviewReason: "billing_unknown": providerCallId i rezervovaný rozpočet zůstávají připojené do POST .../reconcile; po retry nový pokus čeká na doúčtování původního hovoru a po skip už se znovu nevolá, ale původní hovor může být později naúčtovaný (jeho rezervace se dál počítá do rozpočtu). Po retry proto úkol zůstane needs_attention, dokud se cena neusadí; po skip přejde do paused a jde spustit, pokud zbývá práce, jinak počká v needs_attention a po usazení ceny se dokončí. Hovor, který se vůbec nevytočil, se usadí hned na 0. U ostatních důvodů přejde po vyřízení posledního kontaktu z needs_attention do paused (start ho obnoví) nebo completed.

Požadavek

bash
curl -X POST https://volai.cz/v1/tasks/task_9f3a2c1d/items/item_1a2b3c/resolve \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"action": "retry", "revision": 5}'

Odpověď

json
{
  "task": {
    "id": "task_9f3a2c1d",
    "name": "Payment reminder - September",
    "agentId": "ag_4e91a2f0",
    "taskType": "custom",
    "source": "manual",
    "callingWindow": { "timezone": "Europe/Prague", "start": "09:00", "end": "18:00", "days": [1, 2, 3, 4, 5] },
    "maxAttempts": 2,
    "maxDurationSecs": 120,
    "budgetHal": 80000,
    "reservedBudgetHal": 2400,
    "spentHal": 0,
    "status": "needs_attention",
    "revision": 6,
    "createdAt": 1757280000000,
    "updatedAt": 1757280240000,
    "reviewReason": "billing_unknown",
    "items": [
      {
        "id": "item_1a2b3c",
        "phone": "+420777123456",
        "variables": { "jmeno": "Jana Novakova" },
        "paymentStatus": "not_applicable",
        "status": "pending",
        "attempts": [
          { "id": "attempt_7c2d", "startedAt": 1757280180000, "finishedAt": 1757280230000, "providerCallId": "c_late_price", "outcome": "completed" }
        ],
        "reviewReason": "billing_unknown",
        "providerCallId": "c_late_price",
        "estimatedCostHal": 2400,
        "reservedCostHal": 2400
      }
    ]
  }
}

Chybové stavy

  • 400validationTělo nesedí na schéma (action je retry nebo skip, revision nezáporné celé číslo), nebo itemId na úkolu není, nebo ruční retry odmítá politika lorela_enterprise.
  • 404task_not_foundŽádný úkol s tímhle id na účtu není.
  • 409task_not_editableÚkol není v povoleném stavu. Pro přeskočení po nákupu použij draft nebo paused; běžící úkol nejdřív pozastav a doúčtuj aktivní hovory.
  • 409item_not_resolvableKontakt nelze vyřídit touto variantou. Běžná větev vyžaduje review; customer_purchased vyžaduje neaktivní pending v draftu nebo pending/failed v paused úkolu bez nevypořádaných hovorů.
  • 409revision_conflictÚkol se mezitím změnil - načti ho znovu (GET) a zopakuj s aktuální revision.

POST/v1/tasks/{id}/reconcile

Sladí stav úkolu se skutečnými hovory po přerušení - bezpečné volat kdykoli, i opakovaně. revision je NA ROZDÍL od start/pause NEPOVINNÁ.

Požadavek

bash
curl -X POST https://volai.cz/v1/tasks/task_9f3a2c1d/reconcile \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{}'

Odpověď

json
{
  "task": {
    "id": "task_9f3a2c1d",
    "status": "completed",
    "revision": 3,
    "budgetHal": 80000,
    "reservedBudgetHal": 0,
    "spentHal": 684,
    "name": "Payment reminder - September",
    "agentId": "ag_4e91a2f0",
    "taskType": "custom",
    "source": "manual",
    "callingWindow": { "timezone": "Europe/Prague", "start": "09:00", "end": "18:00", "days": [1, 2, 3, 4, 5] },
    "maxAttempts": 2,
    "maxDurationSecs": 120,
    "createdAt": 1757280000000,
    "updatedAt": 1757280240000,
    "items": [
      { "id": "item_1a2b3c", "phone": "+420777123456", "variables": { "jmeno": "Jana Novakova" }, "paymentStatus": "not_applicable", "status": "completed", "attempts": [{ "id": "attempt_5d4c3b", "startedAt": 1757280100000, "finishedAt": 1757280160000, "providerCallId": "c_7a2b9c1d", "outcome": "completed", "costHal": 684 }], "actualCostHal": 684 }
    ]
  }
}

Chybové stavy

  • 400validationTělo nesedí na schéma (revision, je-li uvedená, musí být nezáporné celé číslo).
  • 404task_not_foundŽádný úkol s tímhle id na účtu není.
  • 409revision_conflictÚkol se mezitím změnil - načti ho znovu (GET) a zopakuj s aktuální revision.

POST/v1/tasks/{id}/process

Sladí stav skutečných hovorů a zkusí zpracovat nejvýše jednoho splatného příjemce již běžícího úkolu. Vyžaduje aktuální revision; úkol nikdy sám nespustí ani neobnoví. Používá stejné volací okno, blokace, rezervaci rozpočtu, kredit, kapacitu a atomický claim jako cron. Volitelná uložená volba dispatchOrder: "first_attempts_first" dá přednost splatným kontaktům bez skutečného pokusu; odmítnutí před vytočením (countsAsAttempt: false) jejich přednost nezruší. Výchozí pořadí zůstává nezměněné a budoucí nextAttemptAt se nepřeskakuje. Po nejasném výsledku nejdřív načtěte stav, neopakujte slepě požadavek. Nový logický tik potřebuje nový Idempotency-Key, i když se revize nezměnila.

Požadavek

bash
curl -X POST https://volai.cz/v1/tasks/task_9f3a2c1d/process \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: task-process-2026-10-08-tick-001" \
  -d '{"revision": 2}'

Odpověď

json
{
  "status": "idle",
  "task": {
    "id": "task_9f3a2c1d",
    "name": "Payment reminder - September",
    "agentId": "ag_4e91a2f0",
    "taskType": "custom",
    "source": "manual",
    "callingWindow": { "timezone": "Europe/Prague", "start": "09:00", "end": "18:00", "days": [1, 2, 3, 4, 5] },
    "maxAttempts": 2,
    "maxDurationSecs": 120,
    "budgetHal": 80000,
    "reservedBudgetHal": 0,
    "spentHal": 0,
    "status": "running",
    "revision": 2,
    "createdAt": 1757280000000,
    "updatedAt": 1757280120000,
    "items": [
      { "id": "item_1a2b3c", "phone": "+420777123456", "variables": { "jmeno": "Jana Novakova" }, "paymentStatus": "not_applicable", "status": "pending", "attempts": [], "nextAttemptAt": 1757280180000 }
    ]
  }
}

Chybové stavy

  • 400validationTělo nesedí na schéma, nebo neobsahuje revision.
  • 404task_not_foundŽádný úkol s tímhle id na účtu není.
  • 409revision_conflictÚkol se mezitím změnil - načti ho znovu (GET) a zopakuj s aktuální revision.
  • 409task_not_runningÚkol není běžící nebo byl před vytočením pozastaven. Načtěte aktuální stav; process úkol sám neobnoví.
  • 409task_pausedÚkol není běžící nebo byl před vytočením pozastaven. Načtěte aktuální stav; process úkol sám neobnoví.
  • 409claim_lostVýsledek dispatch nelze bezpečně potvrdit. Načtěte nebo rekonciliujte úkol a zkontrolujte hovor; požadavek slepě neopakujte.
  • 500dispatch_result_unpersistedVýsledek dispatch nelze bezpečně potvrdit. Načtěte nebo rekonciliujte úkol a zkontrolujte hovor; požadavek slepě neopakujte.
  • 409agent_version_changedPublikovaná verze agenta se změnila. Zkontrolujte ji a před dalším spuštěním úkolu ji znovu otestujte.
  • 403agent_suspendedAgenta úkolu ručně pozastavil provozovatel volai - dokud pozastavení trvá, úkol nejde spustit ani znovu spustit po pauze. Pozastavený celý účet vrací místo toho account_suspended.
  • 503engine_contract_unavailableMotor momentálně neinzeruje podporu pro proměnné a stropy odchozích úkolů - zkus start znovu později.
  • 409window_closedMimo nastavené volací okno (callingWindow) - počkej na otevření, nebo ho uprav přes PATCH.

DELETE/v1/tasks/{id}

Smaže úkol i všechny jeho položky - jen ve stavu draft/ready/paused/completed/needs_attention BEZ položky in_progress. revision čte ?revision= NEBO JSON tělo {"revision": ...} (query má přednost).

Požadavek

bash
curl -X DELETE "https://volai.cz/v1/tasks/task_9f3a2c1d?revision=3" \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "deleted": true
}

Chybové stavy

  • 400validationrevision chybí, nebo není nezáporné celé číslo (ani v query, ani v těle).
  • 404task_not_foundŽádný úkol s tímhle id na účtu není.
  • 409revision_conflictÚkol se mezitím změnil - načti ho znovu (GET) a zopakuj s aktuální revision.
  • 409task_not_deletableÚkol má položku in_progress, nebo je ve stavu running - počkej, až doběhne, nebo ho nejdřív zastav (pause).

POST/v1/tasks/csv-preview

Naparsuje a ověří CSV příjemců PŘED založením nebo úpravou úkolu - nic se neukládá, chyby na jednotlivých řádcích jdou do pole errors ve 200 odpovědi, ne do HTTP chyby.

Požadavek

bash
curl -X POST https://volai.cz/v1/tasks/csv-preview \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"csv": "phone,jmeno\n+420777123456,Jana Novakova\nneplatne,Petr Svoboda\n"}'

Odpověď

json
{
  "headers": ["phone", "jmeno"],
  "delimiter": ",",
  "rows": [
    { "line": 2, "phone": "+420777123456", "variables": { "jmeno": "Jana Novakova" }, "errors": [] },
    { "line": 3, "variables": { "jmeno": "Petr Svoboda" }, "errors": [{ "field": "phone", "message": "Invalid phone number", "row": 3 }] }
  ],
  "validRows": [
    { "phone": "+420777123456", "variables": { "jmeno": "Jana Novakova" } }
  ],
  "errors": [{ "field": "phone", "message": "Invalid phone number", "row": 3 }]
}

Chybové stavy

  • 400validationTělo neobsahuje csv jako řetězec, nebo csv přesahuje 1 000 000 znaků.

Čísla

GET/v1/numbers/{e164}/callback-routing

Přečte opt-in politiku zpětného volání vlastního čísla. Výchozí směrování čísla zůstává stejné.

Požadavek

bash
curl "https://volai.cz/v1/numbers/+420601234567/callback-routing" \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{"callbackRouting":{"agentIds":["ag_campaign"],"lookbackSecs":604800}}

Chybové stavy

  • 400validatione164 v URL není platné telefonní číslo.
  • 404not_foundČíslo neexistuje nebo nepatří tvému účtu.

PUT/v1/numbers/{e164}/callback-routing

Uloží výslovný seznam vlastních aktivních agentů motoru pro zákazníky s jednoznačným skutečným odchozím hovorem z tohoto čísla během lookbackSecs (60-604800 sekund). Neznámý, nejednoznačný nebo neověřený volající zůstane u výchozího agenta. Prázdné agentIds politiku vypne. Volitelná firstMessage platí jen pro ověřený callback. Volitelná missedFirstMessage (do 500 znaků) nahradí firstMessage u zákazníka, jehož předchozí odchozí hovor neměl rozhovor (nezvednuto, záznamník, hlasové menu, nikdo nepromluvil) - takový zákazník nabídku ještě neslyšel; bez ní platí firstMessage pro všechny. Motor dostane u ověřeného callbacku i volai_callback_kind (missed nebo contacted). engineSyncRequired: true znamená, že je nutné samostatně aktivovat webhooks.inbound_route_url v konfiguraci motoru výchozího agenta a ověřit ji čtením; tento požadavek živou konfiguraci motoru nepřepisuje. Detail ověřeného příchozího hovoru obsahuje callbackOf.callId a případně serverové taskId a taskItemId.

Požadavek

bash
curl -X PUT "https://volai.cz/v1/numbers/+420601234567/callback-routing" \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"agentIds":["ag_campaign"],"lookbackSecs":604800}'

Odpověď

json
{"callbackRouting":{"agentIds":["ag_campaign"],"lookbackSecs":604800},"engineSyncRequired":true}

Chybové stavy

  • 400validatione164 v URL není platné telefonní číslo.
  • 404not_foundČíslo neexistuje nebo nepatří tvému účtu.

GET/v1/numbers

Seznam telefonních čísel na účtu, u každého aktuální směrování.

Požadavek

bash
curl https://volai.cz/v1/numbers \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "numbers": [
    {
      "e164": "+420601234567",
      "routing": { "mode": "agent", "agentId": "ag_kx91fa2b", "hasSipPassword": false, "sipTarget": "engine" },
      "monthlyFeeHal": 2500,
      "boughtAt": 1756111640000,
      "region": "objednavka",
      "regionLabel": "Czech number",
      "billingMonths": 1
    }
  ]
}

GET/v1/numbers/{e164}

Detail jednoho vlastního čísla - stejný tvar jako položka výpisu GET /v1/numbers. Hodí se, když znáš jen E.164 (typicky agent přes MCP) a nechceš stránkovat celý výpis.

Požadavek

bash
curl "https://volai.cz/v1/numbers/+420601234567" \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "number": {
    "e164": "+420601234567",
    "routing": { "mode": "agent", "agentId": "ag_kx91fa2b", "hasSipPassword": false, "sipTarget": "engine" },
    "monthlyFeeHal": 2500,
    "boughtAt": 1756111640000,
    "region": "objednavka",
    "regionLabel": "Czech number",
    "billingMonths": 1
  }
}

Chybové stavy

  • 400validatione164 v URL není platné telefonní číslo.
  • 404not_foundČíslo neexistuje nebo nepatří tvému účtu.

POST/v1/numbers

Koupí číslo. Prázdné tělo {} přiřadí kterékoli volné z aktuální nabídky, nebo pošli konkrétní { "e164": "..." } z GET /v1/numbers/available. Předvolbu vybereš přes { "region": "brno" } - hodnoty praha, brno, internet, bratislava; číslo z jiného kraje se objednává přes POST /v1/numbers/orders. Strhne se měsíční poplatek 25,00 Kč (u slovenského čísla, nabídka bratislava, rovnou 75,00 Kč - platí se billingMonths měsíců dopředu a stejná částka se strhává znovu po každých billingMonths měsících), směrování začíná na none - nastav ho hned dál přes PATCH.

Požadavek

bash
curl -X POST https://volai.cz/v1/numbers \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{}'

Odpověď

json
{
  "number": {
    "e164": "+420266266647",
    "routing": { "mode": "none", "hasSipPassword": false },
    "monthlyFeeHal": 2500,
    "boughtAt": 1756111640000,
    "region": "praha",
    "regionLabel": "Prague",
    "billingMonths": 1
  }
}

Chybové stavy

  • 400validationTělo požadavku nesedí na schéma (e164/region přes 32 znaků nebo špatný typ), nebo region není žádná ze zavedených nabídek.
  • 402insufficient_creditKredit nepokryje měsíční poplatek 25,00 Kč. U slovenského čísla (nabídka bratislava) musí kredit pokrýt rovnou monthlyFeeHal * billingMonths. Účet, který ještě nikdy nedobil kredit, smí z uvítacího kreditu držet jedno číslo (počítá se i rozpracovaná objednávka); další číslo jde koupit až po prvním dobití. Nic neúčtujeme.
  • 404number_unavailableTohle konkrétní číslo mezitím koupil někdo jiný - vyber si prosím jiné z aktuální nabídky (GET /v1/numbers/available).
  • 503pool_emptyČísla z aktuální nabídky nám došla - objednej z jiného kraje (POST /v1/numbers/orders), nebo se v portálu přihlas do čekačky.

GET/v1/numbers/waitlist

Stav čekačky na každou nabídku ze zásoby (praha/brno/internet/bratislava) - joined říká, jestli na ní účet čeká, available jestli je zrovna skladem.

Požadavek

bash
curl https://volai.cz/v1/numbers/waitlist \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "offers": [
    { "offerId": "praha", "label": "Prague", "joined": false, "available": true },
    { "offerId": "brno", "label": "Brno", "joined": true, "available": false },
    { "offerId": "internet", "label": "Internet number", "joined": false, "available": true },
    { "offerId": "bratislava", "label": "Bratislava", "joined": false, "available": true }
  ]
}

Chybové stavy

  • 404user_not_foundUživatel, kterému patří API klíč, na účtu chybí - ojedinělý stav, obrať se na podporu.

POST/v1/numbers/waitlist

Přihlásí se do čekačky na vyprodanou nabídku (POST /v1/numbers selhal kódem pool_empty) - nekupuje číslo, jen zaregistruje zájem. Až se nabídka doplní, dostaneš e-mail. Prázdné tělo {} bere výchozí nabídku (praha).

Požadavek

bash
curl -X POST https://volai.cz/v1/numbers/waitlist \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"offerId": "brno"}'

Odpověď

json
{
  "joined": true
}

Chybové stavy

  • 400validationofferId není žádná z hodnot v nabídce (praha/brno/internet/bratislava).
  • 404user_not_foundUživatel, kterému patří API klíč, na účtu chybí - ojedinělý stav, obrať se na podporu.

DELETE/v1/numbers/waitlist/{offerId}

Odhlásí se z čekačky JEDNÉ nabídky (offerId v cestě) - čekačku na jiné nabídky nemění.

Požadavek

bash
curl -X DELETE https://volai.cz/v1/numbers/waitlist/brno \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "left": true
}

Chybové stavy

  • 400validationofferId v cestě není žádná z hodnot v nabídce.
  • 404user_not_foundUživatel, kterému patří API klíč, na účtu chybí - ojedinělý stav, obrať se na podporu.

GET/v1/numbers/available

Nabídka volných čísel k zakoupení (max 5 na region). numbers drží čísla ze zvoleného regionu (bez parametru pražská), regions vždycky celou nabídku najednou - včetně bratislava (slovenské číslo s odlišným poplatkem, viz POST /v1/numbers výš).

Požadavek

bash
curl "https://volai.cz/v1/numbers/available?region=brno" \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "region": "brno",
  "numbers": ["+420510510129", "+420510510132"],
  "regions": [
    {
      "id": "praha",
      "label": "Prague",
      "description": "A landline with a Prague area code.",
      "numbers": ["+420266266643", "+420266266645"]
    },
    {
      "id": "brno",
      "label": "Brno",
      "description": "A landline with a Brno area code.",
      "numbers": ["+420510510129", "+420510510132"]
    },
    {
      "id": "internet",
      "label": "Internet number",
      "description": "Area code 910, not tied to any region - works from anywhere.",
      "numbers": ["+420910084099"]
    },
    {
      "id": "bratislava",
      "label": "Bratislava",
      "description": "A Slovak number with the Bratislava area code +421 2. Billed three months upfront.",
      "numbers": ["+421222205798"]
    }
  ]
}

Chybové stavy

  • 400validationregion musí být jedno z id v nabídce (viz /cenik).

GET/v1/numbers/address-options

Adresy pro objednávku čísla z kraje, který není v nabídce. Kaskáda číselníku operátora: pošli psc a dostaneš obce, přidej obec a dostaneš části obce, cobce vrátí ulice a ulice čísla popisná. Jakmile je vybrané cp, přijde v poli recap čitelná adresa. Kódy si nevymýšlej - platí jen ty odsud. Rychlejší cesta bez kaskády je query, viz callout níž.

Požadavek

bash
curl "https://volai.cz/v1/numbers/address-options?psc=70200&obec=554821&cobce=413950" \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "obce": [{ "value": "554821", "label": "Ostrava" }],
  "casti": [{ "value": "413950", "label": "Moravská Ostrava" }],
  "ulice": [{ "value": "353710", "label": "28. října" }],
  "cp": [],
  "recap": null
}

Chybové stavy

  • 400validationquery je delší než 200 znaků, neobsahuje PSČ, nebo operátor pro rozpoznanou úroveň nenabízí žádnou odpovídající volbu.
  • 502provisioning_failedAdresy se od operátora nepodařilo načíst vůbec.
  • 503orders_disabledObjednávky jsou dočasně pozastavené.

Rychlejší cesta: celá adresa jedním dotazem (query)

Místo kaskády pošli query s celou adresou (např. "Nádražní 100, 702 00 Ostrava") - server ji za tebe odjede celou. Z textu se čte napřímo jen PSČ, zbytek se pouze porovnává s nabídkami, které pro danou úroveň vrátil operátor - kódy si server nikdy nevymýšlí.

bash
curl -G https://volai.cz/v1/numbers/address-options \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  --data-urlencode "query=Nádražní 100, 702 00 Ostrava"
json
{
  "obce": [],
  "casti": [],
  "ulice": [],
  "cp": [],
  "recap": "28. října 102/1, Ostrava, 70200",
  "selection": {
    "psc": "70200",
    "obec": "554821",
    "cobce": "413950",
    "ulice": "353710",
    "cp": "3180026"
  }
}

Když je adresa nejednoznačná, vrátí se jen ta úroveň, kde se to pozná (ambiguousLevel: obec/cobce/ulice/cp) s nabídkou k výběru v příslušném poli, například:

json
{
  "obce": [
    { "value": "554821", "label": "Ostrava" },
    { "value": "554813", "label": "Fulnek" }
  ],
  "casti": [],
  "ulice": [],
  "cp": [],
  "recap": null,
  "selection": { "psc": "70200", "obec": "", "cobce": "", "ulice": "", "cp": "" },
  "ambiguousLevel": "obec",
  "message": "The given address is ambiguous - choose the municipality (obec) from the options. Repeat the query WITHOUT query and with the step parameters: psc=70200, obec=<value from the options>."
}

selection vždy nese, co se rozpoznalo předtím - pokračuje se stejným krokovým parametrem (psc/obec/cobce/ulice/cp), jaký by se použil bez query. Nejde-li adresu rozpoznat vůbec, nebo dojde-li časový rozpočet uprostřed zjišťování, přijde místo recap srozumitelná zpráva v poli message a poctivě rozpracovaný selection - odtud pokračuj krokovými parametry dál.

query a krokové parametry se NEKOMBINUJÍ v jednom požadavku - pošleš-li obojí najednou, endpoint vrátí validation. Po nejednoznačné odpovědi tedy nezopakuj stejné query s přidaným obec (nebo jinou úrovní) z nabídky - další dotaz pošli BEZ query, jen krokovými parametry. Přesný tvar (i s hodnotami, které se už rozpoznaly) je vypsaný přímo v poli message té nejednoznačné odpovědi.

POST/v1/numbers/orders

Objedná číslo z kraje mimo nabídku (Ostrava, Plzeň, Budějovice...). Kódy adresy musí přijít z GET /v1/numbers/address-options. Endpoint počká, než u operátora založíme tísňovou adresu provozovny a koupíme číslo - obvykle do minuty; hotové číslo pak vrátí v poli e164 se stavem done. Když se to napoprvé nepovede, vrátí objednávku ve stavu pending a dokončí ji cron. Poplatek 25,00 Kč se strhne až s hotovým číslem.

Požadavek

bash
curl -X POST https://volai.cz/v1/numbers/orders \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"psc":"70200","obec":"554821","cobce":"413950","ulice":"353710","cp":"3180026"}'

Odpověď

json
{
  "order": {
    "id": "Xa3Kd9pQmR2v",
    "status": "pending",
    "address": "70200",
    "createdAt": 1756111640000,
    "updatedAt": 1756111640000
  }
}

Chybové stavy

  • 400validationTělo požadavku nesedí na schéma - psc/obec/cobce/cp chybí nebo je prázdné, nebo pole přesahuje svůj limit délky (ulice je nepovinná).
  • 400incomplete_addressChybí psc, obec, cobce nebo cp (ulice je nepovinná - ne každá adresa ji má).
  • 402insufficient_creditKredit nepokryje měsíční poplatek 25,00 Kč. Účet, který ještě nikdy nedobil kredit, smí z uvítacího kreditu držet jedno číslo (počítá se i rozpracovaná objednávka); další jde objednat až po prvním dobití. Nic neúčtujeme.
  • 503orders_disabledObjednávky jsou dočasně pozastavené.
  • 409idempotency_conflictStejný Idempotency-Key už byl použit pro jinou adresu objednávky.

GET/v1/numbers/orders

Stav objednávek: pending (ve frontě), provisioning (právě pořizujeme), done (číslo je v poli e164 a patří účtu), failed (důvod v poli error, nic se neúčtovalo), cancelled (zrušená zákazníkem, dokud čekala ve frontě - nic se neúčtovalo).

Požadavek

bash
curl https://volai.cz/v1/numbers/orders \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "orders": [
    {
      "id": "Xa3Kd9pQmR2v",
      "status": "done",
      "address": "28. října 102/1, Ostrava, 70200",
      "e164": "+420596123456",
      "createdAt": 1756111640000,
      "updatedAt": 1756118840000
    }
  ]
}

POST/v1/numbers/orders/{id}/cancel

Zruší objednávku, která ještě čeká ve frontě (pending) - bez těla, vrátí ji se stavem cancelled. Nic se neúčtuje (poplatek jde až s hotovým číslem) a účet bez prvního dobití tak může objednat znovu. Objednávku, kterou právě pořizujeme (provisioning), zrušit nejde - vrátí 409 in_progress, zkus to za pár minut; hotovou (done) ani neúspěšnou (failed) také ne (409 invalid_transition) - hotové číslo uvolníš přes DELETE /v1/numbers/{e164}. Opakované zrušení už zrušené objednávky je neškodné (vrátí ji beze změny).

Požadavek

bash
curl -X POST https://volai.cz/v1/numbers/orders/Xa3Kd9pQmR2v/cancel \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "order": {
    "id": "Xa3Kd9pQmR2v",
    "status": "cancelled",
    "address": "28. října 102/1, Ostrava, 70200",
    "createdAt": 1756111640000,
    "updatedAt": 1756112240000
  }
}

Chybové stavy

  • 404not_foundObjednávka neexistuje nebo nepatří tvému účtu.
  • 409in_progressObjednávku právě pořizujeme (provisioning, nebo si ji zrovna bere cron) - zrušit půjde až hotové číslo uvolnit. Zkus to za pár minut.
  • 409invalid_transitionObjednávka je hotová (done - číslo uvolni přes DELETE /v1/numbers/{e164}), nebo už skončila neúspěchem (failed - není co rušit).

DELETE/v1/numbers/{e164}

Uvolní číslo zpátky do zásoby - odpojí směrování i registraci u agenta. Zaplacený měsíc se nevrací.

Požadavek

bash
curl -X DELETE "https://volai.cz/v1/numbers/+420601234567" \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "released": true
}

Chybové stavy

  • 400validatione164 v URL není platné telefonní číslo.
  • 404not_foundČíslo neexistuje nebo nepatří tvému účtu.
  • 502release_failedOdpojení směrování v síti se nepodařilo bezpečně potvrdit, číslo se do zásoby nevrací. Zkus to prosím znovu za chvíli, nebo napiš podpoře.

PATCH/v1/numbers/{e164}

Změní směrování čísla.

  • mode: "agent" + agentId - hovory bere hlasový agent.
  • mode: "forward" + forwardTo (E.164) - přesměrování, platíš obě nohy.
  • mode: "sip" + sipUri - směruje na tvůj SIP server (viz SIP) - nepovinně i s přihlášením přes sipUsername a sipPassword, když to cílová ústředna vyžaduje. Vynechané pole (v těle PATCH úplně chybí) se ponechá tak, jak je uložené - u obou polí stejně, takže třeba změna samotného sipUri nevyžaduje znovu poslat uložené jméno ani heslo. Výjimka: když v sipUri měníš i server (část za zavináčem), musíš heslo poslat znovu - uložené heslo nepošleme na jiný server, než pro který jsi ho zadal. Zrušit autentizaci jde jen výslovně - pošli sipUsername i sipPassword jako prázdné řetězce. Odpověď navíc vždy nese hasSipPassword (nepovinné, jen pro čtení) - true znamená, že je k číslu uložené SIP heslo; samotné sipPassword se přes API nikdy nevrací.
  • mode: "none" - hovory na číslo se nikam nesměrují a síť je odmítne.

sipTarget v routing je nepovinné informativní pole, které nastavuje volai - klient ho nikdy neposílá. Hodnota "engine" znamená, že číslo obsluhuje agent na vlastním motoru volai. Jakákoli jiná hodnota nebo chybějící pole znamená, že na motoru NENÍ (typicky agent na ElevenLabs) - chybějící pole samo o sobě neber jako jistý důkaz ElevenLabs, jediný spolehlivý signál je hodnota "engine".

Požadavek

bash
curl -X PATCH "https://volai.cz/v1/numbers/+420601234567" \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"routing": {"mode": "agent", "agentId": "ag_kx91fa2b"}}'

Odpověď

json
{
  "number": {
    "e164": "+420601234567",
    "routing": { "mode": "agent", "agentId": "ag_kx91fa2b", "hasSipPassword": false, "sipTarget": "engine" },
    "monthlyFeeHal": 2500,
    "boughtAt": 1756111640000,
    "region": "objednavka",
    "regionLabel": "Czech number",
    "billingMonths": 1
  }
}

Chybové stavy

  • 400validationrouting.mode vyžaduje odpovídající pole (agentId / forwardTo / sipUri). U routing.mode: "sip" navíc platí pro sipUsername a sipPassword: vyplň buď obě, nebo žádné, bez dvojtečky, zavináče, mezery, uvozovky a zpětného lomítka a nejvýš 64 znaků každé. Když jsou vyplněné oba přihlašovací údaje, navíc nesmí volané číslo v sipUri (část před zavináčem) obsahovat dvojtečku a server v sipUri (za zavináčem) nesmí mít port - s přihlašovacími údaji se to zatím nepodporuje. Stejný kód dostaneš i tehdy, když k číslu s uloženým heslem změníš server v sipUri a nepošleš sipPassword znovu.
  • 400invalid_sip_urisipUri nemá tvar sip:uzivatel@server (volitelně s portem) - platí jen pro routing.mode: "sip". Stejný kód dostaneš i tehdy, když operátor zápis OVĚŘENÉHO směrování (s vyplněným sipUsername i sipPassword) definitivně odmítne, nebo po zápisu vrátí jiný cíl, než jsme poslali - opakování beze změny vstupu nepomůže. U směrování bez přihlašovacích údajů se stejná situace hlásí jako routing_failed.
  • 402insufficient_creditÚčet ještě nikdy nedobil kredit a číslo zatím není přesměrované - přesměrování hovorů (routing.mode: "forward") jde zapnout až po prvním dobití, protože se účtuje zpětně z výpisu operátora a bez brzdy během hovoru. Agent, SIP i příchozí hovory fungují dál. Číslo, které už přesměrované je, jde přesměrovat na jiný cíl i bez dobití. Nic neúčtujeme.
  • 404not_foundČíslo neexistuje nebo nepatří tvému účtu.
  • 502routing_failedPožadované směrování se teď nepotvrdilo (přesměrování, SIP, odpojení, nebo potvrzení směrování na motor) - dočasný výpadek v síti, zkus to znovu za chvíli.
  • 502engine_unavailableU routing.mode: "agent" s agentem na vlastním motoru volai: motor teď neodpovídá, přiřazení čísla se nepovedlo. Zkus to prosím znovu za chvíli, nebo napiš podpoře.
  • 502engine_readback_mismatchU routing.mode: "agent" s agentem na vlastním motoru volai: motor odpověděl, ale při zpětném čtení se ukázalo jiné namapování, než jsme poslali. Zkus to znovu.

GET/v1/numbers/{e164}/sip

SIP údaje čísla - pro vlastní softphone/PBX i pro odchozí trunk hlasové platformy (ElevenLabs, Vapi...). Nastavení krok za krokem je na stránce SIP.

PoleVýznam
serverDoména pro REGISTER vlastního softphonu nebo PBX. Není pro odchozí trunk hlasové platformy - na to slouží outboundTrunkAddress.
outboundTrunkAddressAdresa odchozího SIP trunku tvé platformy (u ElevenLabs pole outbound_trunk_config.address). Zadej přesně tuhle hodnotu bez ohledu na to, jestli se shoduje se server - jiná adresa znamená, že se hovor v síti nesměruje a platforma ohlásí timeout.
username / passwordPřihlašovací údaje k lince - stejné pro REGISTER i pro trunk s digest autentizací.
port / transportPort a doporučený transport pro SIP signalizaci - TCP (velký INVITE s SDP se do UDP MTU nemusí vejít a platforma ho pak vůbec neodešle), UDP funguje také.
inboundSignallingCidrsOdkud přichází naše SIP signalizace k tvé ústředně nebo platformě - allowlist příchozího trunku (např. ElevenLabs allowed_addresses, Vapi gateways).

server a outboundTrunkAddress nejsou zaměnitelné: server je určený pro REGISTER softphonu nebo ústředny, outboundTrunkAddress pro odchozí trunk platformy - do trunku patří vždycky hodnota z outboundTrunkAddress, ať už se se server shoduje, nebo ne; jiná adresa znamená, že se hovor v síti nesměruje a platforma ohlásí timeout (podrobně na stránce SIP a v návodu na vlastního agenta).

Požadavek

bash
curl "https://volai.cz/v1/numbers/+420601234567/sip" \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "server": "sip.volai.cz",
  "username": "123456",
  "password": "a1b2c3d4e5f6",
  "outboundTrunkAddress": "sip.volai.cz",
  "port": 5060,
  "transport": "tcp",
  "inboundSignallingCidrs": ["81.31.45.0/24"]
}

Chybové stavy

  • 400validatione164 v URL není platné telefonní číslo.
  • 404not_foundČíslo neexistuje nebo nepatří tvému účtu.
  • 502sip_credentials_unavailableSIP heslo linky se nepodařilo dohledat. Zkus to za chvíli znovu, případně kontaktuj podporu.

GET/v1/numbers/{e164}/sip/status

Skutečný stav SIP registrace čísla ve směrování Vlastní SIP ústředna a poslední příchozí hovor na něj - na rozdíl od pole routing.mode jde o PRAVDIVĚ ověřený stav, ne odvozený z nastavení. registration je null, když číslo míří na CIZÍ ústřednu (sipa: cíl bez výzvy) - u ní se hodí jen lastInbound.

Požadavek

bash
curl "https://volai.cz/v1/numbers/+420601234567/sip/status" \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "target": { "kind": "own_line", "host": null },
  "registration": { "registered": true },
  "lastInbound": {
    "at": 1756118900000,
    "status": "completed",
    "from": "+420777123456",
    "recent": true
  }
}

Chybové stavy

  • 400validatione164 v URL není platné telefonní číslo.
  • 404not_foundČíslo neexistuje nebo nepatří tvému účtu.
  • 400not_sip_modeČíslo není ve směrování „Vlastní SIP ústředna“.

Zprávy

SMS přijímá jen doplněk SMS číslo

Hlasová čísla volai (pevná a VoIP) SMS nepřijímají. Příjem SMS a odesílání z vlastního čísla umí doplněk SMS číslo (sekce SMS čísla níž): české mobilní číslo jen pro SMS, bez hovorů. Přijaté zprávy přečteš v GET /v1/messages?direction=in, v MCP nástroji list_messages nebo ve webhooku message.received a uchováváme je 90 dní. Nad 300 přijatých zpráv za hodinu na jedno SMS číslo se webhook message.received pro další zprávy neposílá; zprávy se dál ukládají. Ověřovací kódy od služeb (banky, Google a podobně) nezaručujeme - číslo je určené pro psaní s lidmi. Zprávy odeslané bez fromNumber odcházejí pod sdíleným jménem SMSinfo, na které příjemce neodpoví a které nemá doručenky.

GET/v1/messages

Historie SMS, od nejnovější. Bez parametru direction jen odeslané zprávy, s direction=in jen zprávy přijaté na tvém SMS čísle a s direction=all obojí dohromady podle času vzniku. Přijatá zpráva má direction: "in", status: "received" a source: "inbound"; from je odesílatel (telefonní číslo, nebo textové jméno) a to je tvoje SMS číslo. Výpis přijatých zpráv drží nejvýš 1 000 nejnovějších zpráv na účet, takže starší z něj zmizí i dřív než za 90 dní.

Požadavek

bash
curl "https://volai.cz/v1/messages?limit=20&direction=all" \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "messages": [
    {
      "id": "msg_7c1f9a2e",
      "to": "+420777123456",
      "from": "volai",
      "body": "Ahoj z volai! - Moje Appka",
      "status": "sent",
      "failReason": null,
      "priceHal": 136,
      "segments": 1,
      "createdAt": 1756111500000,
      "source": "api",
      "direction": "out",
      "smsNumber": null,
      "deliveryStatus": null
    },
    {
      "id": "msg_3d9b5e71",
      "to": "+420770112233",
      "from": "+420777123456",
      "body": "Díky, přijdu v pátek v 10:00.",
      "status": "received",
      "failReason": null,
      "priceHal": 0,
      "segments": 1,
      "createdAt": 1756111900000,
      "source": "inbound",
      "direction": "in",
      "smsNumber": "+420770112233",
      "deliveryStatus": null
    }
  ],
  "hasMore": false,
  "nextBefore": null
}

Chybové stavy

  • 400validationlimit musí být kladné celé číslo; before musí být kladné celé číslo (časové razítko v ms); direction musí být out, in nebo all.

POST/v1/messages

Odešle SMS. Jen česká a slovenská čísla (+420 / +421). Cena 1,36 Kč za segment - delší (nebo znaky mimo GSM-7 abecedu, typicky česká diakritika) zprávy se dělí na víc segmentů a platí se za každý zvlášť, viz segments v odpovědi. Jde až po prvním dobití kreditu - uvítací kredit SMS neodemkne.

Bez fromNumber odcházejí zprávy pod sdíleným jménem SMSinfo - operátoři pro české sítě povolují jen schválená jména, vlastní jméno odesílatele u sdíleného odesílatele nastavit nejde a příjemce na SMSinfo neodpoví. Pole from v odpovědi je u takové zprávy jen interní štítek volai, příjemce ho nevidí; u zprávy odeslané ze SMS čísla je v from to číslo. Svou identitu proto napiš přímo do textu zprávy, tak jako v příkladu níž.

Zprávu odešleš ze svého SMS čísla polem fromNumber (číslo z GET /v1/sms-numbers). Příjemce pak vidí tvoje číslo místo SMSinfo. Z čísla jde odeslat jen na česká čísla (+420), zpráva stojí 1,90 Kč za segment (bez DPH) a odesílání má denní strop na účet: 20 zpráv v prvních 30 dnech od koupě čísla, potom 50. Pole from v požadavku se dál ignoruje.

  • Bez diakritiky (GSM-7 abeceda) se do jednoho segmentu vejde 160 znaků, delší text se dělí po 153.
  • S diakritikou nebo jiným znakem mimo GSM-7 (UCS-2) je limit 70 znaků na segment, delší text se dělí po 67.
  • Tip: chceš-li se vejít do jednoho segmentu, piš bez diakritiky - Prilis zlutoucky kun místo Příliš žluťoučký kůň.

Požadavek

bash
curl -X POST "https://volai.cz/v1/messages" \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: REPLACE_WITH_UNIQUE_POST_V1_MESSAGES_ID" \
  -d '{"to":"+420777123456","body":"Ahoj z volai! - Moje Appka"}'

Před spuštěním nahraď REPLACE_WITH_UNIQUE… jedinečným ID této akce, například UUID, a ulož si ho. Při opakování stejného požadavku po timeoutu použij stejné ID. Pro jinou akci nebo změněné tělo použij nové ID, i když voláš jiný endpoint.

Odpověď

json
{
  "id": "msg_7c1f9a2e",
  "status": "sent",
  "priceHal": 136,
  "segments": 1
}

Chybové stavy

  • 400validationTělo požadavku nesedí na schéma - chybí to nebo body, nebo body přesahuje 2000 znaků.
  • 400invalid_numberTo není platné české nebo slovenské telefonní číslo.
  • 400invalid_bodyText zprávy musí mít 1 až 765 znaků.
  • 400unsupported_countryČíslo mimo ČR/SR - v MVP nepodporujeme.
  • 400recipient_cannot_receive_smsPevná linka - SMS by nedorazila. Nic neúčtujeme.
  • 400unsupported_charactersText obsahuje emoji nebo jiný znak, který telefonní síť nedoručí. Nic neúčtujeme.
  • 400recipient_is_virtual_numberPříjemce je číslo volai. Na hlasové číslo volai (i vlastní) SMS nedorazí vůbec, přijímá jen hovory. Na SMS číslo volai nedorazí SMS od sdíleného jména SMSinfo: pošli ji ze svého SMS čísla s polem fromNumber. Nic neúčtujeme.
  • 400recipient_rejectedTelefonní síť tohle číslo nepřijímá - bývá to virtuální nebo neexistující číslo. Nic neúčtujeme.
  • 400invalid_from_numberfromNumber není aktivní SMS číslo tvého účtu. Zkontroluj ho v GET /v1/sms-numbers; číslo ve stavu releasing použít nejde. Nic neúčtujeme.
  • 400from_number_unsupported_destinationZe SMS čísla jde odesílat jen na česká čísla (+420). Slovenské číslo pošli bez fromNumber, přes sdílené jméno SMSinfo. Nic neúčtujeme.
  • 402insufficient_creditKredit nepokryje cenu všech segmentů zprávy, nebo účet ještě nikdy nedobil kredit - SMS ze sdíleného odesílatele SMSinfo jdou až po prvním dobití, uvítací kredit na ně použít nejde ani s vlastním číslem. Nic neúčtujeme.
  • 403sms_numbers_unavailableOdesílání ze SMS čísla není pro tenhle účet teď k dispozici. Nic neúčtujeme; bez fromNumber jde zpráva odeslat přes SMSinfo.
  • 409recipient_opted_outPříjemce poslal na tvoje SMS číslo STOP a další zprávy z něj nepřijímá, dokud nepošle START. Opakování nepomůže a nic neúčtujeme.
  • 429rate_limitedVíc než 1 SMS za 2 vteřiny, nebo přes 100 SMS na účet dnes. Při odesílání ze SMS čísla (fromNumber) platí navíc denní strop na účet: 20 zpráv v prvních 30 dnech od koupě čísla, potom 50.
  • 502send_failedTelefonní síť žádost odmítla. Kredit jsme nestrhli, zkus to prosím znovu.
  • 502send_failed_operatorChyba na naší straně (odesílatel nebo kredit u operátora). Kredit jsme nestrhli, zkus to prosím za chvíli.
  • 502sms_gateway_failedBrána operátora dočasně selhala. Kredit jsme nestrhli, zkus to za pár minut.
  • 502send_unknownVýsledek odeslání se nepodařilo spolehlivě potvrdit; zpráva přesto mohla odejít - účtuje se, neposílej ji prosím znovu naslepo.

status zprávy: pending (uložená, ještě se odesílá), sent (operátor přijal zprávu k odeslání, případně do fronty; nejde o potvrzení doručení příjemci), failed (operátor odmítl, u zprávy ze SMS čísla případně až dodatečně - neúčtuje se, už stržený kredit se vrátí, priceHal 0), received (zpráva přijatá na tvé SMS číslo, vždy s direction: "in" a priceHal 0) a unknown (potvrzení o odeslání nedorazilo, typicky timeout na straně operátora) - přesto se účtuje plnou cenou, protože zpráva mohla dojít. Dostaneš-li unknown, zprávu prosím neposílej znovu naslepo - ozvi se podpoře, případný duplicitní kredit vrátíme.

U status: "failed" navíc dostaneš failReason: unsupported_characters (emoji nebo znak, který síť nedoručí), unsupported_recipient (síť číslo nepřijímá), forbidden_sender a low_balance (chyba na naší straně), gateway_failed (brána operátora), network_rejected (jiné odmítnutí), opted_out (příjemce poslal na tvoje SMS číslo STOP). U ostatních stavů je null.

deliveryStatus (delivered nebo undelivered) je doručenka operátora a vzniká jen u zpráv odeslaných z tvého SMS čísla. U zpráv přes sdílené jméno SMSinfo, které doručenky nemá, a u přijatých zpráv je null. Stav sent zůstává potvrzením přijetí operátorem a doručení nedokládá. Zpráva s undelivered se účtuje, protože ji operátor přijal. Když operátor dodatečně ohlásí selhání, změní se status zprávy na failed, priceHal klesne na 0 a kredit za ni se vrátí.

Příklad: zpráva s diakritikou nad 70 znaků = víc segmentů

Tahle zpráva má 112 znaků a obsahuje diakritiku, takže se počítá jako UCS-2 (limit 67 znaků na segment u vícedílné zprávy) - vyjde na 2 segmenty, tedy 2,72 Kč, ne 1,36 Kč:

bash
curl -X POST https://volai.cz/v1/messages \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"to": "+420777123456", "body": "Příliš žluťoučký kůň úpěl ďábelské ódy - a tahle věta má přes sedmdesát znaků, takže spadne do druhého segmentu."}'
json
{
  "id": "msg_9a3f1c7d",
  "status": "sent",
  "priceHal": 272,
  "segments": 2
}

GET/v1/messages/{id}

Detail jedné zprávy.

Požadavek

bash
curl https://volai.cz/v1/messages/msg_7c1f9a2e \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "message": {
    "id": "msg_7c1f9a2e",
    "to": "+420777123456",
    "from": "volai",
    "body": "Ahoj z volai! - Moje Appka",
    "status": "sent",
    "failReason": null,
    "priceHal": 136,
    "segments": 1,
    "createdAt": 1756111500000,
    "source": "api",
    "direction": "out",
    "smsNumber": null,
    "deliveryStatus": null
  }
}

Chybové stavy

  • 404not_foundZpráva neexistuje nebo nepatří tvému účtu.

SMS čísla

GET/v1/sms-numbers

SMS čísla účtu (doplněk SMS číslo): česká mobilní čísla, na která přijímáš SMS a ze kterých je odesíláš. Každé nese status (active, nebo releasing, dokud uvolnění běží či čeká na zopakování), poplatek monthlyFeeHal za 30denní období a nextChargeAt, kdy je splatné další. Výpis funguje i tehdy, když nové koupě nejsou dostupné.

Požadavek

bash
curl https://volai.cz/v1/sms-numbers \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "smsNumbers": [
    {
      "number": "+420770112233",
      "status": "active",
      "createdAt": 1756111640000,
      "monthlyFeeHal": 39000,
      "nextChargeAt": 1758703640000
    }
  ]
}

POST/v1/sms-numbers

Koupí účtu české mobilní SMS číslo. Tělo požadavku se neposílá, číslo vybereme my. Číslo přijímá SMS (přečteš je v GET /v1/messages?direction=in nebo událostí webhooku message.received) a zprávy z něj odešleš polem fromNumber v POST /v1/messages. Hovory nepřijímá ani nevolá. Z kreditu se hned strhne 390,00 Kč bez DPH za první 30denní období a pak znovu každých 30 dní; zaplacené období se při uvolnění nevrací. Účet může mít nejvýš dvě SMS čísla a koupě jde až po prvním dobití kreditu. Když se po koupi u operátora něco nepovede, číslo se zase uvolní a nic se neúčtuje. Když běží jiná koupě téhož účtu, dostaneš 429 rate_limited - počkej pár vteřin a zopakuj to. Doručení zpráv od každého odesílatele, včetně ověřovacích kódů, nezaručujeme.

Požadavek

bash
curl -X POST https://volai.cz/v1/sms-numbers \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "smsNumber": {
    "number": "+420770112233",
    "status": "active",
    "createdAt": 1756111640000,
    "monthlyFeeHal": 39000,
    "nextChargeAt": 1758703640000
  }
}

Chybové stavy

  • 402insufficient_creditKredit nepokryje poplatek 390,00 Kč za první období, nebo účet ještě nikdy nedobil kredit - SMS číslo jde koupit až po prvním dobití a uvítací kredit ho neodemkne. Nic neúčtujeme.
  • 403sms_numbers_unavailableDoplněk SMS číslo teď pro tenhle účet není k dispozici. Nic neúčtujeme; kdybys přístup čekal, napiš na podpora@volai.cz.
  • 409sms_number_limitÚčet už má nejvyšší povolený počet SMS čísel (dvě). Jedno uvolni přes DELETE /v1/sms-numbers/{e164} a pak kup další. Nic neúčtujeme.
  • 503sms_number_out_of_stockOperátor teď nemá volné české mobilní číslo. Nic neúčtujeme, zkus to za chvíli znovu.

DELETE/v1/sms-numbers/{e164}

Uvolní SMS číslo: vrátí se operátorovi a znovu ho získat nejde, zprávy poslané na něj už k tobě nedorazí. Poplatek za běžící 30denní období se nevrací. Když uvolnění u operátora selže, číslo zůstane ve stavu releasing a stejným požadavkem ho uvolníš znovu.

Požadavek

bash
curl -X DELETE "https://volai.cz/v1/sms-numbers/+420770112233" \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "released": true
}

Chybové stavy

  • 400validatione164 v URL není platné telefonní číslo.
  • 404not_foundSMS číslo neexistuje nebo nepatří tvému účtu.
  • 502release_failedUvolnění se u operátora teď nepovedlo. Číslo zůstává tvoje ve stavu releasing, zopakuj stejný požadavek za chvíli. Zaplacené období se nevrací.

Hovory

GET/v1/calls

Historie hovorů, od nejnovějšího. direction (in nebo out) omezí výpis na jeden směr. Filtry from/to (Unix ms, startedAt v rozsahu, from <= to), agentId, flagged (true/false), goal (success/failure/unclear/no_conversation/not_evaluated, nebo zpětně kompatibilní unknown - záznamy bez jasného výsledku: goal.result chybí nebo je unknown (nejasné a bez hodnocení vždy, hovory bez rozhovoru jen s výsledkem unknown) - viz pole goal u hovoru), outcome (agent/transferred/no_answer/other, viz sloupec Účtování u tabulky kind níž). Neznámé parametry se ignorují. Hovor vs. konverzace: GET /v1/calls vrací jednotlivé ZÁZNAMY hovoru - každá noha telefonního spojení je vlastní řádek. Portálová tabulka /hovory seskupuje související nohy (příchozí noha na agenta plus odchozí noha přepojení) do jedné KONVERZACE - úspěšně přepojený hovor se proto v portálu ukáže jako jeden řádek, ale přes API/MCP až jako dva záznamy (noha, která přepojila, má outcome: "transferred"; přijímající noha si drží svůj vlastní outcome). Počítáš-li hovory z API, počítej s tím.

Požadavek

bash
curl "https://volai.cz/v1/calls?limit=20" \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "calls": [
    {
      "id": "c_8f2ac1d4",
      "direction": "in",
      "from": "+420777123456",
      "to": "+420601234567",
      "status": "completed",
      "startedAt": 1756111400000,
      "durationSecs": 47,
      "priceHal": 236,
      "agentId": "ag_kx91fa2b",
      "note": null,
      "flag": null,
      "handledAt": null,
      "rating": null,
      "goal": null,
      "hasConversation": true,
      "source": "inbound",
      "kind": "inbound",
      "endReason": "completed",
      "hasRecording": true,
      "data": {
        "jmeno": "Jana Nováková",
        "pocet_kav": 2,
        "spechalo": "bezna"
      }
    }
  ],
  "hasMore": false,
  "nextBefore": null
}

Chybové stavy

  • 400validationlimit/before musí být kladné celé číslo, direction musí být „in“ nebo „out“; from musí být menší nebo rovno to, pošleš-li oba; agentId nesmí přesáhnout 64 znaků; flagged/goal/outcome mimo povolené hodnoty.

POST/v1/calls

Zahájí odchozí hovor na to. Pošli právě JEDNO z: agentId (vede ho tvůj hlasový agent podle svého systemPrompt), from (přímé spojení dvou čísel bez agenta - bridge, viz níž) nebo systemPrompt (zkušební hovor bez vlastního čísla - žádný nákup ani zakládání agenta předem, viz níž). Nikdy víc než jedno, nikdy žádné. U varianty agentId lze navíc poslat variables (vlastní hodnoty do promptu) a ringingTimeoutSecs (jak dlouho nechat vyzvánět, viz callout níž).

Požadavek

bash
curl -X POST "https://volai.cz/v1/calls" \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: REPLACE_WITH_UNIQUE_POST_V1_CALLS_ID" \
  -d '{"to":"+420777123456","agentId":"ag_kx91fa2b","variables":{"jmeno_zakaznika":"Jana","cislo_objednavky":"A-42"},"ringingTimeoutSecs":30}'

Před spuštěním nahraď REPLACE_WITH_UNIQUE… jedinečným ID této akce, například UUID, a ulož si ho. Při opakování stejného požadavku po timeoutu použij stejné ID. Pro jinou akci nebo změněné tělo použij nové ID, i když voláš jiný endpoint.

Odpověď

json
{
  "id": "c_9d4e2b7f",
  "status": "initiated"
}

Chybové stavy

  • 400invalid_numberto není platné telefonní číslo.
  • 400validationChybí, nebo je jich víc než jedno, z agentId/from/systemPrompt; u agentId variables mimo limit (max 20 klíčů, klíč 64 znaků, hodnota 512 znaků) nebo ringingTimeoutSecs mimo rozsah 5-60; u systemPrompt chybí/nad 1200 znaků, firstMessage nad 300 znaků, nebo ringingTimeoutSecs mimo rozsah 5-30.
  • 400on_dncČíslo máš na seznamu nevolat (/v1/dnc).
  • 400destination_auto_blockedDestinace je po opakovaných neúspěších automaticky blokovaná na 30 dní - blokaci jde zrušit v portálu (Nastavení) nebo přes POST /v1/dnc/{e164}/unblock.
  • 400cannot_call_own_numberOchrana proti smyčce - na vlastní volai číslo se volat nedá.
  • 400cannot_call_volai_numberOchrana proti smyčce - cíl je volai číslo jiného účtu.
  • 404bridge_needs_numberBridge (from) potřebuje aspoň jedno vlastní volai číslo na účtu.
  • 409destination_busyNa stejné číslo ti právě běží jiný hovor.
  • 404agent_not_foundagentId neexistuje nebo nepatří tvému účtu.
  • 404agent_no_numberAgent nemá přiřazené telefonní číslo.
  • 403agent_suspendedAgenta ručně pozastavil provozovatel volai - hovor přes něj teď nejde uskutečnit. Pozastavený celý účet vrací místo toho account_suspended.
  • 402insufficient_creditKredit nepokryje minimum pro zahájení hovoru.
  • 503capacity_busyVšechny odchozí linky jsou právě obsazené, zkus to za minutu.
  • 503trial_calls_disabledU systemPrompt: zkušební hovor je dočasně vypnutý provozním přepínačem - zavolej přes vlastního agenta (agentId).
  • 403trial_email_unverifiedU systemPrompt: jen pro účty s ověřeným e-mailem.
  • 500trial_not_configuredU systemPrompt: sdílený demo agent volai není správně nastavený - dočasná chyba na naší straně.
  • 502call_rejectedU systemPrompt: hlasová platforma spojení hned odmítla, nic se neúčtovalo.
  • 429rate_limitedLimit hovorů, nic se neúčtuje. S agentId: nejvýš 10 hovorů za minutu a 1000 za den na účet, nebo vyčerpaný limit odchozích minut vlastního hlasového motoru - pak odpověď nese Retry-After (za kolik vteřin to zkusit znovu). U systemPrompt: vyčerpaný denní limit 3 zkušebních hovorů na účet, nebo dočasně vytížený sdílený limit napříč všemi účty.
  • 502callback_failedU bridge (from): telefonní operátor rovnou odmítl objednávku spojení, hovor se vůbec nesestavil. Kredit se nestrhl.
  • 502engine_unavailableS agentId na agentovi vlastního hlasového motoru: motor teď neodpovídá (výpadek, ne limit) - zkus to za chvíli, a když to trvá, napiš na podporu s requestId z chyby. Když motor žádost přijal, ale jeho odpověď nedorazila včas, hovor mohl přesto vzniknout: v historii zůstane jako initiated, skutečný výsledek i cenu doplníme, a stejné číslo je asi 15 minut zamčené (destination_busy), ať mu nevoláš dvakrát. Vyčerpaný limit odchozích hovorů je naopak 429 rate_limited.

Vlastní proměnné a doba vyzvánění

variables je objekt řetězec-řetězec, který agent dostane jako dynamické proměnné - v promptu se na ně odkážeš zápisem {{jmeno_zakaznika}}. Limity: nejvýš 20 klíčů, klíč do 64 znaků, hodnota do 512 znaků. Klíč smí obsahovat jen písmena, číslice a podtržítko a nesmí začínat číslicí ani předponou system__ nebo volai_ (obě jsou vyhrazené, požadavek s takovým klíčem odmítneme). Jména attempt_id, caller_number, called_number, trial_prompt a trial_first_message jsou rezervovaná - doplňujeme je sami a tvoje stejnojmenné hodnoty se zahodí.

ringingTimeoutSecs je 5 až 60 vteřin, výchozí 25. Po uplynutí se nevyzvednutý hovor ukončí a v detailu dostane endReason: "no_answer". Delší vyzvánění znamená vyšší šanci, že to zákazník zvedne, ale taky delší dobu blokovaného odchozího slotu.

Bez agenta: přímé spojení dvou čísel (bridge)

Místo agentId pošli from - vlastní číslo nebo jiné číslo zákazníka. volai nejdřív zavolá na from, a jakmile ho někdo zvedne, vytočí to. Obě nohy se účtují zvlášť po 0,92 Kč/min. Když operátor na objednávku neodpoví (výpadek sítě), vrátíme hovor jako zahájený (initiated): mohl proběhnout a jeho skutečný výsledek i cenu doplní synchronizace s operátorem. Neopakuj ho hned - stejné číslo zůstane asi 10 minut zamčené.

bash
curl -X POST https://volai.cz/v1/calls \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"to": "+420777123456", "from": "+420601234567"}'

Zkušební hovor bez vlastního čísla

Místo agentId/from pošli systemPrompt (instrukce pro agenta na tenhle jeden hovor, nejvýš 1200 znaků) a volitelně firstMessage (úvodní věta, nejvýš 300 znaků). Hovor jde ze sdíleného demo čísla volai, ne z tvého vlastního - odpověď proto nese navíc trial: true a from (demo číslo). voiceId u týhle varianty poslat nejde, jede na výchozím hlase. ringingTimeoutSecs má nižší strop 5 až 30 (výchozí 25) - drží se slot ze sdíleného fondu.

bash
curl -X POST https://volai.cz/v1/calls \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+420777123456",
    "systemPrompt": "Jsi recepční kavárny Nula. Přijímáš objednávky na vyzvednutí a mluvíš stručně a přátelsky.",
    "firstMessage": "Dobrý den, tady zkušební agent volai, jak vám mohu pomoct?"
  }'
json
{
  "id": "c_2f7a91mn",
  "status": "initiated",
  "trial": true,
  "from": "+420266266641"
}

Jen pro účty s ověřeným e-mailem, nejvýš 3 hovory za 24 hodin na účet (plus sdílený strop napříč všemi účty, ať nával nezahltí demo číslo). Účtuje se úplně normálně vč. agentní přirážky - žádná sleva. Kdo potřebuje víc, koupí si vlastní číslo (POST /v1/numbers) a založí agenta (POST /v1/agents) - tam tenhle strop neplatí.

Kdyby byl zkušební hovor dočasně vypnutý (provozní přepínač), vrátí systemPrompt srozumitelnou chybu (trial_calls_disabled) místo hovoru; použij mezitím vlastního agenta (agentId).

GET/v1/calls/active

Načte všechny nakonfigurované uzly vlastního motoru pro jedno vlastní agentId. Jde o živý snímek, odlišný od historie hovorů. complete znamená platné odpovědi všech uzlů a úplné údaje o vlastnictví. outboundCount zahrnuje i nezmapované hovory; inboundCount počítá zpětná volání samostatně. Každý odchozí řádek má neprůhledné nativeRef, startedAt (Unix ms) a resolved. Pouze ověřené hovory úloh obsahují callId, taskId, itemId a phoneSha256. Chybějící vazba zůstává viditelná jako resolved: false. checkedAt je ISO čas dokončení snímku a enginesChecked počet uzlů. Nulu odchozích hovorů lze doložit jen při complete: true, outboundCount: 0 a unresolvedOutboundCount: 0; chyba ani neúplný snímek nulu nedokazují. Endpoint pokrývá pouze telefonní hovory vlastního motoru, nikoli webové relace nebo jiné poskytovatele. Platí stávající sdílený limit API klíče.

Požadavek

bash
curl 'https://volai.cz/v1/calls/active?agentId=ag_example' -H 'Authorization: Bearer vk_TVUJ_KLIC'

Odpověď

json
{
  "data": {
    "agentId": "ag_example",
    "complete": true,
    "checkedAt": "2026-10-09T17:59:00.000Z",
    "enginesChecked": 2,
    "outboundCount": 0,
    "inboundCount": 0,
    "unresolvedOutboundCount": 0,
    "outbound": []
  }
}

Chybové stavy

  • 400validationOčekávané vazby nebo dotaz na agenta chybí, nejsou platné nebo už neodpovídají hovoru.
  • 404agent_not_foundAgent neexistuje nebo patří jinému účtu.
  • 502engine_unavailableNativní stav se nepodařilo ověřit; nelze předpokládat, že hovory skončily.
  • 503engine_not_configuredPřipojení k vlastnímu motoru není nakonfigurované.

POST/v1/calls/{id}/hangup

Ukončí jeden vlastní odchozí hovor úlohy na vlastním motoru. Pošli všechny čtyři očekávané vazby: expectedAgentId, expectedTaskId, expectedItemId a expectedPhoneSha256 (SHA-256 přesného cílového čísla E.164 malými hexadecimálními znaky). Server před jediným nativním zavěšením ověří historii úlohy, kontext volání, místnost, účet, agenta, směr i čas zahájení. Trvalý záznam podle call ID zabrání opakovanému požadavku poskytovateli včetně souběhu a timeoutu; Idempotency-Key není potřeba. Výsledek má callId, outcome a endConfirmed: hung_up znamená potvrzení přesného hovoru motorem, already_ended vyžaduje konečný uložený stav i nepřítomnost na všech uzlech a unknown nikdy neznamená úspěch. Po nejasném výsledku zkontroluj živé hovory a uložený stav; opakované volání tohoto endpointu pouze ověřuje stav a další zavěšení neposílá. Po ukončení znovu načti aktivní hovory. Příchozí, cizí, samostatné hovory a jiní poskytovatelé jsou odmítnuti. Rozsah kampaně, povolený čas a uzávěrku hlídá řídicí proces; endpoint nemění účtování ani stav úlohy.

Požadavek

bash
curl -X POST https://volai.cz/v1/calls/c_example/hangup -H 'Authorization: Bearer vk_TVUJ_KLIC' -H 'Content-Type: application/json' -d '{"expectedAgentId":"ag_example","expectedTaskId":"task_example","expectedItemId":"item_example","expectedPhoneSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}'

Odpověď

json
{
  "data": {
    "callId": "c_example",
    "outcome": "hung_up",
    "endConfirmed": true
  }
}

Chybové stavy

  • 400validationOčekávané vazby nebo dotaz na agenta chybí, nejsou platné nebo už neodpovídají hovoru.
  • 404call_not_foundHovor neexistuje nebo nepatří tvému účtu.
  • 502engine_unavailableNativní stav se nepodařilo ověřit; nelze předpokládat, že hovory skončily.
  • 503engine_not_configuredPřipojení k vlastnímu motoru není nakonfigurované.

GET/v1/calls/{id}

Detail hovoru. Volitelně waitSecs (viz callout níž) nechá server počkat na výsledek místo pollování. U hovorů s agentem obsahuje navíc transcript (přepis po replikách, každá s nepovinným polem timeInCallSecs - vteřiny od začátku hovoru, chybí, pokud repliku poskytovatel časem neoznačil) a summary (krátké shrnutí) - viz Webhooky pro stejný tvar doručený automaticky po skončení hovoru. Ve výpisu i v detailu chodí navíc answeredBy (jen u ODCHOZÍCH hovorů: human, voicemail, ivr (zvedlo hlasové menu - automat; motor okamžitě zavěsí) nebo unknown, když motor nerozhodl nebo přepis chybí; u příchozích hovorů pole chybí, protože stručná odpověď volajícího není hlasová schránka). Motor rozhoduje hodnotami human, voicemail a ivr; unknown znamená, že se motor do konce detekčního okna nerozhodl, a od 1.33.0 zůstává unknown (dřív se místo něj bral odhad z přepisu, který krátkou odpověď jako „Ano.“ po dohraném úvodu považoval za záznamník). Odhad z přepisu (human nebo voicemail, unknown u prázdného přepisu; hlasové menu nepozná) platí jen u hovorů bez hodnoty z motoru. Odchozí hovory obsloužené vlastním hlasovým motorem volai k tomu nesou voicemailReason (phrase, long_monologue, beep, human_reply, ivr_phrase nebo digits - human_reply znamená, že hovor zvedl člověk tak krátkou odpovědí, že si toho detektor všiml (ne že jde o schránku), ivr_phrase je fráze hlasového menu, digits hláška operátora čtoucí volané číslo po jednotlivých číslicích), voicemailMessageLeft (motor přečetl nastavený vzkaz do schránky) a voicemailDetectedAtSecs (za kolik vteřin od zvednutí padlo rozhodnutí detektoru - i u hovoru, který zvedl člověk) - všechna tři pole dává jen motor, ElevenLabs klasifikace z přepisu (answeredBy bez explicitní hodnoty) důvod ani čas nezná. Odchozí hovor na motoru nese i optOutRequested: true, když si volaný v hovoru výslovně řekl, ať mu už nevoláme (třeba „už mi nevolejte“, i v posledních vteřinách po rozloučení) - číslo je pak automaticky v seznamu nevolat účtu (GET /v1/dnc) a politika opakování úlohy další pokus nenaplánuje; hovor bez takové repliky klíč nemá. Dál chodí endReason (completed, no_answer, busy, rejected, capacity, blocked, loop_guard, credit_blocked, suspended, engine_error, caller_loop, failed) a kind (agent, bridge, relay, inbound, transfer, sip) - viz tabulku níž. Vlna nástrojů a nahrávek přidala dvě další pole: data (co si agent z hovoru zapsal podle svých dataFields; hodnota null znamená, že to nezaznělo) a hasRecording (hovor má nahrávku ke stažení, viz endpoint níž). Starší hovory tahle pole nemají a v odpovědi chybí. durationSecs je ve vteřinách; u hovorů na vlastním motoru volai může jít o desetinné číslo (zaokrouhlené na dvě desetinná místa). Odpověď nese anotace majitele účtu, vždy přítomné, null když nenastavené: note (string), flag ({reason, flaggedAt} | null), handledAt (Unix ms nebo null) a goal ({result: "success"|"failure"|"unknown", rationale, reason} | null, vyplněné jen po vyhodnocení cíle agenta; reason je "no_caller_speech"|"evaluation_error"|null a říká, proč vyšel výsledek "unknown" - null u skutečně nejasného hodnocení) - viz PATCH /v1/calls/{id} níž. Odpověď dál vždy nese hasConversation (boolean | null) - jestli hovor vůbec měl rozhovor s volajícím; false u nespojeného hovoru, záznamníku nebo hlasového menu, null u staršího záznamu bez dost signálu k rozhodnutí. Hovor u agenta s fázemi (plán workflow uzlů) obsloužený vlastním hlasovým motorem volai (provider: "engine") navíc nese workflowPath - viz sekci Fáze hovoru (workflow) u agentů výš; hovor agenta na ElevenLabs pole nikdy nemá, i když fázemi prošel, protože ho zapisuje jen post-call motoru.

Požadavek

bash
curl https://volai.cz/v1/calls/c_8f2ac1d4 \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "call": {
    "id": "c_8f2ac1d4",
    "direction": "in",
    "from": "+420777123456",
    "to": "+420601234567",
    "status": "completed",
    "startedAt": 1756111400000,
    "durationSecs": 47,
    "priceHal": 236,
    "agentId": "ag_kx91fa2b",
    "note": null,
    "flag": null,
    "handledAt": null,
    "rating": null,
    "goal": null,
    "hasConversation": true,
    "source": "inbound",
    "kind": "inbound",
    "endReason": "completed",
    "hasRecording": true,
    "data": {
      "jmeno": "Jana Nováková",
      "pocet_kav": 2,
      "spechalo": "bezna"
    },
    "transcript": [
      { "role": "agent", "message": "Dobrý den, tady recepce kavárny Nula, jak vám mohu pomoct?", "timeInCallSecs": 0 },
      { "role": "caller", "message": "Chtěl bych si objednat dva latte s sebou.", "timeInCallSecs": 4 },
      { "role": "agent", "message": "Jasně, dva latte na vyzvednutí, bude to za patnáct minut.", "timeInCallSecs": 9 }
    ],
    "summary": "Zákazník si objednal dva latte s sebou, vyzvednutí za 15 minut."
  }
}

Chybové stavy

  • 404call_not_foundHovor neexistuje nebo nepatří tvému účtu.
  • 400validationwaitSecs není celé číslo 0 až 45.

Čekat na výsledek místo pollování (waitSecs)

Hned po POST /v1/calls zavolej GET /v1/calls/{id} s waitSecs (0 až 45, výchozí 0) - server odpoví, až hovor dojde do koncového stavu (completed/failed/missed/no_answer) a vrátí i transcript a summary rovnou, nebo po uplynutí waitSecs, podle toho, co nastane dřív.

bash
curl -X GET "https://volai.cz/v1/calls/c_9d4e2b7f?waitSecs=30" \
  -H "Authorization: Bearer vk_TVUJ_KLIC"
json
{
  "call": {
    "id": "c_9d4e2b7f",
    "direction": "out",
    "from": "+420601234567",
    "to": "+420777123456",
    "status": "completed",
    "startedAt": 1756111400000,
    "durationSecs": 47,
    "priceHal": 269,
    "agentId": "ag_kx91fa2b",
    "note": null,
    "flag": null,
    "handledAt": null,
    "rating": null,
    "goal": null,
    "hasConversation": true,
    "source": "api",
    "kind": "agent",
    "endReason": "completed",
    "hasRecording": true,
    "answeredBy": "human"
  }
}

Vypršel-li čas dřív, než hovor skončil, stillRunning je true a pole nesou aktuální (ne)koncový stav - nikdy to nechybuje. Zkus GET (nebo MCP get_call) znovu, klidně s vyšším waitSecs.

json
{
  "call": {
    "id": "c_9d4e2b7f",
    "direction": "out",
    "from": "+420601234567",
    "to": "+420777123456",
    "status": "ringing",
    "startedAt": 1756111400000,
    "agentId": "ag_kx91fa2b",
    "note": null,
    "flag": null,
    "handledAt": null,
    "rating": null,
    "goal": null,
    "hasConversation": null,
    "source": "api"
  },
  "stillRunning": true
}

GET/v1/calls/{id}/recording

Nahrávka hovoru. Na rozdíl od zbytku API tahle odpověď není JSON - formát těla určuje hlavička content-type skutečné odpovědi (přípona v content-disposition jí odpovídá): u agenta na ElevenLabs audio/mpeg (MP3, 128 kbps, 16 kHz mono), u agenta na vlastním motoru volai dnes audio/ogg. Formát se nikdy nepřevádí - řiď se touhle hlavičkou, ne pevně předpokládaným typem. Nahrávku ti nekopírujeme k sobě: proud jde z hlasové platformy rovnou skrz nás, my jen ověříme, že hovor patří tvému účtu.

?stahnout=1 přepne content-disposition na attachment (prohlížeč soubor uloží místo přehrání) - přípona souboru v ní (.mp3 / .ogg, jinak .bin) vždy odpovídá skutečnému content-type výš. Rozsahy (Range) tahle routa nenabízí - je pro strojovou integraci, která si stáhne celý soubor. Nahrávají se jen hovory agenta, který má recordCalls zapnuté, a držíme je 90 dní od hovoru.

Požadavek

bash
curl https://volai.cz/v1/calls/c_8f2ac1d4/recording \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -OJ

Chybové stavy

  • 404call_not_foundHovor neexistuje nebo nepatří tvému účtu.
  • 404no_recordingHovor nahrávku nemá - nevedl ho agent, nebo mělo nahrávání vypnuté.
  • 410recording_expiredNahrávka byla, ale uplynulo 90 dní a je smazaná. Přepis hovoru zůstává.

PATCH/v1/calls/{id}

Anotace hovoru, kterou si k němu majitel účtu vede sám - poznámka, nahlášení chyby agenta s důvodem a ruční „vyřízeno“. Aspoň jedno ze tří polí musí být přítomné. note (string nebo null, max 2000 znaků) - null poznámku smaže. flag ({reason} max 500 znaků, nebo null) - nastavení nahlášení SMAŽE handledAt (nahlášený hovor přestává být „vyřízený“), flag: null nahlášení zruší beze změny handled. handled (boolean) - true nastaví handledAt na teď, false ho vrátí na null. flag (ne-null) a handled: true v JEDNOM požadavku nejdou dohromady (400 validation) - nahlášení a potvrzené vyřízení se navzájem vylučují, pošli je ve dvou voláních.

Požadavek

bash
curl -X PATCH https://volai.cz/v1/calls/c_8f2ac1d4 \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"note": "Volal kvůli reklamaci, přeposlat na podporu.", "flag": {"reason": "Agent nerozuměl dotazu na fakturu."}}'

Odpověď

json
{
  "call": {
    "id": "c_8f2ac1d4",
    "direction": "in",
    "from": "+420777123456",
    "to": "+420601234567",
    "status": "completed",
    "startedAt": 1756111400000,
    "durationSecs": 47,
    "priceHal": 236,
    "agentId": "ag_kx91fa2b",
    "source": "inbound",
    "kind": "inbound",
    "endReason": "completed",
    "hasRecording": true,
    "data": { "jmeno": "Jana Nováková" },
    "note": "Volal kvůli reklamaci, přeposlat na podporu.",
    "flag": { "reason": "Agent nerozuměl dotazu na fakturu.", "flaggedAt": 1756111460000 },
    "handledAt": null,
    "rating": null,
    "goal": { "result": "failure", "rationale": "Zákazník si objednal dva latte s sebou, vyzvednutí za 15 minut.", "reason": null },
    "hasConversation": true
  }
}

Chybové stavy

  • 400validationŽádné z note/flag/handled v těle není přítomné, note přesahuje 2000 znaků, flag.reason chybí nebo přesahuje 500 znaků, nebo tělo obsahuje flag (ne-null) společně s handled: true.
  • 404call_not_foundHovor neexistuje nebo nepatří tvému účtu.

Stav hovoru: pole status

status je jediné pole, které se u hovoru mění v čase. Prvních pět minut po zahájení se ještě může posunout, pak už je konečné - čtyři z těch stavů jsou koncové.

statusVýznam
initiatedHovor je objednaný, ještě nezačal zvonit.
ringingZvoní u volaného.
in_progressHovor běží.
completedKoncový. Spojený a ukončený hovor - u drtivé většiny čekej durationSecs > 0 (výjimka: hovor s endReason: credit_blocked nebo suspended níže má i délku i cenu 0). Cenu 0 má i hovor s endReason: caller_loop (automat na druhém konci, do pěti minut), ten ale délku má.
no_answerKoncový. Odchozí hovor zvonil, volaný ho nezvedl.
missedKoncový. Příchozí hovor zůstal bez odezvy.
failedKoncový. Obvykle hovor se vůbec nespojil - platforma ho odmítla nebo selhala síť. Výjimka (karta K6, audit R02): endReason: engine_error znamená, že se hovor SPOJIL, ale skončil technickou chybou na naší straně - nese transcript a zpravidla i nahrávku (když ji motor stihl uložit) a nikdy se neúčtuje. Důvod vždy upřesňuje endReason.

Druh hovoru: pole kind

kind říká, kudy hovor vznikl - a tím i jak se účtuje. Je to jediné pole, podle kterého ve faktuře odlišíš hovor vlastního (BYO) agenta od hovoru zabudovaného, protože u relaye se neplatí agentní přirážka.

kindCo to jeÚčtování
agentOdchozí hovor zabudovaného hlasového agenta.0,92 Kč/min + 2,50 Kč/min
bridgePřímé spojení dvou čísel bez agenta.0,92 Kč/min za každou ze dvou nohou
relayOdchozí hovor tvého vlastního agenta přes POST /v1/relay.0,92 Kč/min, bez agentní přirážky
inboundPříchozí hovor na tvoje číslo.0,50 Kč/min (+ agent, pokud ho vedl)
transferDruhá noha, která vznikla přepojením příchozího hovoru na člověka. Agent po přepojení z hovoru odchází, takže se tahle noha účtuje jako běžný odchozí hovor.0,92 Kč/min, bez agentní přirážky
sipHovor vytočený přímo ze zaregistrovaného SIP klienta (softphone, vlastní ústředna) k tvé lince, mimo naše API - doúčtovaný zpětně ze záznamů sítě.0,92 Kč/min, bez agentní přirážky

Proč hovor skončil: pole endReason

endReason doplňujeme u všech uzavřených hovorů. Pozor: hovor zablokovaný kvůli dluhu nebo ručnímu pozastavení provozovatelem má status completed, ale endReason je credit_blocked, resp. suspended, a cena 0 - podle samotného status ho od proběhlého hovoru nepoznáš. Totéž platí pro hovor, na jehož druhém konci byl automat opakující pořád tutéž hlášku (caller_loop): má status completed, ale pokud trval nejvýš pět minut, je jeho cena 0. Tři varianty „nevyzvednuto“ jsou schválně rozlišené, ne jeden společný stav.

endReasonVýznam
completedHovor proběhl a byl regulérně ukončen.
no_answerZvonilo, volaný to nezvedl.
busyObsazovací tón - to NENÍ totéž co nezvednutí.
rejectedPlatforma spojení rovnou odmítla.
capacityOdchozí linky (relay pool) byly plné.
blockedCíl je na seznamu nevolat nebo v automatické 30denní blokaci (viz Seznam nevolat níž).
loop_guardOchrana proti volání na vlastní/cizí volai číslo.
credit_blockedPříchozí hovor na agenta, jehož majitel je v dluhu nad limit - agent hned po úvodní větě zavěsí, cena 0.
suspendedPříchozí hovor na agenta, kterého (nebo jehož celý účet) ručně pozastavil provozovatel volai - agent hned po úvodní větě zavěsí, cena 0.
failedHovor se vůbec nespojil (platforma nebo síť).
engine_errorHovor se spojil, ale skončil technickou chybou hlasového motoru na naší straně - ne vinou volaného. Neúčtuje se.
caller_loopPříchozí hovor, kde volající byl automat opakující pořád tutéž hlášku (fronta, vytáčecí systém) - volai ho po krátkém čekání na člověka ukončí. Hovor má status completed a kladnou délku, ale do pěti minut se neúčtuje (cena 0); delší hovor se účtuje jako každý jiný.

Agenti

GET/v1/agents

Seznam hlasových agentů na účtu.

Požadavek

bash
curl https://volai.cz/v1/agents \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "agents": [
    {
      "id": "ag_pw7q2vnd",
      "name": "Recepční",
      "language": "cs",
      "systemPrompt": "Jsi recepční kavárny Nula. Přijímáš objednávky na vyzvednutí...",
      "firstMessage": "Dobrý den, tady recepce kavárny Nula, jak vám mohu pomoct?",
      "voiceId": "7JbZPqJGWUfXXBim0T8U",
      "provider": "elevenlabs",
      "createdAt": 1756111000000,
      "status": "active",
      "toolIds": ["tl_5c2a91f4"],
      "dataFields": [
        {
          "key": "jmeno",
          "type": "string",
          "description": "Jméno volajícího, které sám řekl."
        }
      ],
      "recordCalls": true,
      "goal": null,
      "knowledge": null,
      "notifyEmail": true,
      "silencePromptSecs": 10,
      "laughter": "auto",
      "missedCallSms": { "enabled": false },
      "suspended": false
    },
    {
      "id": "ag_kx91fa2b",
      "name": "Recepční",
      "language": "cs",
      "systemPrompt": "Jsi recepční kavárny Nula. Přijímáš objednávky na vyzvednutí...",
      "firstMessage": "Dobrý den, tady recepce kavárny Nula, jak vám mohu pomoct?",
      "voiceId": "milena",
      "provider": "engine",
      "numberE164": "+420601234567",
      "createdAt": 1756111000000,
      "status": "active",
      "toolIds": ["tl_5c2a91f4"],
      "transferTo": "+420777123456",
      "transferCondition": "Volající výslovně žádá spojení s člověkem.",
      "dataFields": [
        {
          "key": "jmeno",
          "type": "string",
          "description": "Jméno volajícího, které sám řekl."
        },
        {
          "key": "spechalo",
          "type": "string",
          "description": "Jak moc věc spěchala.",
          "enumValues": ["nizka","bezna","vysoka"]
        }
      ],
      "recordCalls": true,
      "goal": null,
      "knowledge": null,
      "notifyEmail": true,
      "silencePromptSecs": 10,
      "laughter": "auto",
      "missedCallSms": { "enabled": false },
      "suspended": false
    }
  ]
}

POST/v1/agents

Vytvoří nového hlasového agenta. Povinný je jen name a systemPrompt - jak takový prompt napsat dobře je na stránce Hlasový agent . Když pošleš numberE164, agent se na číslo rovnou napojí. POST vrací jen id; následným GET /v1/agents/{id} přečti skutečné provider. transferTo po uložení spustí pokus převést agenta z ElevenLabs na motor, ale při pozastavení, nedostupnosti nebo chybě může zůstat na ElevenLabs, kde přepojení nefunguje. Hlas vybere voiceId: explicitní useForOutboundTasks: true nahradí známý hlas nepodporovaný motorem, včetně katty, výchozím hlasem motoru. Mimo tuto výjimku přijme motor hlasy katalogu kromě hlasů ElevenLabs (katty, starší anet); syrové ElevenLabs ID nebo jiný nekompatibilní hlas vrátí 400. Při false musí prvotní hlas patřit ElevenLabs (katty nebo syrové ElevenLabs ID; anet už ElevenLabs novým agentům nepřiděluje a volai ho odmítne chybou 400) a úspěšný převod kvůli transferTo ho může změnit na výchozí hlas motoru. Bez voiceId agent použije u motoru Milenu a u ElevenLabs hlas šablony (Katty). Volitelná pole (toolIds, transferTo, transferCondition, dataFields, recordCalls, goal, knowledge, notifyEmail, useForOutboundTasks, voicemail) popisuje tabulka pod endpointy.

Požadavek

bash
curl -X POST "https://volai.cz/v1/agents" \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: REPLACE_WITH_UNIQUE_POST_V1_AGENTS_ID" \
  -d '{"name":"Recepční","systemPrompt":"Jsi recepční kavárny Nula. Přijímáš objednávky na vyzvednutí a odpovídáš na otázky o otevírací době. Nikdy nevymýšlej ceny, které neznáš. Hovor ukonči shrnutím objednávky.","firstMessage":"Dobrý den, tady recepce kavárny Nula, jak vám mohu pomoct?","language":"cs","numberE164":"+420601234567","toolIds":["tl_5c2a91f4"],"transferTo":"+420777123456","transferCondition":"Volající chce mluvit s obsluhou, nebo řeší reklamaci.","dataFields":[{"key":"jmeno","type":"string","description":"Jméno volajícího, které sám řekl."},{"key":"pocet_kav","type":"number","description":"Kolik káv si volající objednal."}],"recordCalls":true}'

Před spuštěním nahraď REPLACE_WITH_UNIQUE… jedinečným ID této akce, například UUID, a ulož si ho. Při opakování stejného požadavku po timeoutu použij stejné ID. Pro jinou akci nebo změněné tělo použij nové ID, i když voláš jiný endpoint.

Odpověď

json
{
  "id": "ag_kx91fa2b"
}

Chybové stavy

  • 400invalid_nameJméno agenta musí mít 1 až 60 znaků.
  • 400invalid_system_promptInstrukce agenta musí mít 10 až 6000 znaků.
  • 400invalid_languagelanguage musí být cs, sk, en, de nebo pl.
  • 400agent_limitNa účtu už je maximální počet agentů (10).
  • 400tool_limitV toolIds je víc než 10 nástrojů.
  • 400invalid_data_fieldsdataFields mají neplatný klíč, typ, popis nebo je jich víc než 10.
  • 400invalid_goalgoal je moc dlouhý - vejde se do 300 znaků.
  • 400invalid_knowledgeknowledge je moc dlouhý - vejde se do 20000 znaků.
  • 400transfer_looptransferTo míří na volai číslo téhož účtu - hovor by se vracel sám na sebe.
  • 400blocked_destinationtransferTo je prémiová linka se zvláštním tarifem.
  • 400invalid_numbertransferTo není platné telefonní číslo.
  • 400invalid_voicemail_messagemessage ve voicemail chybí nebo je moc dlouhá - povinná při action "message", vejde se do 400 znaků.
  • 400voicemail_requires_enginevoicemail vyžaduje prvotní volbu motoru: useForOutboundTasks: true nebo vynechané pole s motorem jako výchozím. false chybu vrátí i s transferTo, protože validace předchází pokusu o přepnutí.
  • 400invalid_workflowNeplatný graf workflow (nedosažitelný uzel, chybějící vstupní uzel, rezervované jméno nástroje...); nic se neuloží, konkrétní důvod je vždy v message.
  • 404calendar_integration_not_foundcalendar.integrationId neukazuje na kalendářové připojení tvého účtu - vyber ho znovu z GET /v1/integrations.
  • 400calendar_invalid_calendarcalendar.calendarId není mezi kalendáři toho připojení, nebo je jen ke čtení (accessRole reader nebo freeBusyReader) - agent do něj schůzku nezapíše. Vyber kalendář s právem zápisu z list_calendars.
  • 400calendar_invalid_timezonecalendar.timezone není platná IANA zóna, například "Europe/Prague".
  • 400calendar_invalid_windowsOkna v calendar.windows nemají tvar HH:MM, překrývají se, nebo je jich na jeden den víc než čtyři.
  • 400calendar_no_windowsKalendář je zapnutý, ale žádný den nemá okno - agent by neměl co nabídnout.
  • 400calendar_invalid_rulesČíselná pravidla kalendáře jsou mimo meze, nebo je minimální předstih delší než horizont hledání.
  • 404tool_not_foundNástroj z toolIds neexistuje nebo nepatří tvému účtu.
  • 404not_foundnumberE164 neexistuje nebo nepatří tvému účtu.
  • 400validationTvar vstupu nesedí - typicky voiceId, které neodpovídá žádné položce z GET /v1/voices ani nevypadá jako syrové ElevenLabs ID (chyba vyjmenuje platné hodnoty), nebo silencePromptSecs mimo meze 3 až 25.
  • 500agent_template_missingŠablona pro založení agenta na naší straně chybí nebo je poškozená - dočasná chyba na naší straně, napiš prosím podpoře.
  • 503engine_not_configuredVyžádal sis vlastní hlasový motor volai (useForOutboundTasks: true), ale motor nebo odchozí provoz na motoru teď nejsou nakonfigurované. Zkontroluj konfiguraci, nebo pole vynech - agent vznikne na ElevenLabs.
  • 503engine_setup_incompleteAgent vznikl, ale příprava odchozích úkolů na vlastním motoru volai se nepovedla. Vytváření NEOPAKUJ - vznikl by duplicitní agent. Dohledej ho v GET /v1/agents podle jména, zkontroluj jeho provider a dokonči ho v portálu Agenti nebo s podporou; samotný PATCH /v1/agents/{id} neumí poskytovatele zvolit.

GET/v1/agents/{id}

Detail jednoho agenta - stejná pole jako ve výpisu, jen pro jednoho. Smazaný agent se chová, jako by neexistoval: 404 agent_not_found. Při zakládání určuje prvotní platformu useForOutboundTasks: true zvolí vlastní motor, false ElevenLabs a vynechané pole použije výchozí nastavení nasazení. Protože POST vrací jen id, výsledek ověř tímto následným GET v poli provider. transferTo u agenta na ElevenLabs spustí pokus o převod na motor, který může být pozastavený, nedostupný nebo selhat; dokud provider zůstává elevenlabs, přepojení nefunguje. Při úspěchu se může změnit hlas. Změna language přes PATCH z jazyka, ve kterém noví agenti vznikají na ElevenLabs (dnes en, de, pl), na jazyk, ve kterém vznikají na motoru (cs, sk), spustí stejný pokus: při úspěchu agent dostane výchozí hlas motoru pro nový jazyk (Milena, Katarina), při selhání zůstane na ElevenLabs. Změna mezi cs a sk nic nepřesouvá. Ostatní přechody dělá volai (podpora). Agent na ElevenLabs bere jen anet nebo syrové ElevenLabs voice id; ostatní hlasy katalogu fungují jen na motoru. Odpověď navíc vždy nese pole laughterEffective ({active, reason, sensitiveCategory} | null) - živý stav politiky smíchu pro PUBLIKOVANOU konfiguraci, spočtený čerstvě mimo hovor. U agenta na ElevenLabs je to bez volání motoru vždy {active: false, reason: "not_engine", sensitiveCategory: null}; u agenta na motoru je reason jedna z několika hodnot (ok, agent_off, sensitive_prompt, voice_not_listed a další) a motor smí časem přidat novou - neznámou hodnotu zobraz jako vypnuto, ne jako chybu. Když motor neodpoví do 3 vteřin, je nedostupný, nebo agenta nezná (tichá 404 u starší verze motoru), je laughterEffective rovnou null - nikdy odhad.

Když motor neodpoví do 3 vteřin, je nedostupný nebo agenta nezná, vypadá odpověď takto: "laughterEffective": null - ne objekt s odhadnutými hodnotami.

Požadavek

bash
curl https://volai.cz/v1/agents/ag_kx91fa2b \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "agent": {
    "id": "ag_kx91fa2b",
    "name": "Recepční",
    "language": "cs",
    "systemPrompt": "Jsi recepční kavárny Nula. Přijímáš objednávky na vyzvednutí...",
    "firstMessage": "Dobrý den, tady recepce kavárny Nula, jak vám mohu pomoct?",
    "voiceId": "milena",
    "provider": "engine",
    "numberE164": "+420601234567",
    "createdAt": 1756111000000,
    "status": "active",
    "toolIds": ["tl_5c2a91f4"],
    "transferTo": "+420777123456",
    "transferCondition": "Volající výslovně žádá spojení s člověkem.",
    "dataFields": [
      {
        "key": "jmeno",
        "type": "string",
        "description": "Jméno volajícího, které sám řekl."
      },
      {
        "key": "spechalo",
        "type": "string",
        "description": "Jak moc věc spěchala.",
        "enumValues": ["nizka","bezna","vysoka"]
      }
    ],
    "recordCalls": true,
    "goal": null,
    "knowledge": null,
    "notifyEmail": true,
    "silencePromptSecs": 10,
    "laughter": "auto",
    "missedCallSms": { "enabled": false },
    "suspended": false,
    "laughterEffective": {
      "active": true,
      "reason": "ok",
      "sensitiveCategory": null
    }
  }
}

Chybové stavy

  • 404agent_not_foundAgent neexistuje, byl smazaný, nebo nepatří tvému účtu.

GET/v1/agents/{id}/call-hold

Přečte vlastní trvalou stopku agenta. Odpověď je přímo {agentId, held, hold}; před zastavením held:false, hold:null. Neříká nic o administrátorské suspenzi ani dalších podmínkách volání.

Požadavek

bash
curl https://volai.cz/v1/agents/ag_example/call-hold -H 'Authorization: Bearer vk_TVUJ_KLIC'

Odpověď

json
{
  "agentId": "ag_example",
  "held": false,
  "hold": null
}

Chybové stavy

  • 404agent_not_foundAgent neexistuje, byl smazaný, nebo nepatří tvému účtu.

POST/v1/agents/{id}/call-hold

Trvale zastaví nové odchozí hovory vlastního agenta motoru a jeho ověřené zpětné kampaňové hovory. Tělo má přesně reason:budget_limit a operationId (1-128 znaků, písmena, čísla, tečka, podtržítko, dvojtečka a pomlčka; první znak písmeno nebo číslo). Stopka nemá expiraci ani obnovení. Stejné ID a obsah vrátí původní audit, jiná operace 409 revision_conflict; po nejisté odpovědi ověřte GET. hold obsahuje agentId, reason, operationId, fingerprint a createdAt. Nemění admin suspenzi, demo ani běžící hovory. Nezávislý výpadek callback resolveru stále ponechá výchozího agenta čísla. Idempotency-Key zde není zdrojem idempotence.

Požadavek

bash
curl https://volai.cz/v1/agents/ag_example/call-hold -X POST -H 'Authorization: Bearer vk_TVUJ_KLIC' -H 'Content-Type: application/json' -d '{"reason":"budget_limit","operationId":"budget-stop-001"}'

Odpověď

json
{
  "agentId": "ag_example",
  "held": true,
  "hold": {
    "agentId": "ag_example",
    "reason": "budget_limit",
    "operationId": "budget-stop-001",
    "fingerprint": "14e5bfb3f57897b30d172ef4ba861b2a27e7beb1de278a94b47bc0f14c614f4c",
    "createdAt": "2026-10-08T12:00:00.000Z"
  }
}

Chybové stavy

  • 404agent_not_foundAgent neexistuje, byl smazaný, nebo nepatří tvému účtu.
  • 400validationNeplatné tělo nebo nepodporovaný agent. Zvolte vlastního agenta motoru mimo sdílenou ukázku.
  • 409revision_conflictExistuje jiné trvalé zastavení. Původní audit zůstal zachován; stav ověřte přes GET.

PATCH/v1/agents/{id}

Upraví existujícího agenta - stejná pole jako při vytvoření, všechna nepovinná. Pošli jen to, co se má změnit. Dvě z nich se ale posílají VŽDY CELÁ, protože nahrazují dosavadní seznam: toolIds a dataFields. Prázdné pole tedy funkci vypne ("toolIds": [] odebere agentovi všechny nástroje), kdežto chybějící klíč znamená „nech, jak to je“. Přepojení se vypíná prázdným řetězcem: "transferTo": "".

Požadavek

bash
curl -X PATCH https://volai.cz/v1/agents/ag_kx91fa2b \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"firstMessage": "Dobrý den, kavárna Nula, co si dáte?"}'

Odpověď

json
{
  "agent": {
    "id": "ag_kx91fa2b",
    "name": "Recepční",
    "language": "cs",
    "systemPrompt": "Jsi recepční kavárny Nula. Přijímáš objednávky na vyzvednutí...",
    "firstMessage": "Dobrý den, kavárna Nula, co si dáte?",
    "voiceId": "milena",
    "provider": "engine",
    "numberE164": "+420601234567",
    "createdAt": 1756111000000,
    "status": "active",
    "toolIds": ["tl_5c2a91f4"],
    "transferTo": "+420777123456",
    "transferCondition": "Volající výslovně žádá spojení s člověkem.",
    "recordCalls": true,
    "goal": null,
    "knowledge": null,
    "notifyEmail": true,
    "silencePromptSecs": 10,
    "laughter": "auto",
    "missedCallSms": { "enabled": false },
    "suspended": false
  }
}

Chybové stavy

  • 400invalid_nameJméno agenta musí mít 1 až 60 znaků.
  • 400invalid_system_promptInstrukce agenta musí mít 10 až 6000 znaků.
  • 400invalid_languagelanguage musí být cs, sk, en, de nebo pl.
  • 400tool_limitV toolIds je víc než 10 nástrojů.
  • 400invalid_data_fieldsdataFields mají neplatný klíč, typ, popis nebo je jich víc než 10.
  • 400invalid_goalgoal je moc dlouhý - vejde se do 300 znaků.
  • 400invalid_knowledgeknowledge je moc dlouhý - vejde se do 20000 znaků.
  • 400transfer_looptransferTo míří na volai číslo téhož účtu.
  • 400blocked_destinationtransferTo je prémiová linka se zvláštním tarifem.
  • 400invalid_voicemail_messagemessage ve voicemail chybí nebo je moc dlouhá - povinná při action "message", vejde se do 400 znaků.
  • 400voicemail_requires_enginevoicemail je jen pro agenta s provider "engine". Agenta na ElevenLabs převede na motor podpora (podpora@volai.cz); do té doby pole vynech.
  • 400invalid_workflowNeplatný graf workflow (nedosažitelný uzel, chybějící vstupní uzel, rezervované jméno nástroje...); nic se neuloží, konkrétní důvod je vždy v message.
  • 404calendar_integration_not_foundcalendar.integrationId neukazuje na kalendářové připojení tvého účtu - vyber ho znovu z GET /v1/integrations.
  • 400calendar_invalid_calendarcalendar.calendarId není mezi kalendáři toho připojení, nebo je jen ke čtení (accessRole reader nebo freeBusyReader) - agent do něj schůzku nezapíše. Vyber kalendář s právem zápisu z list_calendars.
  • 400calendar_invalid_timezonecalendar.timezone není platná IANA zóna, například "Europe/Prague".
  • 400calendar_invalid_windowsOkna v calendar.windows nemají tvar HH:MM, překrývají se, nebo je jich na jeden den víc než čtyři.
  • 400calendar_no_windowsKalendář je zapnutý, ale žádný den nemá okno - agent by neměl co nabídnout.
  • 400calendar_invalid_rulesČíselná pravidla kalendáře jsou mimo meze, nebo je minimální předstih delší než horizont hledání.
  • 404tool_not_foundNástroj z toolIds neexistuje nebo nepatří tvému účtu.
  • 404agent_not_foundAgent neexistuje nebo nepatří tvému účtu.
  • 400validationTvar vstupu nesedí - typicky voiceId, které neodpovídá žádné položce z GET /v1/voices ani nevypadá jako syrové ElevenLabs ID (chyba vyjmenuje platné hodnoty), nebo silencePromptSecs mimo meze 3 až 25.
  • 409in_progressBuď na agentovi právě probíhá jiná změna (koncept nebo publikace), nebo předchozí změna proběhla, ale nešla bezpečně potvrdit - než tohle PATCH zopakuješ, přečti GET /v1/agents/{id}/draft/operation a rozhodni podle operation.
  • 502engine_unavailableU agenta, který už běží na vlastním motoru volai: motor teď neodpovídá, úprava se neuložila. Zkus to prosím znovu za chvíli.
  • 503engine_not_configuredU agenta, který už běží na vlastním motoru volai: motor je momentálně nenakonfigurovaný na naší straně, úprava se neuložila. Napiš prosím podpoře.
  • 502engine_readback_mismatchU agenta, který už běží na vlastním motoru volai: motor uložil jinou konfiguraci, než jsme poslali, úprava se neuložila. Zkus to znovu.

DELETE/v1/agents/{id}

Smaže agenta u volai i u ElevenLabs. Číslo, které na něj bylo navěšené, zůstává tvoje - jen mu nastav nové směrování přes PATCH /v1/numbers/{e164}.

Požadavek

bash
curl -X DELETE https://volai.cz/v1/agents/ag_kx91fa2b \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "deleted": true
}

Chybové stavy

  • 404agent_not_foundAgent neexistuje nebo nepatří tvému účtu.

Odpovědi POST /v1/agents, PATCH /v1/agents/{id}, POST /v1/agents/{id}/draft/publish a POST /v1/agents/{id}/draft/rollback mohou nést nepovinné pole warnings. Několik kódů se týká první věty: first_message_no_ai_disclosure, když uložená první věta agenta nesděluje, že mluví digitální asistent (evropský AI Act, čl. 50), a first_message_too_long, když je odhadovaná mluvená doba TOHO, CO VOLAJÍCÍ SKUTEČNĚ SLYŠÍ (první věta plus věta o nahrávání, když je recordCalls zapnuté), nad 7 sekundami (asi 2,53 slova za sekundu; číslice vždy a velké zkratky do čtyř znaků se počítají po znacích). Nezměněná výchozí šablona první věty tenhle kód nikdy nespustí, jen text, který zákazník upravil. Kdo agentku přeruší, udělá to nejčastěji do jedné sekundy slyšitelné řeči. Agent s fázemi (pole workflow, viz sekce Fáze hovoru (workflow) níž) přidává do stejného pole dalších deset kódů - dead_end, empty_node, entry_phrase_never_spoken, missing_enter_phrase, short_condition, condition_about_agent, base_prompt_long, workflow_not_synced_to_provider, workflow_node_tool_not_synced a workflow_node_tool_missing (uzel odkazuje nástroj, který na účtu už není - smazaný; fázi je potřeba uložit bez něj) - a typicky je nese i tehdy, když první věta v pořádku je (šablona Recepce jich vrací tři). Varování, které patří konkrétní fázi nebo přechodu, nese navíc nodeId, nebo edgeId. Žádný z kódů uložení nezastaví - jen na ně upozorní. Pole v odpovědi chybí, jen když není co hlásit. Vrácení konceptu na dřívější revizi (draft/rollback) vrací warnings stejně jako publikace, protože taky propisuje první větu na živého agenta. Jen publikace a rollback (ne POST/PATCH /v1/agents) navíc můžou ve stejném poli vrátit number_changed_by_publish s hodnotami from/to (E.164, nebo null bez čísla), když republikace doopravdy změní telefonní číslo živého agenta - ani ten publikaci neblokuje. Samostatný kód calendar_pending_engine_switch se objeví, když má agent nastavený calendar (termíny podle kalendáře), ale běží na ElevenLabs - nastavení se uloží a začne fungovat, jakmile agent přejde na vlastní motor volai; zapnutí kalendáře ten přechod samo spouští. Agent s připojeným číslem (numberE164), jehož zadání (systemPrompt nebo firstMessage) používá {{promennou}} mimo sadu, kterou příchozí hovor doopravdy dodává (caller_number, called_number, attempt_id, volai_blocked, volai_block_reason, trial_prompt, trial_first_message a sedm system__* proměnných), dostane prompt_variables_unavailable_inbound - na příchozím hovoru se taková proměnná dosadí jako prázdný text. Když po odstranění všech {{...}} bloků z neprázdné první věty v ní nezůstane žádné písmeno ani číslice mimo proměnné, dostane first_message_empty. Samostatný kód agent_misuse_suspected se objeví, když konfigurace agenta (u odpovědí výš) obsahuje formulace, které mohou působit jako vydávání se za úřad či jinou instituci, nebo jako žádost o citlivé údaje - viz Podmínky volai § 10. Týž kód ve stejném tvaru vracejí v nepovinném poli warnings i PUT /v1/agents/{id}/draft a POST /v1/agents/{id}/simulate (koncept), POST /v1/calls (prompt zkušebního hovoru nebo variables), POST /v1/tasks a PATCH /v1/tasks/{id} (název úkolu a proměnné příjemců) a POST /v1/tools a PATCH /v1/tools/{id} (popis nástroje a jeho parametrů). Uložení ani odeslání neblokuje, jde jen o upozornění. Jen POST /v1/agents navíc vrací elevenlabs_by_explicit_choice, když požadavek poslal useForOutboundTasks: false u jazyka, kterému by nasazení jinak dalo vlastní motor volai (dnes cs a sk), a agent proto zůstal na ElevenLabs - bez hlasů motoru a bez funkcí, které běží jen na motoru. Agent se založí, varování jen upozorní; kdo chtěl agenta jen na příchozí hovory, má pole vynechat.

json
{
  "id": "ag_kx91fa2b",
  "warnings": [
    {
      "code": "first_message_no_ai_disclosure",
      "message": {
        "cs": "Podle evropského AI Act (čl. 50) musí první věta prozradit, že mluví digitální asistentka. Stačí: „Dobrý den, tady digitální asistentka firmy Novák.“ Bez toho za sdělení odpovídáš ty.",
        "en": "Under the EU AI Act (Article 50), the first line must reveal that a digital assistant is speaking. For example: \"Hello, this is the digital assistant for Novak Inc.\" Without it, you are responsible for the disclosure."
      }
    }
  ]
}

Nová pole agenta

POST /v1/agents i PATCH /v1/agents/{id} berou kromě jména, promptu, hlasu a čísla i tahle pole. Všechna jsou nepovinná při vytváření. toolIds, transferTo, transferCondition, dataFields a recordCalls u staršího agenta v odpovědi chybí, dokud je nikdo nenastaví. goal a knowledge se naopak vrací VŽDY - nenastavené jako null, ne jako chybějící klíč - a notifyEmail je vždycky boolean (chybí-li, znamená to zapnuto). Odpověď navíc vždy nese provider (engine nebo elevenlabs) - říká, na kterém poskytovateli agent doopravdy běží, obě hodnoty se v provozu vyskytují. Stejně tak vždy nese suspended (boolean) - true, když agenta ručně pozastavil provozovatel volai; takový agent nepřijímá ani neuskutečňuje hovory (agent_suspended) a pole přes API ani MCP nastavit nejde. useForOutboundTasks je výjimka - bere ho JEN POST /v1/agents, v PATCH není (existujícího agenta mezi poskytovateli přepíná volai, ne zákazník; sám to udělá po zapnutí transferTo nebo calendar a po změně language na jazyk, ve kterém noví agenti vznikají na motoru - viz detail agenta výš). voicemail je dostupné jen u agenta s provider: "engine" - u ostatních POST/PATCH s tímhle polem odmítne. silencePromptSecs (připomínka v tichu) se naopak přijímá a vrací u OBOU poskytovatelů - u agenta na ElevenLabs se hodnota jen uloží, zazní až po přepnutí na vlastní motor volai. workflow (fáze hovoru) je dostupné u obou poskytovatelů, ale ne stejně úplně - viz sekci Fáze hovoru (workflow) níž. laughter (smích agentky) je taky vždy přítomné, stejně jako goal/knowledge/notifyEmail výš - u staršího agenta bez nastavené hodnoty je to auto, nikdy chybějící klíč; přijímá se a ukládá u OBOU poskytovatelů, slyšet je ale jen u agenta na motoru s českým hlasem Cartesia.

PoleCo dělá
toolIdsPole id webhook nástrojů z GET /v1/tools, které smí agent během hovoru volat. Nejvýš 10. Nástroj patří účtu, takže ho může mít zapnutý víc agentů.
transferToČíslo v E.164, na které agent přepojí volajícího, když si řekne o člověka. Prázdný řetězec přepojení vypne. Vlastní volai číslo účtu tu být nesmí (transfer_loop).
transferConditionKdy má přepojit, napsané jako pokyn modelu (do 500 znaků). Když ho nepošleš, dosadíme výchozí větu.
dataFieldsCo si má agent z hovoru zapsat: pole objektů {key, type, description, enumValues}, nejvýš 10. key jsou malá písmena bez diakritiky, číslice a podtržítka; type je string, number nebo boolean; enumValues jde zadat jen u textového údaje. Vyplněné hodnoty pak chodí v poli data u hovoru.
recordCallsNahrávat hovory tohohle agenta. Výchozí je zapnuto; při false nahrávka nevznikne a GET /v1/calls/{id}/recording vrátí no_recording. Když je zapnuté, agent to volajícímu automaticky řekne v první větě. Za to, že sdělení odpovídá zákonu pro tvoji situaci, i tak odpovídáš ty.
goalCíl hovoru jednou větou - podle něj LLM soudce po hovoru vyhodnotí, jestli agent dosáhl toho, kvůli čemu volající zavolal (vyhodnocuje se jen u agenta na vlastním motoru volai, viz sekci Cíl agenta a hodnocení v dokumentaci Hlasového agenta). Nejvýš 300 znaků. PATCH s null nebo prázdným řetězcem cíl smaže.
knowledgeText, který agent zná navíc k instrukcím - ceník, otevírací dobu, časté dotazy. Připojí se na konec promptu. Nejvýš 20000 znaků. PATCH s null nebo prázdným řetězcem znalosti smaže.
notifyEmailPoslat majiteli účtu e-mail se shrnutím po každém hovoru tohohle agenta - dnes se posílá jen u agenta na vlastním motoru volai (provider: "engine"); u agenta na ElevenLabs se hodnota uloží, ale e-mail se zatím neodesílá. Výchozí je zapnuto.
useForOutboundTaskstrue vybere při založení vlastní motor, false začne na ElevenLabs a bez pole platí výchozí nastavení nasazení - to závisí na zapnutí motoru a povolených jazycích. Samotné cs nebo sk motor nezaručuje; při vypnutém výchozím motoru agent vzniká na ElevenLabs. POST vrací jen id, výsledek proto čti z provider v následném GET /v1/agents/{id}. Pole je jen při zakládání; další přechody dělá volai (podpora). transferTo navíc spouští pokus o převod z ElevenLabs na motor, jehož výsledek je potřeba ověřit.
voicemailRozpoznání hlasové schránky u ODCHOZÍCH hovorů - objekt {enabled, action, message}. message je povinná při action: "message", do 400 znaků. PATCH ho bere jen u agenta s provider: "engine". U POST musí na motor mířit prvotní volba: explicitní useForOutboundTasks: true nebo vynechané pole s motorem jako výchozím. false selže s voicemail_requires_engine i s transferTo, protože validace předchází pokusu o převod. PATCH s null nastavení smaže.
silencePromptSecsKolik vteřin ticha od volajícího vyvolá jednu připomínku ("Jste tam?"), celé číslo 3 až 25. null připomínku vypne. Chybí-li pole při POST, uloží se výchozích 10. Přijímá a ukládá se u OBOU poskytovatelů - u agenta na standardní platformě ElevenLabs (provider: "elevenlabs") se hodnota jen uloží a zazní až po přepnutí agenta na vlastní hlasový motor volai, který jediný umí připomínku přehrát. Hodnota mimo meze 3 až 25 se odmítne obecnou validační chybou (validation, 400) - žádný vlastní kód.
workflowRozdělí hovor na fáze (uzly) - viz sekci Fáze hovoru (workflow) níž pro tvar, limity a příklad. PATCH s null fáze smaže a agent se vrátí na jednu fázi jako dosud.
laughterJestli se agentka smí v hovoru zasmát, když se zasměje nebo zažertuje volající: auto nechá rozhodnutí na detekci citlivého tématu (dluhy, zdravotnictví, úřady, pohřebnictví), on přebije tuhle detekci, off ho vždy potlačí; skutečný výsledek pro danou konfiguraci ukazuje laughterEffective. Vynechané pole se při POST uloží jako auto a v odpovědi je laughter vždy přítomné, nikdy jako chybějící klíč. Přijímá a ukládá se u OBOU poskytovatelů, slyšet je ale jen u agenta na motoru s českým hlasem Cartesia - u ElevenLabs nemá žádný efekt. Změna platí od dalšího hovoru.

Přepojení: co API opravdu udělá

Na vlastním hlasovém motoru volai jde přepojení mostem: agent vytočí cíl jako druhého účastníka a po zvednutí zmlkne. Na standardní platformě přepojení nefunguje. transferTo spustí pokus převést agenta na motor, ale při pozastavení, nedostupnosti nebo chybě může zůstat na ElevenLabs; výsledek ověř v provider. Shrnutí hovoru člověku předat neumíme ani na jedné platformě, žádný „warm transfer“ neexistuje.

Druhá noha je běžný odchozí hovor a tak se i účtuje: 0,92 Kč/min bez agentní přirážky. Ve výpisu hovorů ji poznáš podle kind: "transfer".

Fáze hovoru (workflow)

Volitelné pole workflow rozdělí hovor do fází (nodes). Uzel je ROZSAH, ne scénář: jen doplní prompt, znalosti a nástroje platné, dokud je hovor v něm, a volitelně větu, kterou agent řekne hned při vstupu. Přechod mezi uzly rozhoduje model sám tím, že zavolá nástroj - nikdy to nepředepisuješ jako pevný skript. Základní systemPrompt agenta by měl nést jen to, co platí VE VŠECH fázích (identitu, tón, jak se předává vzkaz); instrukce jedné fáze patří do jejího uzlu, jinak nemá účinek. Do základního promptu patří i dvě věty, bez kterých agent nejčastěji odpoví, co neví: „Provozní údaje, které nemáš v zadání (otevírací doba, lhůty, ceny, dostupnost lidí), neříkej a neodhaduj - řekni, že to upřesní kolega.“ a „Ptej se vždy jen na jednu věc najednou.“

entry je id uzlu, ve kterém hovor začíná. edges jsou přechody: condition (5 až 200 znaků) popisuje, KDY přejít, a musí mluvit o tom, co řekl VOLAJÍCÍ (nikdy o tom, co udělal agent nebo nástroj) - jde doslova do popisu nástroje, kterým model přechod provede. Limity: 2 až 8 uzlů, 1 až 24 hran, nejvýš 5 hran z jednoho uzlu, mezi dvěma uzly smí vést jen jedna hrana (v libovolném směru, duplicate_pair); uzlův prompt má nejvýš 2 000 znaků, knowledge nejvýš 4 000, prompt a knowledge dohromady nejvýš 4 000 na uzel, a toolIds nejvýš 10 nástrojů na uzel.

enterPhrase je věta vyslovená jednou při vstupu do uzlu (nejvýš 160 znaků). enterBehavior: "wait" řekne modelu, ať po ní v tomtéž tahu nic dalšího neříká a počká na volajícího - typicky u citlivé fáze jako oznámení úmrtí; vyžaduje vyplněné enterPhrase, jinak wait_without_phrase. Uzel bez vlastního enterPhrase dostane při vstupu výchozí krátkou větu (např. česky „Rozumím.“) jen na vlastním motoru volai (provider: "engine"); u agenta na ElevenLabs se při vstupu neozve nic - vlastní věta zní přirozeněji a je jediná, která zazní u obou, a proto ji editor dál doporučuje varováním missing_enter_phrase.

Uzel typu konec neexistuje - fáze samy o sobě hovor nikdy neukončí. Hovor pořád končí jen vestavěným ukončením, stejně jako dosud.

enterBehavior: "wait" se přenáší i k agentům na ElevenLabs, ne jen na vlastní hlasový motor volai (provider: "engine"); věta při vstupu na motoru zazní doslovně, u ElevenLabs jde do promptu fáze jako instrukce, ať ji řekne - model ji zpravidla řekne podobně, ale ne nutně stejnými slovy. Uzel BEZ vlastního enterPhrase u ElevenLabs neřekne při vstupu nic: výchozí krátkou větu (např. česky „Rozumím.“) doplňuje jedině vlastní motor volai. Dva rozdíly jsou větší. Nástroj přiřazený JEN uzlu (node.toolIds) se k agentovi na ElevenLabs zatím nepřenáší - fáze tam poběží bez něj, a odpověď na to upozorní kódem workflow_node_tool_not_synced; když ho tam chceš mít, přidej ho i mezi nástroje agenta (pole toolIds, v portálu záložka Pokročilé). Na vlastním motoru volai se uzlový nástroj přenáší beze změny. A workflowPath u hovoru zapisuje výhradně post-call vlastního motoru, takže hovor agenta na ElevenLabs ho nikdy nenese, i když fázemi prošel.

Neplatný graf (nedosažitelný uzel, chybějící vstupní uzel, rezervované jméno nástroje...) vrátí invalid_workflow a nic se neuloží; konkrétní důvod je vždy v message.

Příklad - recepce pohřební služby

json
{
  "workflow": {
    "version": 1,
    "entry": "triaz",
    "nodes": [
      {
        "id": "triaz",
        "label": "Triáž",
        "prompt": "Zjisti, proč volající volá, jednou otázkou. Pomáháš volajícím s ohlášením úmrtí, s výkopem hrobu nebo uložením urny a s poptávkou práce na pomníku."
      },
      {
        "id": "umrti",
        "label": "Úmrtí",
        "prompt": "Zjisti jméno zesnulého, kde se úmrtí stalo, jestli je tělo v nemocnici nebo doma, kontakt a čas, kdy se ozvat. Slib, že pracovník pohřební služby zavolá do 15 minut. Otázky na pomník nebo jeho cenu tady neřeš - řekni jen, že to s pozůstalými probere kolega, nic nevysvětluj a vrať se k úmrtí. Pomník sám nezmiňuj.",
        "enterPhrase": "Upřímnou soustrast.",
        "enterBehavior": "wait"
      },
      {
        "id": "vykop",
        "label": "Výkop a uložení",
        "prompt": "Volající řeší výkop hrobu, uložení urny nebo otevření hrobky. Zjisti termín, hřbitov a číslo hrobu, kontakt a jestli je potřeba domluvit s duchovním."
      },
      {
        "id": "poptavka",
        "label": "Poptávka práce",
        "prompt": "Volající poptává novou práci na pomníku - nápis, opravu nebo rekonstrukci hrobu. Zjisti hřbitov, číslo hrobu, rozsah práce a kontakt na zpětné volání s cenou."
      }
    ],
    "edges": [
      {
        "id": "triaz_umrti",
        "from": "triaz",
        "to": "umrti",
        "condition": "Volající oznamuje úmrtí blízké osoby nebo potřebuje pohřební službu."
      },
      {
        "id": "triaz_vykop",
        "from": "triaz",
        "to": "vykop",
        "condition": "Volající potřebuje výkop hrobu, uložení urny nebo otevření hrobky."
      },
      {
        "id": "triaz_poptavka",
        "from": "triaz",
        "to": "poptavka",
        "condition": "Volající poptává novou práci: pomník, nápis, opravu nebo rekonstrukci hrobu."
      },
      {
        "id": "vykop_umrti",
        "from": "vykop",
        "to": "umrti",
        "condition": "Volající během hovoru oznámí úmrtí blízké osoby."
      },
      {
        "id": "poptavka_umrti",
        "from": "poptavka",
        "to": "umrti",
        "condition": "Volající během hovoru oznámí úmrtí blízké osoby."
      }
    ]
  }
}

Hovor, který fázemi prošel NA VLASTNÍM MOTORU VOLAI (provider: "engine"), nese v GET /v1/calls/{id} (i ve výpisu GET /v1/calls) navíc pole workflowPath: pole kroků {node, label, viaEdge, turnIndex, at, enterPhraseSpoken}, at jako unix ms. Vstupní krok má viaEdge: null. Hovor agenta na ElevenLabs pole nikdy nemá, i když fázemi prošel - zapisuje ho jen post-call motoru. Hovor bez fází klíč vůbec nemá.

Koncept agenta (draft)

Koncept (draft) je oddělený rozpracovaný snímek agenta - uložíš si do něj úpravu, prohlédneš si ji, klidně ji vyzkoušíš (simulace níž) a teprve pak ji jedním voláním nasadíš na živého agenta. PUT /v1/agents/{id}/draft NIKDY nesahá na poskytovatele hlasu (ElevenLabs ani vlastní motor volai) ani na telefonii - jen uloží koncept. POST /v1/agents/{id}/draft/publish ano - je to jediný krok konceptu, který živého agenta doopravdy mění.

Naproti tomu PATCH /v1/agents/{id} (sekce Agenti výš) sahá na poskytovatele SÁM A HNED, bez konceptu a bez mezikroku.

Publikace může vrátit kteroukoli chybu PATCH /v1/agents/{id} - je to týž zápis k poskytovateli.

Koncept, nebo PATCH?

PATCH - jsi jediný editor agenta a změna má být hned živá. Koncept - agenta souběžně edituje i člověk v portálu (portálový editor jede VÝHRADNĚ přes koncept, nikdy přes PATCH), chceš změnu zkontrolovat nebo vyzkoušet (simulace) před nasazením, nebo potřebuješ optimistickou souběžnost (expectedRevision ochrání před přepsáním cizí rozpracované změny).

Co se stane, když koncept obejdeš

Zámek na agentovi je SPOLEČNÝ pro PATCH /v1/agents/{id} a POST .../draft/publish - PATCH během probíhající publikace vrátí 409 in_progress. PATCH mimo probíhající publikaci ale KLIDNĚ projde: zvýší revision o 1, přepíše published na novou živou hodnotu, smaže marker tested (dřívější simulace už neplatí pro novou konfiguraci) a draft nechá beze změny. Rozpracovaný koncept (typicky člověk v portálu) pak při dalším uložení narazí na 409 revision_conflict - to je ZÁMĚR, ne chyba: konfliktní PATCH se nesmí ztratit v tichosti.

Editor agenta v portálu (/agent/{id}) jede VÝHRADNĚ přes koncept - i tam, kde majitel jen upraví jedno pole, portál interně volá save_agent_draft + publish_agent_draft.

Pole snímku konceptu

draft a published mají v každé odpovědi STEJNÝ tvar - AgentDraftSnapshot, kompletní snímek konfigurace agenta. Tělo PUT/publish je .strict() - na rozdíl od PATCH /v1/agents, který neznámý klíč tiše zahodí, tady neznámý klíč vrátí 400 validation. U pole workflow se při ukládání konceptu ověřuje JEN tvar JSONu - pravidla grafu (dosažitelnost fází, existující nástroje, limity na fázi) kontroluje až publikace a PATCH /v1/agents/{id}, takže koncept se uloží i s grafem, který publikace odmítne chybou invalid_workflow. Dvě výjimky z "kompletní snímek pokaždé" existují: chybějící silencePromptSecs ve snímku znamená BEZE ZMĚNY, ne vypnuto a ne výchozích 10 - u agenta na ElevenLabs nese uloženou hodnotu, která zazní až po přepnutí na vlastní motor volai. Stejně tak chybějící numberE164 znamená BEZE ZMĚNY - publikace nechá číslo živého agenta být; teprve null ho odpojí a řetězec připojí uvedené číslo, stejná trojice významů jako u PATCH /v1/agents/{id}. Za "beze změny" se počítá i řetězec, který se shoduje se ŽIVÝM číslem agenta v tu chvíli (ne s uloženou published verzí) - taková hodnota se chová jako chybějící klíč. Tohle se vyhodnocuje znovu při KAŽDÉM čtení konceptu proti tehdy aktuálně živému číslu, takže to není trvalé rozhodnutí: přesune-li se číslo později na jiného agenta mimo tenhle koncept, stejná uložená hodnota se stane výslovnou volbou a další publikace nebo rollback konceptu ho tomuhle agentovi vrátí zpátky - ne potichu, ale s varováním number_changed_by_publish ve warnings.

PolePoznámka
name, language, systemPrompt, providername, language, systemPrompt a provider jsou POVINNÉ - koncept je úplný snímek, ne dílčí patch.
providerprovider ("engine" nebo "elevenlabs") musí přesně odpovídat aktuálnímu provider agenta, jinak invalid_draft - u POST/PATCH /v1/agents se naopak vůbec neposílá (server ho odvozuje sám). Je to jediné pole, které koncept nese navíc oproti běžnému agentovi.
toolIds, dataFieldstoolIds a dataFields mají strop 10 položek každé - stejný jako u POST /v1/agents a PATCH /v1/agents/{id} (viz tabulku Nová pole agenta výš).

Zbylá pole (firstMessage, voiceId, transferTo, transferCondition, recordCalls, goal, knowledge, notifyEmail) mají stejný význam a meze jako u POST/PATCH /v1/agents - viz tabulku Nová pole agenta výš.

operation: co agenta právě blokuje

operation (u GET .../draft i GET .../draft/operation) je null, když agenta nic neblokuje, jinak objekt { id, kind, status, expectedRevision, createdAt, updatedAt }. Hodnoty kind a status jsou zúžené na to, co potřebuješ k rozhodnutí - interní detaily (kdo přesně operaci spustil, jaký byl přesný text chyby poskytovatele) ven nejdou.

kindPoznámka
updateŽivá úprava přes PATCH /v1/agents/{id} (mimo koncept).
publishPublikace uloženého konceptu (POST .../draft/publish).
rollbackNávrat na dřívější publikovanou revizi z historie (POST .../draft/rollback) - stejně jako publish sahá na poskytovatele a telefonii.
outbound_taskDávkové vytáčení (odchozí kampaň) - agenta právě používá jiná dávková úloha; publikace/PATCH počká, až doběhne.
statusPoznámka
runningOperace právě běží u poskytovatele.
uncertainPoskytovatel mohl, ale nemusel změnu provést - zkontroluj živého agenta a operaci NEOPAKUJ naslepo (viz POST .../draft/operation níž).
driftŽivá konfigurace u poskytovatele se liší od toho, co si myslíme, že jsme poslali - vyžaduje ruční rozhodnutí.
failedOperace selhala jistě - koncept zůstal beze změny, klidně zkus znovu.
doneOperace doběhla úspěšně (publikace, potvrzený návrat, nebo bezpečně vyřešená nejistota).

tested (GET .../draft i odpověď POST .../simulate) je { revision, testedAt } | null - kdy byla naposled otestovaná PRÁVĚ TATO revize konceptu simulací. Jakákoli další úprava konceptu (PUT) marker smaže; simulace NENÍ podmínkou publikace, je to jen dobrovolná kontrola formulace před nasazením.

history je seznam { revision, publishedAt } posledních publikací (bez uložených snímků konfigurace) - kompletní historii včetně obsahu má jen portál.

GET/v1/agents/{id}/draft

Přečte koncept, poslední publikovanou konfiguraci, revizi (pro expectedRevision dalších volání), poslední výsledek simulace (tested) a to, jestli agenta právě něco blokuje (operation).

Požadavek

bash
curl https://volai.cz/v1/agents/ag_kx91fa2b/draft \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "draft": {
    "name": "Recepční",
    "language": "cs",
    "systemPrompt": "Jsi recepční kavárny Nula. Přijímáš objednávky na vyzvednutí...",
    "firstMessage": "Dobrý den, kavárna Nula, co si dáte?",
    "voiceId": "milena",
    "numberE164": "+420601234567",
    "provider": "engine"
  },
  "published": {
    "name": "Recepční",
    "language": "cs",
    "systemPrompt": "Jsi recepční kavárny Nula. Přijímáš objednávky na vyzvednutí...",
    "firstMessage": "Dobrý den, tady recepce kavárny Nula, jak vám mohu pomoct?",
    "voiceId": "milena",
    "numberE164": "+420601234567",
    "provider": "engine"
  },
  "revision": 4,
  "tested": {
    "revision": 4,
    "testedAt": 1756118920000
  },
  "history": [
    {
      "revision": 3,
      "publishedAt": 1756118840000
    },
    {
      "revision": 2,
      "publishedAt": 1756112640000
    }
  ],
  "operation": null
}

Chybové stavy

  • 403forbiddenSdílený demo agent volai je jen pro čtení.
  • 404agent_not_foundAgent neexistuje, byl smazaný, nebo nepatří tvému účtu.
  • 500storage_failedUložení nebo načtení konceptu selhalo na naší straně - zkus to prosím znovu.

PUT/v1/agents/{id}/draft

Uloží koncept podle expectedRevision. Tělo posílej PŘESNĚ jako objekt, který jsi přečetl z GET, s jednou provedenou změnou - tělo je .strict(), takže i drobný neznámý klíč vrátí 400. Uložení NIKDY nemění běžícího agenta. Ukázka GET .../draft výš zobrazuje stav PO tomhle PUT (revize 3 -> 4): proto tenhle PUT posílá expectedRevision: 3 (revize, na které úprava stavěla) a GET výš už vrací revision: 4 (novou revizi po uložení).

Požadavek

bash
curl -X PUT "https://volai.cz/v1/agents/ag_kx91fa2b/draft" \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"expectedRevision":3,"draft":{"name":"Recepční","language":"cs","systemPrompt":"Jsi recepční kavárny Nula. Přijímáš objednávky na vyzvednutí...","firstMessage":"Dobrý den, kavárna Nula, co si dáte?","voiceId":"milena","numberE164":"+420601234567","provider":"engine"}}'

Odpověď

json
{
  "draft": {
    "name": "Recepční",
    "language": "cs",
    "systemPrompt": "Jsi recepční kavárny Nula. Přijímáš objednávky na vyzvednutí...",
    "firstMessage": "Dobrý den, kavárna Nula, co si dáte?",
    "voiceId": "milena",
    "numberE164": "+420601234567",
    "provider": "engine"
  },
  "revision": 4
}

Chybové stavy

  • 400validationNeplatný tvar těla - viz meze u konkrétního pole.
  • 400invalid_draftprovider v konceptu neodpovídá provider agenta - koncept se dá upravovat jen v mezích téhož poskytovatele.
  • 403forbiddenSdílený demo agent volai je jen pro čtení.
  • 404agent_not_foundAgent neexistuje, byl smazaný, nebo nepatří tvému účtu.
  • 409revision_conflictKoncept mezitím upravil (nebo PATCHem obešel) někdo jiný. Chyba nese currentRevision - načti aktuální stav přes GET .../draft a zkontroluj rozdíl, než uložení zopakuješ.
  • 409publish_in_progressNa agentovi právě běží jiná publikace nebo dávková úloha - zkus to znovu za chvíli.
  • 409operation_uncertainNa agentovi visí nejistá operace z předchozího pokusu - zkontroluj živého agenta a vyřeš ji přes GET/POST .../draft/operation, než zkusíš znovu.
  • 500storage_failedUložení nebo načtení konceptu selhalo na naší straně - zkus to prosím znovu.

POST/v1/agents/{id}/draft/publish

Nasadí uložený koncept na živého agenta - jediný krok konceptu, který sahá na poskytovatele (ElevenLabs nebo motor volai) a telefonii. Bere Idempotency-Key (viz sekci Idempotence výš) - druhé volání se stejným klíčem do 24 hodin vrátí přesně tu samou uloženou odpověď, ať byla úspěšná, nebo operation_uncertain.

Chyby z nasazení (deploy) se propagují BEZE ZMĚNY - publikace dědí CELOU chybovou tabulku PATCH /v1/agents/{id} výš (in_progress, engine_unavailable, engine_not_configured, engine_readback_mismatch a validační kódy), plus vlastní tabulku konceptu výš.

Požadavek

bash
curl -X POST "https://volai.cz/v1/agents/ag_kx91fa2b/draft/publish" \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: REPLACE_WITH_UNIQUE_POST_V1_AGENTS_AG_KX91FA2B_DRAFT_PUBLISH_ID" \
  -d '{"expectedRevision":4}'

Před spuštěním nahraď REPLACE_WITH_UNIQUE… jedinečným ID této akce, například UUID, a ulož si ho. Při opakování stejného požadavku po timeoutu použij stejné ID. Pro jinou akci nebo změněné tělo použij nové ID, i když voláš jiný endpoint.

Odpověď

json
{
  "revision": 5,
  "published": {
    "name": "Recepční",
    "language": "cs",
    "systemPrompt": "Jsi recepční kavárny Nula. Přijímáš objednávky na vyzvednutí...",
    "firstMessage": "Dobrý den, kavárna Nula, co si dáte?",
    "voiceId": "milena",
    "numberE164": "+420601234567",
    "provider": "engine"
  }
}

Chybové stavy

  • 400validationNeplatný tvar těla - viz meze u konkrétního pole.
  • 400invalid_draftprovider v konceptu neodpovídá provider agenta - koncept se dá upravovat jen v mezích téhož poskytovatele.
  • 403forbiddenSdílený demo agent volai je jen pro čtení.
  • 404agent_not_foundAgent neexistuje, byl smazaný, nebo nepatří tvému účtu.
  • 409revision_conflictKoncept mezitím upravil (nebo PATCHem obešel) někdo jiný. Chyba nese currentRevision - načti aktuální stav přes GET .../draft a zkontroluj rozdíl, než uložení zopakuješ.
  • 409publish_in_progressNa agentovi právě běží jiná publikace nebo dávková úloha - zkus to znovu za chvíli.
  • 409operation_uncertainNa agentovi visí nejistá operace z předchozího pokusu - zkontroluj živého agenta a vyřeš ji přes GET/POST .../draft/operation, než zkusíš znovu.
  • 409idempotency_in_progressSouběžné volání se stejným Idempotency-Key ještě běží - zkus to prosím za chvíli znovu.
  • 500storage_failedUložení nebo načtení konceptu selhalo na naší straně - zkus to prosím znovu.
  • 503engine_not_configuredU agenta na vlastním motoru volai: motor je momentálně nenakonfigurovaný na naší straně, publikace se neuložila. Napiš prosím podpoře.
  • 502engine_unavailableU agenta na vlastním motoru volai: motor teď neodpovídá, publikace se neuložila. Zkus to prosím znovu za chvíli.
  • 502engine_readback_mismatchU agenta na vlastním motoru volai: motor uložil jinou konfiguraci, než jsme poslali, publikace se neuložila. Zkus to znovu.
  • 409in_progressBuď na agentovi právě probíhá jiná změna (živý PATCH nebo jiná publikace), nebo předchozí změna proběhla, ale nešla bezpečně potvrdit - než publikaci zopakuješ, přečti GET /v1/agents/{id}/draft/operation a rozhodni podle operation.

POST/v1/agents/{id}/draft/rollback

Vrátí agenta na dřívější PUBLIKOVANOU revizi z historie (targetRevision, viz pole history u GET .../draft) a rovnou ji nasadí poskytovateli - je to publikace, ne jen přepsání konceptu. expectedRevision chrání proti souběžné úpravě stejně jako u PUT .../draft. Na rozdíl od .../draft/publish BEZ Idempotency-Key - opakování se stejným tělem je bezpečné samo o sobě.

Sdílí přesně stejné dva zdroje chyb jako publikace výš: vlastní chybovou tabulku konceptu (validation, invalid_draft, forbidden, agent_not_found, revision_conflict, publish_in_progress, operation_uncertain, storage_failed) a CELOU chybovou tabulku PATCH /v1/agents/{id} (nasazení sahá na poskytovatele stejně jako u PATCH/publikace).

Požadavek

bash
curl -X POST "https://volai.cz/v1/agents/ag_kx91fa2b/draft/rollback" \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"expectedRevision":4,"targetRevision":2}'

Odpověď

json
{
  "revision": 5,
  "published": {
    "name": "Recepční",
    "language": "cs",
    "systemPrompt": "Jsi recepční kavárny Nula. Přijímáš objednávky na vyzvednutí...",
    "firstMessage": "Dobrý den, kavárna Nula, co si dáte?",
    "voiceId": "milena",
    "numberE164": "+420601234567",
    "provider": "engine"
  }
}

Chybové stavy

  • 400validationNeplatný tvar těla - viz meze u konkrétního pole.
  • 400invalid_drafttargetRevision už není mezi uloženými historickými revizemi (history u GET .../draft ji nenese).
  • 403forbiddenSdílený demo agent volai je jen pro čtení.
  • 404agent_not_foundAgent neexistuje, byl smazaný, nebo nepatří tvému účtu.
  • 409revision_conflictKoncept mezitím upravil (nebo PATCHem obešel) někdo jiný. Chyba nese currentRevision - načti aktuální stav přes GET .../draft a zkontroluj rozdíl, než uložení zopakuješ.
  • 409publish_in_progressNa agentovi právě běží jiná publikace nebo dávková úloha - zkus to znovu za chvíli.
  • 409operation_uncertainNa agentovi visí nejistá operace z předchozího pokusu - zkontroluj živého agenta a vyřeš ji přes GET/POST .../draft/operation, než zkusíš znovu.
  • 500storage_failedUložení nebo načtení konceptu selhalo na naší straně - zkus to prosím znovu.
  • 503engine_not_configuredU agenta na vlastním motoru volai: motor je momentálně nenakonfigurovaný na naší straně, publikace se neuložila. Napiš prosím podpoře.
  • 502engine_unavailableU agenta na vlastním motoru volai: motor teď neodpovídá, publikace se neuložila. Zkus to prosím znovu za chvíli.
  • 502engine_readback_mismatchU agenta na vlastním motoru volai: motor uložil jinou konfiguraci, než jsme poslali, publikace se neuložila. Zkus to znovu.
  • 409in_progressBuď na agentovi právě probíhá jiná změna (živý PATCH nebo jiná publikace), nebo předchozí změna proběhla, ale nešla bezpečně potvrdit - než publikaci zopakuješ, přečti GET /v1/agents/{id}/draft/operation a rozhodni podle operation.

GET/v1/agents/{id}/draft/operation

Přečte aktuální stav blokující operace (null, když agenta nic neblokuje) - stejný tvar jako operation u GET .../draft.

Požadavek

bash
curl https://volai.cz/v1/agents/ag_kx91fa2b/draft/operation \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "operation": {
    "id": "ado_4f2a91cd",
    "kind": "publish",
    "status": "uncertain",
    "expectedRevision": 4,
    "createdAt": 1756118900000,
    "updatedAt": 1756118905000
  }
}

Chybové stavy

  • 403forbiddenSdílený demo agent volai je jen pro čtení.
  • 404agent_not_foundAgent neexistuje, byl smazaný, nebo nepatří tvému účtu.
  • 500storage_failedUložení nebo načtení konceptu selhalo na naší straně - zkus to prosím znovu.

POST/v1/agents/{id}/draft/operation

Po ruční kontrole živého agenta bezpečně uzavře nejistou (uncertain) operaci - potvrdíš, že jsi stav zkontroloval, a server ověří poskytovatele (readback) a operaci uzavře.

Požadavek

bash
curl -X POST https://volai.cz/v1/agents/ag_kx91fa2b/draft/operation \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"operationId":"ado_4f2a91cd","decision":"acknowledge_live_state"}'

Odpověď

json
{
  "reconciled": true,
  "revision": 5
}

Chybové stavy

  • 400validationoperationId chybí, nebo decision není "acknowledge_live_state".
  • 403forbiddenSdílený demo agent volai je jen pro čtení.
  • 404agent_not_foundAgent neexistuje, byl smazaný, nebo nepatří tvému účtu.
  • 409revision_conflictKoncept mezitím upravil (nebo PATCHem obešel) někdo jiný. Chyba nese currentRevision - načti aktuální stav přes GET .../draft a zkontroluj rozdíl, než uložení zopakuješ.
  • 409operation_uncertainNa agentovi visí nejistá operace z předchozího pokusu - zkontroluj živého agenta a vyřeš ji přes GET/POST .../draft/operation, než zkusíš znovu.
  • 500storage_failedUložení nebo načtení konceptu selhalo na naší straně - zkus to prosím znovu.

POST/v1/agents/{id}/simulate

Pošle textovou zprávu proti ULOŽENÉMU konceptu (ne živému agentovi) a nastaví marker tested. Zdarma (0 Kč, platí volai), NENÍ podmínkou publikace, limit 20 za hodinu na účet, běží na pevném modelu Anthropic (jméno se ven neposílá), BEZ nástrojů a BEZ hlasu (ASR/TTS) - je to kontrola formulace promptu, ne test skutečného hovoru.

messages (nepovinné) jsou předchozí tahy konverzace BEZ aktuální zprávy - { role: "user" | "assistant", content }. Meze: message 1 až 4000 znaků, nejvýš 8 zpráv historie, 2000 znaků na historickou zprávu, 8000 znaků historie celkem.

Požadavek

bash
curl -X POST https://volai.cz/v1/agents/ag_kx91fa2b/simulate \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"expectedRevision":4,"message":"Dobry den"}'

Odpověď

json
{
  "revision": 4,
  "text": "Dobrý den, kavárna Nula, co si dáte?",
  "usage": {
    "inputTokens": 412,
    "outputTokens": 58
  },
  "tested": {
    "revision": 4,
    "testedAt": 1756118920000
  }
}

Chybové stavy

  • 400validationNeplatný tvar těla - viz meze u konkrétního pole.
  • 403forbiddenSdílený demo agent volai je jen pro čtení.
  • 404agent_not_foundAgent neexistuje, byl smazaný, nebo nepatří tvému účtu.
  • 409revision_conflictKoncept mezitím upravil (nebo PATCHem obešel) někdo jiný. Chyba nese currentRevision - načti aktuální stav přes GET .../draft a zkontroluj rozdíl, než uložení zopakuješ.
  • 429rate_limitedVyčerpal jsi limit 20 simulací za hodinu na účet.
  • 502simulation_failedModel Anthropic vrátil chybu nebo neplatnou odpověď - zkus to prosím znovu.
  • 503simulation_not_configuredSimulace je na naší straně dočasně nenakonfigurovaná - napiš prosím podpoře.
  • 503simulation_unavailableKvótu simulace se nepodařilo ověřit (dočasný výpadek Redisu) - zkus to prosím znovu.
  • 409publish_in_progressSimulace proběhla v pořádku, ale zápis markeru tested narazil na právě běžící publikaci konceptu - zkus to znovu za chvíli.
  • 409operation_uncertainSimulace proběhla v pořádku, ale zápis markeru tested narazil na nejistou operaci z předchozího pokusu - vyřeš ji přes GET/POST .../draft/operation, než to zkusíš znovu.
  • 500storage_failedSimulace proběhla v pořádku, ale zápis markeru tested selhal na naší straně - zkus to prosím znovu.

Hlasy

Katalog hlasů pro voiceId u POST/PATCH /v1/agents. Platformu říká provider; prvotní volbu určuje useForOutboundTasks. U existujícího agenta na motoru přijme PATCH každý katalogový hlas KROMĚ hlasů ElevenLabs (katty, starší anet); katty a syrové ElevenLabs ID jsou platné jen na ElevenLabs (anet tam zůstává jen agentům, kteří ho už mají). POST má jednu výjimku: explicitní true nahradí známý hlas nepodporovaný motorem, včetně katty, jeho výchozím hlasem. Pokud motor zvolí výchozí nastavení při vynechaném poli, stejná hodnota vrátí 400; syrové ElevenLabs ID motor odmítne vždy. Filtr ?provider=engine vrátí hlasy motoru, ?provider=elevenlabs hlasy ElevenLabs. Neznámá hodnota vrátí 400 validation a vyjmenuje platné hodnoty. Stejný katalog v tabulce s popisem každého hlasu je na stránce Hlasový agent.

GET/v1/voices

Katalog hlasů dostupných pro voiceId u agentů.

Požadavek

bash
curl https://volai.cz/v1/voices \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "voices": [
    {
      "id": "milena",
      "name": "Milena",
      "gender": "female",
      "provider": "cartesia",
      "tone": "calm, warm",
      "description": "The default volai voice - calm and natural, a good fit for most agents.",
      "languages": [
        "cs"
      ],
      "preview": {
        "studio": "https://volai.cz/hlasy/milena-studio.mp3",
        "phone": "https://volai.cz/hlasy/milena-telefon.mp3"
      },
      "default": true
    },
    {
      "id": "jan",
      "name": "Jan",
      "gender": "male",
      "provider": "cartesia",
      "tone": "calm, matter-of-fact",
      "description": "The male counterpart to Milena - matter-of-fact, easy-to-follow delivery.",
      "languages": [
        "cs"
      ],
      "preview": {
        "studio": "https://volai.cz/hlasy/jan-studio.mp3",
        "phone": "https://volai.cz/hlasy/jan-telefon.mp3"
      },
      "default": false
    },
    {
      "id": "tereza",
      "name": "Tereza",
      "gender": "female",
      "provider": "cartesia",
      "tone": "professional, structured",
      "description": "The second female voice - professional, well-structured delivery.",
      "languages": [
        "cs"
      ],
      "preview": {
        "studio": "https://volai.cz/hlasy/tereza-studio.mp3",
        "phone": "https://volai.cz/hlasy/tereza-telefon.mp3"
      },
      "default": false
    },
    {
      "id": "marek",
      "name": "Marek",
      "gender": "male",
      "provider": "cartesia",
      "tone": "calm, resonant",
      "description": "The second male voice - calm, resonant delivery for longer calls.",
      "languages": [
        "cs"
      ],
      "preview": {
        "studio": "https://volai.cz/hlasy/marek-studio.mp3",
        "phone": "https://volai.cz/hlasy/marek-telefon.mp3"
      },
      "default": false
    },
    {
      "id": "katarina",
      "name": "Katarina",
      "gender": "female",
      "provider": "cartesia",
      "tone": "calm, natural",
      "description": "Slovak female voice - calm, natural delivery.",
      "languages": [
        "sk"
      ],
      "preview": {
        "studio": "https://volai.cz/hlasy/katarina-studio.mp3",
        "phone": "https://volai.cz/hlasy/katarina-telefon.mp3"
      },
      "default": false
    },
    {
      "id": "peter",
      "name": "Peter",
      "gender": "male",
      "provider": "cartesia",
      "tone": "matter-of-fact, clear",
      "description": "Slovak male voice - matter-of-fact, easy-to-follow delivery.",
      "languages": [
        "sk"
      ],
      "preview": {
        "studio": "https://volai.cz/hlasy/peter-studio.mp3",
        "phone": "https://volai.cz/hlasy/peter-telefon.mp3"
      },
      "default": false
    },
    {
      "id": "skylar",
      "name": "Skylar",
      "gender": "female",
      "provider": "cartesia",
      "tone": "calm, friendly",
      "description": "English female voice - calm, friendly delivery.",
      "languages": [
        "en"
      ],
      "preview": {
        "studio": "https://volai.cz/hlasy/skylar-studio.mp3",
        "phone": "https://volai.cz/hlasy/skylar-telefon.mp3"
      },
      "default": false
    },
    {
      "id": "daniel",
      "name": "Daniel",
      "gender": "male",
      "provider": "cartesia",
      "tone": "matter-of-fact, confident",
      "description": "English male voice - matter-of-fact, confident delivery.",
      "languages": [
        "en"
      ],
      "preview": {
        "studio": "https://volai.cz/hlasy/daniel-studio.mp3",
        "phone": "https://volai.cz/hlasy/daniel-telefon.mp3"
      },
      "default": false
    },
    {
      "id": "alina",
      "name": "Alina",
      "gender": "female",
      "provider": "cartesia",
      "tone": "calm, warm",
      "description": "German female voice - calm, warm delivery.",
      "languages": [
        "de"
      ],
      "preview": {
        "studio": "https://volai.cz/hlasy/alina-studio.mp3",
        "phone": "https://volai.cz/hlasy/alina-telefon.mp3"
      },
      "default": false
    },
    {
      "id": "lukas",
      "name": "Lukas",
      "gender": "male",
      "provider": "cartesia",
      "tone": "matter-of-fact, clear",
      "description": "German male voice - matter-of-fact, easy-to-follow delivery.",
      "languages": [
        "de"
      ],
      "preview": {
        "studio": "https://volai.cz/hlasy/lukas-studio.mp3",
        "phone": "https://volai.cz/hlasy/lukas-telefon.mp3"
      },
      "default": false
    },
    {
      "id": "ewa",
      "name": "Ewa",
      "gender": "female",
      "provider": "cartesia",
      "tone": "calm, natural",
      "description": "Polish female voice - calm, natural delivery.",
      "languages": [
        "pl"
      ],
      "preview": {
        "studio": "https://volai.cz/hlasy/ewa-studio.mp3",
        "phone": "https://volai.cz/hlasy/ewa-telefon.mp3"
      },
      "default": false
    },
    {
      "id": "kacper",
      "name": "Kacper",
      "gender": "male",
      "provider": "cartesia",
      "tone": "matter-of-fact, confident",
      "description": "Polish male voice - matter-of-fact, confident delivery.",
      "languages": [
        "pl"
      ],
      "preview": {
        "studio": "https://volai.cz/hlasy/kacper-studio.mp3",
        "phone": "https://volai.cz/hlasy/kacper-telefon.mp3"
      },
      "default": false
    }
  ]
}

Chybové stavy

  • 400validationprovider není jedna z hodnot: elevenlabs, engine.

Pole tone popisuje povahu hlasu (klidný, věcný); languages obsahuje kódy jazyků, ve kterých hlas mluví - cs: Milena, Jan, Tereza, Marek; sk: Katarina, Peter; en: Skylar, Daniel; de: Alina, Lukas; pl: Ewa, Kacper; preview nese URL studiové a telefonní ukázky; default značí doporučenou položku katalogu - v portálu nese jen štítek 'Výchozí' na kartě a sama o sobě nic nepředvybírá. Nový agent založený bez voiceId na ni nepřechází - zdědí výchozí hlas svého poskytovatele (u motoru Milenu, u ElevenLabs hlas šablony, viz sekci Agenti výš).

Nástroje agenta

Nástroj je adresa tvého API, kterou agent zavolá uprostřed hovoru (ověřit objednávku, zapsat rezervaci) a odpověď použije v řeči. Nástroj patří účtu, agentovi se přiřadí přes toolIds - samo vytvoření nástroje tedy žádného agenta nezmění.

Limity: nejvýš 10 nástrojů na účet, 10 parametrů a 5 hlaviček na nástroj, timeoutSecs 5 až 30 (výchozí 20). Adresa musí být https a veřejná - vnitřní sítě a loopback odmítáme (private_address), protože jinak by šlo naším serverem oťukávat cizí infrastrukturu.

GET/v1/tools

Seznam webhook nástrojů na účtu. Hodnoty hlaviček jsou vždy maskované.

Požadavek

bash
curl https://volai.cz/v1/tools \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "tools": [
    {
      "id": "tl_5c2a91f4",
      "name": "overit_objednavku",
      "label": "Ověřit objednávku",
      "description": "Zjistí stav objednávky podle čísla. Použij, když se volající ptá, kde je jeho objednávka.",
      "url": "https://api.tvojeappka.cz/objednavky",
      "method": "POST",
      "headers": { "Authorization": "Bear***" },
      "params": [
        {
          "name": "cislo_objednavky",
          "type": "string",
          "source": "llm",
          "description": "Číslo objednávky, které volající nadiktoval.",
          "required": true
        },
        { "name": "telefon", "type": "string", "source": "caller_number" },
        { "name": "zdroj", "type": "string", "source": "constant", "constantValue": "telefon" }
      ],
      "timeoutSecs": 20,
      "createdAt": 1756111000000,
      "updatedAt": 1756111000000
    }
  ]
}

POST/v1/tools

Vytvoří nástroj. label je čitelný název, jméno pro model se z něj odvodí samo (Ověřit objednávku -> overit_objednavku) a vrátí se v poli name. description je jediné, podle čeho se model rozhodne, KDY nástroj použít - piš ho jako pokyn („Použij, když se volající ptá, kde je jeho objednávka.“), 10 až 1000 znaků.

Požadavek

bash
curl -X POST https://volai.cz/v1/tools \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Ověřit objednávku",
    "description": "Zjistí stav objednávky podle čísla. Použij, když se volající ptá, kde je jeho objednávka.",
    "url": "https://api.tvojeappka.cz/objednavky",
    "method": "POST",
    "headers": { "Authorization": "Bearer tvuj_klic" },
    "params": [
      {
        "name": "cislo_objednavky",
        "type": "string",
        "source": "llm",
        "description": "Číslo objednávky, které volající nadiktoval.",
        "required": true
      },
      { "name": "telefon", "type": "string", "source": "caller_number" },
      { "name": "zdroj", "type": "string", "source": "constant", "constantValue": "telefon" }
    ],
    "timeoutSecs": 20
  }'

Odpověď

json
{
  "tool": {
    "id": "tl_5c2a91f4",
    "name": "overit_objednavku",
    "label": "Ověřit objednávku",
    "description": "Zjistí stav objednávky podle čísla. Použij, když se volající ptá, kde je jeho objednávka.",
    "url": "https://api.tvojeappka.cz/objednavky",
    "method": "POST",
    "headers": { "Authorization": "Bear***" },
    "params": [
      {
        "name": "cislo_objednavky",
        "type": "string",
        "source": "llm",
        "description": "Číslo objednávky, které volající nadiktoval.",
        "required": true
      },
      { "name": "telefon", "type": "string", "source": "caller_number" },
      { "name": "zdroj", "type": "string", "source": "constant", "constantValue": "telefon" }
    ],
    "timeoutSecs": 20,
    "createdAt": 1756111000000,
    "updatedAt": 1756111000000
  }
}

Chybové stavy

  • 400tool_limitNa účtu už je 10 nástrojů.
  • 400invalid_tool_namelabel musí mít 1 až 60 znaků, nebo by z názvu vzniklo rezervované jméno (end_call, transfer_to_human, switch_to_*).
  • 400invalid_tool_urlurl není platná adresa, nebo není https.
  • 400private_addressurl míří do vnitřní sítě nebo na loopback.
  • 400invalid_tool_paramsParametr má neplatné jméno, typ, zdroj, nebo je jich víc než 10.
  • 400invalid_tool_headersHlavička je zakázaná (host, content-length, x-conversation-id, x-caller-id), duplicitní, prázdná, nebo je jich víc než 5.
  • 400validationdescription mimo 10 až 1000 znaků, timeoutSecs mimo 5 až 30, nebo method jiná než GET/POST.
  • 502eleven_labs_errorNástroj se nepodařilo založit u hlasové platformy - lokálně jsme nic neuložili, zkus to znovu.

GET/v1/tools/{id}

Detail jednoho nástroje - stejná pole jako ve výpisu, plus agents: pole agentů, kteří ho mají zapnutý přes toolIds (jen id a name, celého agenta si dotáhneš přes GET /v1/agents/{id}).

Požadavek

bash
curl https://volai.cz/v1/tools/tl_5c2a91f4 \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "tool": {
    "id": "tl_5c2a91f4",
    "name": "overit_objednavku",
    "label": "Ověřit objednávku",
    "description": "Zjistí stav objednávky podle čísla. Použij, když se volající ptá, kde je jeho objednávka.",
    "url": "https://api.tvojeappka.cz/objednavky",
    "method": "POST",
    "headers": { "Authorization": "Bear***" },
    "params": [
      {
        "name": "cislo_objednavky",
        "type": "string",
        "source": "llm",
        "description": "Číslo objednávky, které volající nadiktoval.",
        "required": true
      },
      { "name": "telefon", "type": "string", "source": "caller_number" },
      { "name": "zdroj", "type": "string", "source": "constant", "constantValue": "telefon" }
    ],
    "timeoutSecs": 20,
    "createdAt": 1756111000000,
    "updatedAt": 1756111000000,
    "agents": [{ "id": "ag_kx91fa2b", "name": "Recepční" }]
  }
}

Chybové stavy

  • 404tool_not_foundNástroj neexistuje nebo nepatří tvému účtu.

PATCH/v1/tools/{id}

Upraví nástroj - všechna pole nepovinná, mění se jen ta zaslaná. Pozor na dvě věci: params i headers nahrazují celý dosavadní seznam, a změna label přepíše i jméno, kterým nástroj zná model - když se na něj odkazuješ v systemPrompt, uprav ho zároveň.

Hlavičky chodí ven jen maskované ("Bear***"). Když pošleš zpátky přesně tu maskovanou hodnotu, bereme to jako „nech původní“ - běžný postup „načti nástroj, změň jedno pole, pošli celý objekt zpět“ ti tedy přihlašovací údaje nezničí. Skutečnou změnu hodnoty pošli v plném znění.

Požadavek

bash
curl -X PATCH https://volai.cz/v1/tools/tl_5c2a91f4 \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"timeoutSecs": 10}'

Odpověď

json
{
  "tool": {
    "id": "tl_5c2a91f4",
    "name": "overit_objednavku",
    "label": "Ověřit objednávku",
    "description": "Zjistí stav objednávky podle čísla. Použij, když se volající ptá, kde je jeho objednávka.",
    "url": "https://api.tvojeappka.cz/objednavky",
    "method": "POST",
    "headers": { "Authorization": "Bear***" },
    "params": [
      {
        "name": "cislo_objednavky",
        "type": "string",
        "source": "llm",
        "description": "Číslo objednávky, které volající nadiktoval.",
        "required": true
      },
      { "name": "telefon", "type": "string", "source": "caller_number" },
      { "name": "zdroj", "type": "string", "source": "constant", "constantValue": "telefon" }
    ],
    "timeoutSecs": 10,
    "createdAt": 1756111000000,
    "updatedAt": 1756111890000
  }
}

Chybové stavy

  • 400invalid_tool_urlurl není platná adresa, nebo není https.
  • 400private_addressurl míří do vnitřní sítě nebo na loopback.
  • 400invalid_tool_paramsParametr má neplatné jméno, typ nebo zdroj.
  • 400invalid_tool_headersHlavička je zakázaná, duplicitní nebo prázdná.
  • 400validationdescription (je-li poslaný) mimo 10 až 1000 znaků, timeoutSecs mimo 5 až 30, nebo method jiná než GET/POST.
  • 404tool_not_foundNástroj neexistuje nebo nepatří tvému účtu.
  • 502eleven_labs_mismatchZměna se u hlasové platformy neuložila přesně podle zadání - lokálně jsme nic nepřepsali.

DELETE/v1/tools/{id}

Smaže nástroj a vrátí agenty, kterým tím přestal fungovat (jen id a name; celého agenta si dotáhneš přes GET /v1/agents/{id}). Na rozdíl od uvolnění čísla je to vratná ztráta - stejný nástroj jde založit znovu, jen dostane nové id, které je pak potřeba agentům přiřadit.

Požadavek

bash
curl -X DELETE https://volai.cz/v1/tools/tl_5c2a91f4 \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "deleted": true,
  "agents": [
    { "id": "ag_kx91fa2b", "name": "Recepční" }
  ]
}

Chybové stavy

  • 404tool_not_foundNástroj neexistuje nebo nepatří tvému účtu.
  • 502eleven_labs_errorNástroj se nepodařilo smazat u hlasové platformy - zkus to prosím znovu.

POST/v1/tools/test

Odešle SKUTEČNÝ požadavek podle poslané definice a vrátí, co přišlo zpátky - nástroj se NEUKLÁDÁ (na rozdíl od POST /v1/tools, label/description jsou tu nepovinné). Hodí se na vyzkoušení adresy, než ji uložíš jako nástroj agenta. timeoutSecs se tu jen validuje (5 až 30) - zkouška má vždy pevných 10 sekund, ať u ní nečekáš celý runtime timeout; je to hodnota, kterou by nástroj použil při ostrém běhu po uložení. Sdílí rate limit s POST /v1/tools/{id}/test níž - dohromady nejvýš 10 pokusů za minutu na účet.

Požadavek

bash
curl -X POST https://volai.cz/v1/tools/test \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://api.yourapp.com/orders", "method": "GET", "params": [{"name": "order", "type": "string", "source": "llm", "description": "Order number."}]}'

Odpověď

json
{
  "status": 200,
  "durationMs": 214,
  "body": "{\"status\":\"shipped\"}",
  "note": null
}

Chybové stavy

  • 400validationmethod jiná než GET/POST, nebo timeoutSecs mimo 5 až 30.
  • 400invalid_tool_urlurl není platná adresa, nebo síťová/DNS/TLS chyba při samotném odeslání požadavku (kamkoli v téhle fázi - i timeout - se sbalí do stejného kódu).
  • 400private_addressurl míří do vnitřní sítě nebo na loopback (SSRF ochrana) - kontrola proběhne PŘED odesláním.
  • 400invalid_tool_paramsParametr má neplatné jméno, typ, zdroj, nebo je jich víc než 10.
  • 400invalid_tool_headersHlavička je zakázaná, duplicitní, prázdná, nebo je jich víc než 5.
  • 429rate_limitedVyčerpal jsi 10 pokusů za minutu (společně s POST /v1/tools/{id}/test) - zkus to prosím za chvíli.

POST/v1/tools/{id}/test

Zkouší JIŽ ULOŽENÝ nástroj podle id - tělo je vždy prázdné {}, adresu, metodu a SKUTEČNÉ (nemaskované) hlavičky bere endpoint z uloženého záznamu, ne z těla požadavku. private_address/invalid_tool_params/invalid_tool_headers tu nejdou vrátit (uložený záznam znovu neprochází kontrolou vstupu) - síťová chyba se pořád sbalí do invalid_tool_url.

Požadavek

bash
curl -X POST https://volai.cz/v1/tools/tl_5c2a91f4/test \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{}'

Odpověď

json
{
  "status": 200,
  "durationMs": 214,
  "body": "{\"status\":\"shipped\"}",
  "note": null
}

Chybové stavy

  • 400validationTělo požadavku není prázdné - pošli {}, nebo úplně žádné tělo.
  • 400invalid_tool_urlSíťová/DNS/TLS chyba (i timeout) při odeslání skutečného požadavku.
  • 404tool_not_foundNástroj neexistuje nebo nepatří tvému účtu.

Parametry nástroje: pole source

Každý parametr má name, type (string, number, boolean) a source - odkud se vezme hodnota. U GET jdou parametry do query, u POST do JSON těla.

sourceOdkud je hodnotaCo k tomu vyplnit
llmVytáhne ji z hovoru model.description (co přesně tam má dát) a volitelně required.
caller_numberČíslo volajícího v E.164, doplní se samo.Nic. Model tohle pole vůbec nevidí.
called_numberTvoje volané číslo v E.164, doplní se samo.Nic. Model tohle pole vůbec nevidí.
constantPevná hodnota, kterou zadáš ty.constantValue.

Zkušební hovor

„Nech si zavolat od svého agenta" - reálný odchozí hovor přes makeAgentCall, jen s vlastním denním limitem 3 hovory na účet (napříč všemi agenty), aby zkušební volání nešlo zneužít jako obchvat běžného limitu hovorů.

POST/v1/agents/{id}/test-call

Zavolá ti zpátky z tvého vlastního agenta - stejná cena a stejná pravidla jako u POST /v1/calls, žádná sleva ani zvláštní sazba. Agent na vlastním hlasovém motoru volai zavolá i BEZ přiřazeného čísla - jen ze sdílené linky volai, takže volaný uvidí číslo volai, ne tvoje. Vlastní číslo agentovi přiřadíš přes PATCH /v1/numbers/{e164} (routing agent) nebo polem numberE164 u agenta (POST/PATCH /v1/agents), případně v portálu v sekci Čísla.

Požadavek

bash
curl -X POST https://volai.cz/v1/agents/ag_kx91fa2b/test-call \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"to": "+420777123456"}'

Odpověď

json
{
  "id": "c_9d4e2b7f",
  "status": "initiated"
}

Chybové stavy

  • 400validationto chybí nebo přesahuje 32 znaků, nebo tělo není platné JSON.
  • 404agent_not_foundagentId neexistuje nebo nepatří tvému účtu.
  • 404agent_no_numberAgent nemá přiřazené telefonní číslo - tuhle chybu vrací jen agent na ElevenLabs; agent na vlastním motoru volai zavolá i bez čísla ze sdílené linky volai.
  • 403agent_suspendedAgenta ručně pozastavil provozovatel volai - zkušební hovor teď nejde uskutečnit. Pozastavený celý účet vrací místo toho account_suspended.
  • 402insufficient_creditKredit nepokryje minimum pro zahájení hovoru.
  • 503capacity_busyVšechny odchozí linky jsou právě obsazené, zkus to za minutu.
  • 429rate_limitedVyčerpaný denní limit 3 zkušebních hovorů na účet - běžné volání přes POST /v1/calls tenhle limit nemá.

Relay - vlastní agent

Jednorázový pronájem SIP jména z relay poolu pro odchozí hovor tvého vlastního hlasového agenta (ElevenLabs i jiná platforma) - bez agentní přirážky 2,50 Kč/min, tu totiž platíš u své platformy sám. Kompletní návod s nastavením ElevenLabs je na stránce Vlastní agent.

POST/v1/relay

Vytvoří lease - to je cílové číslo, from tvoje vlastní volai číslo, ze kterého má hovor vypadat, že jde. Vrácené sipName je HOLÉ jméno pro ElevenLabs to_number (ne sipUri - ElevenLabs celé SIP URI odmítne). Odchozí trunk své platformy nastav na outboundTrunkAddress z GET /v1/numbers/{e164}/sip - jiná adresa znamená, že se hovor v síti nesměruje a platforma ohlásí timeout.

ttlSecs (nepovinné, výchozí 120) - kolik vteřin lease čeká na první hovor, než nevyužitý zanikne. Povolený rozsah je 15 až 300 vteřin - mimo něj vrátí 400 validation.

Požadavek

bash
curl -X POST https://volai.cz/v1/relay \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"to": "+420777123456", "from": "+420601234567"}'

Odpověď

json
{
  "id": "rl_4f2a91cd",
  "sipName": "volai_relay_2",
  "sipUri": "sip:volai_relay_2@sip.volai.cz",
  "expiresAt": 1756111760000,
  "callId": "c_9d4e2b7f"
}

Chybové stavy

  • 400invalid_numberto nebo from není platné telefonní číslo.
  • 400validationttlSecs mimo povolený rozsah 15 až 300 vteřin.
  • 404from_number_not_ownedfrom nepatří tvému účtu.
  • 400on_dncto je na tvém seznamu nevolat.
  • 400relay_lease_limitUž máš aktivní lease na tomhle čísle, nebo dvě na celém účtu.
  • 402insufficient_creditKredit nepokryje minimum pro zahájení hovoru.
  • 409destination_busyNa to už běží jiný hovor.
  • 503capacity_busyRelay pool je právě plný, zkus to za chvíli - nic ti neúčtujeme.

GET/v1/relay

Výpis tvých relay spojení - pending čeká na první hovor, active znamená, že hovor právě běží.

Požadavek

bash
curl https://volai.cz/v1/relay \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "leases": [
    {
      "id": "rl_4f2a91cd",
      "sipName": "volai_relay_2",
      "sipUri": "sip:volai_relay_2@sip.volai.cz",
      "from": "+420601234567",
      "to": "+420777123456",
      "callId": "c_9d4e2b7f",
      "status": "active",
      "createdAt": 1756111640000,
      "expiresAt": 1756111760000
    }
  ]
}

DELETE/v1/relay/{id}

Uvolní lease a vrátí slot do poolu, dokud čeká na první hovor (pending). Neukončí probíhající hovor - to dnes žádné API neumí (ani telefonní síť, ani ElevenLabs) - takový lease (active) doběhne sám, jen zabrání dalšímu použití stejného leasu.

Požadavek

bash
curl -X DELETE https://volai.cz/v1/relay/rl_4f2a91cd \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "cancelled": true
}

Chybové stavy

  • 404lease_not_foundLease neexistuje, vypršel, nebo nepatří tvému účtu.
  • 409lease_already_activeLease už spotřeboval hovor, který právě běží - takový lease se nedá zrušit (doběhne sám). Platí jen pro DELETE /v1/relay/{id}, ne pro založení nového leasu.

Seznam nevolat (DNC)

Čísla, na která tvůj agent (vlastní i zabudovaný) nemá volat - ať přes POST /v1/calls, zkušební hovor nebo relay. DNC platí jen pro hovory - odeslání SMS na číslo v seznamu nijak neomezuje.

Kromě ručního seznamu volai destinaci automaticky (a dočasně) zablokuje samo - po 3 neúspěšných pokusech za 24 hodin (obsazeno, nezvednuto, odmítnuto) přestane na číslo volat na 30 dní. Blokaci jde zrušit v portálu (Nastavení) nebo přes POST /v1/dnc/{e164}/unblock - jinak sama odezní.

GET/v1/dnc

Ruční seznam (numbers) i automaticky blokované destinace (blocked) - blockedAt a expiresAt jsou unixové milisekundy. Automatická blokace, která začala před verzí 1.8.0, ve blocked nemusí být; když hovor vrátí destination_auto_blocked, zavolej odblokovací endpoint přímo s jeho číslem i bez řádku ve výpisu.

Požadavek

bash
curl https://volai.cz/v1/dnc \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "numbers": ["+420777998877"],
  "blocked": [
    {
      "e164": "+420777123456",
      "blockedAt": 1757600000000,
      "expiresAt": 1760192000000
    }
  ]
}

POST/v1/dnc

Přidá číslo na seznam nevolat.

Požadavek

bash
curl -X POST https://volai.cz/v1/dnc \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"e164": "+420777998877"}'

Odpověď

json
{
  "added": true,
  "e164": "+420777998877"
}

Chybové stavy

  • 400validatione164 není platné telefonní číslo.

DELETE/v1/dnc/{e164}

Odebere číslo ze seznamu nevolat.

Požadavek

bash
curl -X DELETE "https://volai.cz/v1/dnc/+420777998877" \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "removed": true,
  "e164": "+420777998877"
}

Chybové stavy

  • 400validatione164 v URL není platné telefonní číslo.

POST/v1/dnc/{e164}/unblock

Zruší automatickou blokaci destinace, pokud existuje - i blokaci z doby před verzí 1.8.0, která ve výpisu blocked chybí. Vrátí unblocked: false, ne 404, když blokace nebyla.

Požadavek

bash
curl -X POST "https://volai.cz/v1/dnc/+420777998877/unblock" \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "unblocked": true,
  "e164": "+420777998877"
}

Chybové stavy

  • 400validatione164 v URL není platné telefonní číslo.

Webhook

GET/v1/webhook

Aktuální nastavení odchozích webhooků - bez podpisového secretu, ten se vrací jen z PUT.

Požadavek

bash
curl https://volai.cz/v1/webhook \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "url": "https://tvoje-appka.cz/webhooks/volai",
  "events": ["call.completed", "call.failed", "message.sent"],
  "recentDeliveries": [
    {
      "id": "whd_9f2b7a1c4e",
      "event": "call.completed",
      "url": "https://tvoje-appka.cz/webhooks/volai",
      "status": 200,
      "attempts": 1,
      "ok": true,
      "durationMs": 184,
      "createdAt": 1756111641000
    }
  ]
}

PUT/v1/webhook

Nastaví (nebo přepíše) cílovou URL a odebírané události. V odpovědi dostaneš i podpisový secret - ulož si ho hned, později ho GET už nevrátí. Prázdné pole events: [] znamená odebírat všechny události, ne žádnou - a to i ty, které přibudou později. Když chceš jen některé, vyjmenuj je. Naopak prázdné url: "" webhook zruší - volai pak nepošle nic a odpověď vrátí url: null. Postup ověření podpisu je na stránce Webhooky.

Požadavek

bash
curl -X PUT "https://volai.cz/v1/webhook" \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://tvoje-appka.cz/webhooks/volai","events":["call.completed","call.failed","message.sent"]}'

Odpověď

json
{
  "url": "https://tvoje-appka.cz/webhooks/volai",
  "secret": "whsec_9f2b7a1c4e6d8f0a",
  "events": ["call.completed", "call.failed", "message.sent"]
}

Chybové stavy

  • 400validationurl chybí nebo přesahuje 2000 znaků, nebo events má víc než 20 položek, nebo je některá položka přes 100 znaků.
  • 400invalid_urlURL musí začínat https:// (http:// je povolené jen pro localhost).
  • 400invalid_eventsNěkterá z events není známá (call.completed, call.failed, call.missed, call.no_answer, message.sent, message.received).

DELETE/v1/webhook

Zruší webhook - stejný účinek jako PUT /v1/webhook s prázdným url, jen výslovně pojmenovaný. Nový webhook jde kdykoli nastavit znovu přes PUT. V MCP odpovídá nástroj remove_webhook; list_webhook_deliveries je MCP protějšek GET /v1/webhook/deliveries výš.

Požadavek

bash
curl -X DELETE https://volai.cz/v1/webhook \
  -H "Authorization: Bearer $VOLAI_API_KEY"

Odpověď

json
{"removed":true}

POST/v1/webhook/test

Pošle jeden testovací pokus na nastavenou URL, BEZ OHLEDU na filtr events z PUT /v1/webhook - testuje se doručitelnost, ne odběr. Žádný retry, i neúspěch se zapíše do přehledu doručení. Nejvýš 10 za hodinu.

Požadavek

bash
curl -X POST https://volai.cz/v1/webhook/test \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "delivered": true,
  "status": 200,
  "attempts": 1,
  "durationMs": 184
}

Chybové stavy

  • 404not_foundWebhook není nastavený - nejdřív ho nastav přes PUT /v1/webhook.
  • 429rate_limitedVyčerpal jsi limit 10 testovacích událostí za hodinu.

GET/v1/webhook/deliveries

Posledních 50 doručení (úspěšných, neúspěšných i testovacích), nejnovější první - stejný tvar jako recentDeliveries u GET /v1/webhook.

Požadavek

bash
curl https://volai.cz/v1/webhook/deliveries \
  -H "Authorization: Bearer vk_TVUJ_KLIC"

Odpověď

json
{
  "deliveries": [
    {
      "id": "whd_9f2b7a1c4e",
      "event": "webhook.test",
      "url": "https://tvoje-appka.cz/webhooks/volai",
      "status": 200,
      "attempts": 1,
      "ok": true,
      "durationMs": 184,
      "createdAt": 1756111641000
    }
  ]
}

Google & Apple Calendar

V portálu otevři Připojení a připoj účet. Google se připojuje souhlasem v prohlížeči (povolíš čtení kalendářů i práci s událostmi), Apple e-mailem Apple účtu a heslem pro konkrétní aplikaci (vyžaduje dvoufaktorové ověřování); iCloud kalendáře najde volai automaticky. REST a MCP pak používají běžný API klíč volai stejného účtu a přihlašovací údaje poskytovatele se klientům nevrací. Odkaz na připojení otevírej v prohlížeči s přihlášením do volai. GET /v1/integrations obsahuje připravenost konfigurace a odkazy pro připojení v prohlížeči; ready označuje konfiguraci serveru, nikoli ověřené spojení s účtem.

Rozhraní čte a upravuje existující události. Před změnou načti detail, ukaž uživateli původní hodnoty a návrh a po potvrzení pošli sourceRevision, patch a confirm: true. Revize je neprůhledný řetězec včetně případných uvozovek. Kalendářové chyby mají prefix calendar_: stale_revision (409) vyžaduje nové načtení a schválení, oauth_failed/unauthorized (409) nové připojení, provider_error (502) opakování později. Po nejistém zápisu nebo verification_failed (502) nejdřív načti událost a ověř výsledek.

GET/v1/integrations

Seznam vlastních připojení, připravenost konfigurace a odkazy pro připojení v prohlížeči.

Požadavek

bash
curl "https://volai.cz/v1/integrations" -H "Authorization: Bearer $VOLAI_API_KEY"

Odpověď

json
{"integrations":[],"providers":{"google-calendar":{"ready":true,"connectUrl":"https://volai.cz/api/integrations/google-calendar/oauth/start"},"apple-calendar":{"ready":true,"connectUrl":"https://volai.cz/en/connections"},"fakturoid":{"ready":true,"connectUrl":"https://volai.cz/api/integrations/fakturoid/oauth/start"},"abra-flexi":{"ready":true,"connectUrl":"https://volai.cz/en/connections"}},"googleCalendar":{"ready":true,"connectUrl":"https://volai.cz/api/integrations/google-calendar/oauth/start"}}

POST/v1/integrations

Připojí Apple Kalendář nebo ABRA FlexiBee přihlašovacími údaji v těle. Google Kalendář a Fakturoid mají jen OAuth flow v prohlížeči (odkaz connectUrl z GET /v1/integrations) - poslat jejich provider sem vrátí integration_unsupported. Bere Idempotency-Key; i bez ní je opakované volání se stejnými údaji fakticky idempotentní - existující záznam se najde podle poskytovatele a účtu, ke kterému údaje patří (u Apple podle přihlašovacího jména, u ABRA podle adresy serveru a firmy); label je jen popisek a při opakovaném volání se přepíše.

Přihlašovací údaj předávej jen tehdy, když ho uživatel sám uložil někam, odkud ho můžeš přečíst (proměnná prostředí nebo soubor, který pojmenoval). Nikdy si o něj neříkej v konverzaci a nikdy ho nevypisuj zpět.

Požadavek

bash
curl -X POST https://volai.cz/v1/integrations \
  -H "Authorization: Bearer $VOLAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"provider":"apple-calendar","username":"you@icloud.com","appPassword":"abcd-efgh-ijkl-mnop"}'

Odpověď

json
{"integration":{"id":"int_7f3a91cd","provider":"apple-calendar","kind":"calendar","label":"apple-calendar","createdAt":1756111640000,"updatedAt":1756111640000}}

Chybové stavy

  • 409idempotency_in_progressSouběžné volání se stejným Idempotency-Key ještě běží - zkus to prosím za chvíli znovu.
  • 400validationNeplatný tvar těla - u Apple chybí username/appPassword, u ABRA baseUrl/company/username/password, nebo tělo obsahuje neznámé pole (schéma je .strict()).
  • 400integration_unsupportedprovider je google-calendar nebo fakturoid - ty se připojují jen OAuth flow v prohlížeči.
  • 400integration_invalid_providerAdresa serveru ABRA FlexiBee (baseUrl) není na seznamu domén povolených provozovatelem volai - napiš podpoře, ať doménu povolí.
  • 400integration_invalid_calendarBuď Apple účet nemá žádný dostupný kalendář, který by šlo připojit, nebo poslaná hodnota calendarUrl není mezi kalendáři, které vrátilo vyhledávání kalendářů (discovery) na iCloudu.
  • 409integration_unauthorizedPoskytovatel přihlašovací údaje odmítl (špatné jméno nebo heslo).
  • 409integration_read_onlyApple CalDAV odmítl zápis (403) - účet má jen čtecí přístup ke kalendáři.
  • 409integration_busyNa účtu právě běží jiná operace nad integracemi - zkus to za chvíli znovu.
  • 404integration_not_foundIntegrace, kterou má tenhle požadavek aktualizovat, mezitím zmizela - zkus připojit znovu.
  • 502integration_provider_errorChyba na straně poskytovatele - zkus to prosím za chvíli znovu.
  • 502integration_invalid_provider_responsePoskytovatel vrátil neočekávanou odpověď - zkus to znovu, a pokud to přetrvává, napiš podpoře.
  • 500integration_storage_errorUložení přihlašovacích údajů selhalo na naší straně - zkus to prosím znovu.

DELETE/v1/integrations/{id}

Odpojí libovolnou integraci (Google, Apple, Fakturoid, ABRA) - nevratné, žádné potvrzení a žádný MCP nástroj, protože odpojení nejde vzít zpět. Připojit přes REST znovu jde jen Apple a ABRA (viz POST /v1/integrations výš) - Google a Fakturoid znovu přes OAuth v prohlížeči.

Požadavek

bash
curl -X DELETE https://volai.cz/v1/integrations/int_7f3a91cd \
  -H "Authorization: Bearer $VOLAI_API_KEY"

Odpověď

json
{"disconnected":true}

Chybové stavy

  • 404integration_not_foundIntegrace neexistuje nebo nepatří tvému účtu.
  • 409integration_busyNa integraci právě běží jiná operace (typicky obnova tokenu) - zkus to za chvíli znovu.

GET/v1/integrations/{id}/calendars

Kalendáře vlastního připojení. id získáš ze seznamu připojení.

Požadavek

bash
curl "https://volai.cz/v1/integrations/int_CONNECTION/calendars" -H "Authorization: Bearer $VOLAI_API_KEY"

Odpověď

json
{"calendars":[{"id":"primary","label":"Work","timezone":"Europe/Prague","accessRole":"owner"}]}

Chybové stavy

  • 404calendar_not_foundIntegrace ani kalendář s tímhle ID neexistuje, nebo nepatří tvému účtu.
  • 400calendar_unsupportedPřipojení není kalendář (fakturace), tenhle endpoint pro něj neplatí.
  • 409calendar_read_onlyKalendář je připojený jen pro čtení (Apple CalDAV odmítl zápis) - v Apple Kalendáři si zkontroluj oprávnění ke sdílenému kalendáři.
  • 409calendar_busyNa tomhle připojení právě běží jiná operace (typicky obnova tokenu) - zkus to prosím za chvíli znovu.
  • 409calendar_refresh_in_progressPřístup k účtu se právě obnovuje na pozadí - zkus to prosím za chvíli znovu.
  • 409calendar_unauthorizedPřipojení ztratilo přístup u poskytovatele - připoj účet v portálu znovu.
  • 409calendar_oauth_failedPřihlášení k účtu poskytovatele selhalo - připoj účet v portálu znovu.
  • 409calendar_credentials_unavailableUložené přihlašovací údaje připojení se nepodařilo rozšifrovat - připoj účet v portálu znovu; pokud to přetrvává, napiš podpoře.
  • 409calendar_invalid_credentialsUložené přihlašovací údaje poskytovatel odmítl - připoj účet v portálu znovu.
  • 503calendar_not_configuredKalendářová integrace je na naší straně momentálně nenakonfigurovaná - napiš prosím podpoře.
  • 502calendar_provider_errorChyba na straně poskytovatele (Google, Apple) - zkus to prosím za chvíli znovu.
  • 502calendar_invalid_provider_responsePoskytovatel vrátil neočekávanou odpověď - zkus to prosím znovu, a pokud to přetrvává, napiš podpoře.

POST/v1/integrations/{id}/availability

Vrátí volné termíny v připojeném kalendáři BEZ uloženého agenta - stejný výpočet jako check_availability v hovoru, jen bez pravidel agenta. windows chybí = 24 hodin každý den; když ho pošleš, vynechaný den v týdnu nemá žádné okno. to musí být po from a nejvýš 31 dní po něm. Odpověď nese jen start/end a timezone - žádné názvy ani popisy událostí.

Požadavek

bash
curl -X POST "https://volai.cz/v1/integrations/int_CONNECTION/availability" -H "Authorization: Bearer $VOLAI_API_KEY" -H "Content-Type: application/json" -d '{"calendarId":"primary","from":"2026-09-22T00:00:00+02:00","to":"2026-09-23T00:00:00+02:00","slotMinutes":30,"windows":{"tue":[{"from":"09:00","to":"17:00"}]}}'

Odpověď

json
{"slots":[{"start":"2026-09-22T09:00:00+02:00","end":"2026-09-22T09:30:00+02:00"}],"timezone":"Europe/Prague"}

Chybové stavy

  • 404calendar_not_foundIntegrace ani kalendář s tímhle ID neexistuje, nebo nepatří tvému účtu.
  • 400calendar_unsupportedPřipojení není kalendář (fakturace), tenhle endpoint pro něj neplatí.
  • 409calendar_read_onlyKalendář je připojený jen pro čtení (Apple CalDAV odmítl zápis) - v Apple Kalendáři si zkontroluj oprávnění ke sdílenému kalendáři.
  • 409calendar_busyNa tomhle připojení právě běží jiná operace (typicky obnova tokenu) - zkus to prosím za chvíli znovu.
  • 409calendar_refresh_in_progressPřístup k účtu se právě obnovuje na pozadí - zkus to prosím za chvíli znovu.
  • 409calendar_unauthorizedPřipojení ztratilo přístup u poskytovatele - připoj účet v portálu znovu.
  • 409calendar_oauth_failedPřihlášení k účtu poskytovatele selhalo - připoj účet v portálu znovu.
  • 409calendar_credentials_unavailableUložené přihlašovací údaje připojení se nepodařilo rozšifrovat - připoj účet v portálu znovu; pokud to přetrvává, napiš podpoře.
  • 409calendar_invalid_credentialsUložené přihlašovací údaje poskytovatel odmítl - připoj účet v portálu znovu.
  • 503calendar_not_configuredKalendářová integrace je na naší straně momentálně nenakonfigurovaná - napiš prosím podpoře.
  • 502calendar_provider_errorChyba na straně poskytovatele (Google, Apple) - zkus to prosím za chvíli znovu.
  • 502calendar_invalid_provider_responsePoskytovatel vrátil neočekávanou odpověď - zkus to prosím znovu, a pokud to přetrvává, napiš podpoře.
  • 400validationNeplatný tvar těla (calendarId, from, to, ...) - tělo je .strict(), neznámé pole ho odmítne; from/to musí mít RFC3339 formát s pásmem.
  • 400calendar_invalid_calendarcalendarId neodpovídá žádnému kalendáři z GET .../calendars tohohle připojení.
  • 400calendar_too_many_eventsPožadovaný rozsah obsahuje víc událostí, než jedno čtení zvládne přečíst - zkrať rozsah (dny místo týdnů) a zkus to znovu.
  • 400calendar_invalid_rangeto musí být později než from a nejvýš 31 dní po něm.
  • 400calendar_invalid_timezonetimezone není platná IANA zóna, například Europe/Prague.
  • 400calendar_invalid_windowsOkno musí mít from dřív než to ve tvaru HH:MM, nejvýš 4 okna na den a okna ve stejném dni se nesmí překrývat.
  • 400calendar_no_windowsŽádný den v týdnu nemá žádné okno dostupnosti - přidej aspoň jedno.
  • 400calendar_invalid_rulesNěkteré číslo je mimo povolený rozsah, nebo je minNoticeMinutes delší než celý horizont hledání.

GET/v1/integrations/{id}/events

Povinné calendarId v query. Nejvýš 50 Google událostí nebo Apple událostí a opakovaných sérií, od současnosti. Volitelně search, timeMin, timeMax a pageToken. Časové hranice musí mít pásmo. Pro další stránku použij nextPageToken a stejné filtry včetně pevného timeMin. Apple recurring:true označuje celou sérii; start/end popisují původní výskyt a změna upraví základ série se zachováním jednotlivých výjimek. Neznámý parametr v query tenhle endpoint odmítne chybou 400 validation - na rozdíl od zbytku v1 se přebývající parametry neignorují.

Požadavek

bash
curl "https://volai.cz/v1/integrations/int_CONNECTION/events?calendarId=primary" -H "Authorization: Bearer $VOLAI_API_KEY"

Odpověď

json
{"events":[{"calendarId":"primary","externalId":"event1","title":"Meeting","start":"2026-10-05T10:00:00+02:00","end":"2026-10-05T11:00:00+02:00","status":"confirmed","sourceRevision":"revision1"}]}

Chybové stavy

  • 404calendar_not_foundIntegrace ani kalendář s tímhle ID neexistuje, nebo nepatří tvému účtu.
  • 400calendar_unsupportedPřipojení není kalendář (fakturace), tenhle endpoint pro něj neplatí.
  • 409calendar_read_onlyKalendář je připojený jen pro čtení (Apple CalDAV odmítl zápis) - v Apple Kalendáři si zkontroluj oprávnění ke sdílenému kalendáři.
  • 409calendar_busyNa tomhle připojení právě běží jiná operace (typicky obnova tokenu) - zkus to prosím za chvíli znovu.
  • 409calendar_refresh_in_progressPřístup k účtu se právě obnovuje na pozadí - zkus to prosím za chvíli znovu.
  • 409calendar_unauthorizedPřipojení ztratilo přístup u poskytovatele - připoj účet v portálu znovu.
  • 409calendar_oauth_failedPřihlášení k účtu poskytovatele selhalo - připoj účet v portálu znovu.
  • 409calendar_credentials_unavailableUložené přihlašovací údaje připojení se nepodařilo rozšifrovat - připoj účet v portálu znovu; pokud to přetrvává, napiš podpoře.
  • 409calendar_invalid_credentialsUložené přihlašovací údaje poskytovatel odmítl - připoj účet v portálu znovu.
  • 503calendar_not_configuredKalendářová integrace je na naší straně momentálně nenakonfigurovaná - napiš prosím podpoře.
  • 502calendar_provider_errorChyba na straně poskytovatele (Google, Apple) - zkus to prosím za chvíli znovu.
  • 502calendar_invalid_provider_responsePoskytovatel vrátil neočekávanou odpověď - zkus to prosím znovu, a pokud to přetrvává, napiš podpoře.
  • 400validationNeplatný tvar query (např. timeMax není po timeMin, nebo neznámý parametr) - viz meze u jednotlivých parametrů výš.
  • 400calendar_invalid_calendarcalendarId neodpovídá žádnému kalendáři z GET .../calendars tohohle připojení.
  • 400calendar_invalid_queryNeplatný pageToken nebo Apple eventId. Použij externalId z čerstvého výpisu událostí a při stránkování zachovej stejné filtry.
  • 409calendar_stale_revisionsourceRevision neodpovídá aktuální revizi události - někdo ji mezitím změnil. Načti ji znovu, ukaž uživateli nový stav a nech ho schválit nový návrh. U výpisu Apple událostí znamená totéž, že se seznam mezi stránkami změnil: zahoď pageToken a načti první stránku znovu.

POST/v1/integrations/{id}/events

Založí schůzku v připojeném kalendáři po výslovném schválení uživatele (confirm: true). Pošli přesně jedno z end nebo durationMinutes. idempotencyKey je nepovinný - stejná hodnota podruhé vrátí tutéž událost místo druhého zápisu. Těsně před zápisem se znovu ověří, že termín je stále volný; odpověď je čerstvě přečtená událost, stejný tvar jako GET .../events/{eventId}.

Požadavek

bash
curl -X POST "https://volai.cz/v1/integrations/int_CONNECTION/events" -H "Authorization: Bearer $VOLAI_API_KEY" -H "Content-Type: application/json" -d '{"calendarId":"primary","start":"2026-09-22T09:00:00+02:00","durationMinutes":30,"title":"Meeting","confirm":true}'

Odpověď

json
{
  "event": {
    "calendarId": "primary",
    "externalId": "event1",
    "title": "Meeting",
    "start": "2026-10-05T10:00:00+02:00",
    "end": "2026-10-05T11:00:00+02:00",
    "timezone": "Europe/Prague",
    "status": "confirmed",
    "sourceRevision": "revision1",
    "provider": "google-calendar",
    "sourceUrl": "https://calendar.google.com/calendar/event?eid=ZXZlbnQx",
    "fetchedAt": "2026-09-08T09:00:00.000Z"
  }
}

Chybové stavy

  • 404calendar_not_foundIntegrace ani kalendář s tímhle ID neexistuje, nebo nepatří tvému účtu.
  • 400calendar_unsupportedPřipojení není kalendář (fakturace), tenhle endpoint pro něj neplatí.
  • 409calendar_read_onlyKalendář je připojený jen pro čtení (Apple CalDAV odmítl zápis) - v Apple Kalendáři si zkontroluj oprávnění ke sdílenému kalendáři.
  • 409calendar_busyNa tomhle připojení právě běží jiná operace (typicky obnova tokenu) - zkus to prosím za chvíli znovu.
  • 409calendar_refresh_in_progressPřístup k účtu se právě obnovuje na pozadí - zkus to prosím za chvíli znovu.
  • 409calendar_unauthorizedPřipojení ztratilo přístup u poskytovatele - připoj účet v portálu znovu.
  • 409calendar_oauth_failedPřihlášení k účtu poskytovatele selhalo - připoj účet v portálu znovu.
  • 409calendar_credentials_unavailableUložené přihlašovací údaje připojení se nepodařilo rozšifrovat - připoj účet v portálu znovu; pokud to přetrvává, napiš podpoře.
  • 409calendar_invalid_credentialsUložené přihlašovací údaje poskytovatel odmítl - připoj účet v portálu znovu.
  • 503calendar_not_configuredKalendářová integrace je na naší straně momentálně nenakonfigurovaná - napiš prosím podpoře.
  • 502calendar_provider_errorChyba na straně poskytovatele (Google, Apple) - zkus to prosím za chvíli znovu.
  • 502calendar_invalid_provider_responsePoskytovatel vrátil neočekávanou odpověď - zkus to prosím znovu, a pokud to přetrvává, napiš podpoře.
  • 400validationNeplatný tvar těla - chybí přesně jedno z end/durationMinutes, title je prázdný nebo příliš dlouhý, nebo tělo obsahuje neznámé pole (.strict()).
  • 400calendar_invalid_calendarcalendarId neodpovídá žádnému kalendáři z GET .../calendars tohohle připojení.
  • 400calendar_invalid_rangeto musí být později než from a nejvýš 31 dní po něm.
  • 400calendar_confirmation_requiredconfirm musí být true - bez výslovného schválení uživatele se schůzka nezaloží.
  • 409calendar_slot_takenPožadovaný termín teď není volný - načti dostupnost znovu (POST .../availability) a vyber jiný čas.
  • 503calendar_unavailableDo kalendáře se teď nejde podívat ani zapsat - zkus to prosím za chvíli znovu.

GET/v1/integrations/{id}/events/{eventId}

Povinné calendarId v query. Čerstvý detail s sourceRevision a fetchedAt. ID v cestě i query URL-enkóduj. U Apple použij jako eventId přesnou hodnotu externalId z výpisu událostí. Jde o neprůhledný identifikátor záznamu CalDAV, ne o iCalendar UID; nesestavuj ho ani nedekóduj.

Požadavek

bash
curl "https://volai.cz/v1/integrations/int_CONNECTION/events/EVENT_ID?calendarId=primary" -H "Authorization: Bearer $VOLAI_API_KEY"

Odpověď

json
{
  "event": {
    "calendarId": "primary",
    "externalId": "event1",
    "title": "Meeting",
    "start": "2026-10-05T10:00:00+02:00",
    "end": "2026-10-05T11:00:00+02:00",
    "timezone": "Europe/Prague",
    "status": "confirmed",
    "sourceRevision": "revision1",
    "provider": "google-calendar",
    "sourceUrl": "https://calendar.google.com/calendar/event?eid=ZXZlbnQx",
    "fetchedAt": "2026-09-08T09:00:00.000Z"
  }
}

Chybové stavy

  • 404calendar_not_foundIntegrace ani kalendář s tímhle ID neexistuje, nebo nepatří tvému účtu.
  • 400calendar_unsupportedPřipojení není kalendář (fakturace), tenhle endpoint pro něj neplatí.
  • 409calendar_read_onlyKalendář je připojený jen pro čtení (Apple CalDAV odmítl zápis) - v Apple Kalendáři si zkontroluj oprávnění ke sdílenému kalendáři.
  • 409calendar_busyNa tomhle připojení právě běží jiná operace (typicky obnova tokenu) - zkus to prosím za chvíli znovu.
  • 409calendar_refresh_in_progressPřístup k účtu se právě obnovuje na pozadí - zkus to prosím za chvíli znovu.
  • 409calendar_unauthorizedPřipojení ztratilo přístup u poskytovatele - připoj účet v portálu znovu.
  • 409calendar_oauth_failedPřihlášení k účtu poskytovatele selhalo - připoj účet v portálu znovu.
  • 409calendar_credentials_unavailableUložené přihlašovací údaje připojení se nepodařilo rozšifrovat - připoj účet v portálu znovu; pokud to přetrvává, napiš podpoře.
  • 409calendar_invalid_credentialsUložené přihlašovací údaje poskytovatel odmítl - připoj účet v portálu znovu.
  • 503calendar_not_configuredKalendářová integrace je na naší straně momentálně nenakonfigurovaná - napiš prosím podpoře.
  • 502calendar_provider_errorChyba na straně poskytovatele (Google, Apple) - zkus to prosím za chvíli znovu.
  • 502calendar_invalid_provider_responsePoskytovatel vrátil neočekávanou odpověď - zkus to prosím znovu, a pokud to přetrvává, napiš podpoře.
  • 400validationcalendarId v query je povinné a smí mít nejvýš 500 znaků.
  • 400calendar_invalid_calendarcalendarId neodpovídá žádnému kalendáři z GET .../calendars tohohle připojení.
  • 400calendar_invalid_queryNeplatný pageToken nebo Apple eventId. Použij externalId z čerstvého výpisu událostí a při stránkování zachovej stejné filtry.

PATCH/v1/integrations/{id}/events/{eventId}

Změna názvu (title), začátku (start) nebo konce (end). Časy RFC3339 s pásmem, nebo celodenní YYYY-MM-DD s výlučným koncem. Apple čas bez pásma lze poslat pouze při zachování události, která již pásmo nemá. Vyžaduje souhlas a aktuální revizi; vrací ověřený detail události.

Požadavek

bash
curl -X PATCH "https://volai.cz/v1/integrations/int_CONNECTION/events/EVENT_ID" -H "Authorization: Bearer $VOLAI_API_KEY" -H "Content-Type: application/json" -d '{"calendarId":"primary","sourceRevision":"revision1","patch":{"title":"Updated meeting"},"confirm":true}'

Odpověď

json
{
  "event": {
    "calendarId": "primary",
    "externalId": "event1",
    "title": "Updated meeting",
    "start": "2026-10-05T10:00:00+02:00",
    "end": "2026-10-05T11:00:00+02:00",
    "timezone": "Europe/Prague",
    "status": "confirmed",
    "sourceRevision": "revision1",
    "provider": "google-calendar",
    "sourceUrl": "https://calendar.google.com/calendar/event?eid=ZXZlbnQx",
    "fetchedAt": "2026-09-08T09:00:00.000Z"
  }
}

Chybové stavy

  • 404calendar_not_foundIntegrace ani kalendář s tímhle ID neexistuje, nebo nepatří tvému účtu.
  • 400calendar_unsupportedPřipojení není kalendář (fakturace), tenhle endpoint pro něj neplatí.
  • 409calendar_read_onlyKalendář je připojený jen pro čtení (Apple CalDAV odmítl zápis) - v Apple Kalendáři si zkontroluj oprávnění ke sdílenému kalendáři.
  • 409calendar_busyNa tomhle připojení právě běží jiná operace (typicky obnova tokenu) - zkus to prosím za chvíli znovu.
  • 409calendar_refresh_in_progressPřístup k účtu se právě obnovuje na pozadí - zkus to prosím za chvíli znovu.
  • 409calendar_unauthorizedPřipojení ztratilo přístup u poskytovatele - připoj účet v portálu znovu.
  • 409calendar_oauth_failedPřihlášení k účtu poskytovatele selhalo - připoj účet v portálu znovu.
  • 409calendar_credentials_unavailableUložené přihlašovací údaje připojení se nepodařilo rozšifrovat - připoj účet v portálu znovu; pokud to přetrvává, napiš podpoře.
  • 409calendar_invalid_credentialsUložené přihlašovací údaje poskytovatel odmítl - připoj účet v portálu znovu.
  • 503calendar_not_configuredKalendářová integrace je na naší straně momentálně nenakonfigurovaná - napiš prosím podpoře.
  • 502calendar_provider_errorChyba na straně poskytovatele (Google, Apple) - zkus to prosím za chvíli znovu.
  • 502calendar_invalid_provider_responsePoskytovatel vrátil neočekávanou odpověď - zkus to prosím znovu, a pokud to přetrvává, napiš podpoře.
  • 400validationNeplatný tvar těla (calendarId, sourceRevision, patch, confirm) - tělo je .strict(), neznámé pole ho odmítne.
  • 400calendar_invalid_calendarcalendarId neodpovídá žádnému kalendáři z GET .../calendars tohohle připojení.
  • 400calendar_invalid_queryNeplatný pageToken nebo Apple eventId. Použij externalId z čerstvého výpisu událostí a při stránkování zachovej stejné filtry.
  • 400calendar_provider_mismatchPřipojení mezitím změnilo poskytovatele, takže návrh z předchozího čtení k němu nepatří - načti událost znovu a nech schválit nový návrh.
  • 400calendar_invalid_patchpatch má neplatný tvar nebo neznámé pole - povolené jsou jen title, start a end; start/end musí mít pásmo (nebo být celodenní YYYY-MM-DD s pozdějším koncem).
  • 400calendar_confirmation_requiredconfirm musí být true - bez potvrzení uživatele se událost nezmění.
  • 409calendar_stale_revisionsourceRevision neodpovídá aktuální revizi události - někdo ji mezitím změnil. Načti ji znovu, ukaž uživateli nový stav a nech ho schválit nový návrh. U výpisu Apple událostí znamená totéž, že se seznam mezi stránkami změnil: zahoď pageToken a načti první stránku znovu.
  • 502calendar_verification_failedZápis proběhl, ale zpětné čtení u poskytovatele se nepodařilo ověřit - načti událost znovu a zkontroluj výsledek.

POST/v1/integrations/{id}/events/{eventId}/propose

Připraví návrh změny názvu, začátku nebo konce události BEZ zápisu k poskytovateli (patch a sourceRevision jako u PATCH .../events/{eventId}, ale bez confirm) - čistá funkce nad uloženým typem integrace, nikdy nevolá poskytovatele. Ukaž návrh (proposal.patch) uživateli k výslovnému schválení a teprve pak pošli PATCH .../events/{eventId} se stejnými hodnotami a confirm: true.

Požadavek

bash
curl -X POST "https://volai.cz/v1/integrations/int_CONNECTION/events/EVENT_ID/propose" -H "Authorization: Bearer $VOLAI_API_KEY" -H "Content-Type: application/json" -d '{"calendarId":"primary","sourceRevision":"revision1","patch":{"title":"Updated meeting"}}'

Odpověď

json
{"proposal":{"calendarId":"primary","externalId":"EVENT_ID","sourceRevision":"revision1","patch":{"title":"Updated meeting"},"provider":"google-calendar","proposedAt":"2026-09-08T09:00:00.000Z","requiresExplicitConfirmation":true}}

Chybové stavy

  • 400validationNeplatný tvar těla - calendarId/sourceRevision chybí, nebo patch obsahuje jiné pole než title/start/end.
  • 404calendar_not_foundIntegrace ani kalendář s tímhle ID neexistuje, nebo nepatří tvému účtu.
  • 400calendar_unsupportedPřipojení není kalendář (fakturace), tenhle endpoint pro něj neplatí.
  • 400calendar_invalid_patchpatch má neplatný tvar - start/end musí mít pásmo (nebo být celodenní YYYY-MM-DD s pozdějším koncem).

Faktury z Fakturoidu a ABRA Flexi

Účet nejdřív připoj v Připojení. Fakturoid používá OAuth v prohlížeči; pokud máš přístup k fakturám více firem, vybereš jednu výslovně. ABRA Flexi potřebuje HTTPS server, identifikátor firmy a přihlašovací údaje API uživatele; doménu serveru musí povolit provozovatel volai. Přihlašovací údaje patří do portálu. Rozhraní čte vydané faktury, nevystavuje je ani neoznačuje jako zaplacené.

Chyby fakturačních endpointů mají prefix integration_ a nesou cause i action jako každá chyba API. Podle příčiny: account znamená připojit účet znovu v Připojení, bez toho nemá smysl opakovat; busy (409) zopakuj za pár sekund, tvoje data jsou v pořádku; service (400, 502 nebo 503) je u nás nebo u poskytovatele - zopakuj později a stav faktury zatím ber jako NEZNÁMÝ; request (400) oprav v dotazu. Neúspěšné načtení nikdy nenahrazuj dřívějším tvrzením o úhradě ani tvrzením volajícího.

GET/v1/integrations/{id}/invoices

Volitelný search (nejvýš 100 znaků) hledá přímo u poskytovatele. Vrací prvních 40 výsledků z Fakturoidu nebo 50 z ABRA Flexi. Pro nalezení starších faktur zpřesni dotaz; odpověď nemá stránkovací token.

Požadavek

bash
curl "https://volai.cz/v1/integrations/int_CONNECTION/invoices?search=2026-0001" -H "Authorization: Bearer $VOLAI_API_KEY"

Odpověď

json
{"invoices":[{"externalId":"123","label":"2026-0001","status":"unpaid","amountDueMinor":1210000,"currency":"CZK","dueDate":"2026-09-30","sourceRevision":"v3"}]}

Chybové stavy

  • 400validationParametr search je delší než 100 znaků, nebo dotaz obsahuje neznámý parametr (třeba limit) - kalendářové a fakturační endpointy neznámé parametry odmítají, jinde ve v1 se ignorují.
  • 404integration_not_foundPřipojení s tímto id na účtu není; u detailu také když poskytovatel fakturu s tímto invoiceId nezná (u výpisu 404 od poskytovatele přijde jako provider_error).
  • 400integration_unsupportedPřipojení není fakturační (např. kalendář) - faktury umí jen Fakturoid a ABRA Flexi.
  • 400integration_invalid_providerServer ABRA Flexi uložený u připojení není v seznamu povolených hostitelů provozovatele. Napiš podpoře.
  • 409integration_busyPřihlašovací údaje připojení se právě obnovují a záznam se mezitím změnil - zopakuj požadavek za pár sekund.
  • 409integration_refresh_in_progressJiný požadavek právě obnovuje token připojení - zopakuj požadavek za pár sekund.
  • 409integration_unauthorizedPoskytovatel odmítl uložené přihlašovací údaje (HTTP 401) - připoj účet znovu v Připojení.
  • 409integration_oauth_failedObnovení tokenu Fakturoidu selhalo - připoj účet znovu v Připojení.
  • 409integration_credentials_unavailableUložené přihlašovací údaje nejde dešifrovat - připoj účet znovu v Připojení.
  • 409integration_invalid_credentialsUložené přihlašovací údaje nemají očekávaný tvar (chybí token, slug nebo uživatel API) - připoj účet znovu v Připojení.
  • 503integration_not_configuredProvozovatel nemá nastavené klíče poskytovatele (např. Fakturoid OAuth) - nic neplatíš, napiš podpoře.
  • 502integration_provider_errorPoskytovatel odpověděl chybou (včetně HTTP 403) nebo neodpověděl - zopakuj požadavek později; stav faktury zatím ber jako neznámý.
  • 502integration_invalid_provider_responsePoskytovatel vrátil odpověď v nečekaném tvaru - zopakuj požadavek později; když to trvá, napiš podpoře.
  • 400integration_invalid_queryParametr search obsahuje zpětná lomítka, řídicí znaky nebo míchané uvozovky, které ABRA Flexi ve vyhledávání nepřijme - zjednoduš dotaz.

GET/v1/integrations/{id}/invoices/{invoiceId}

Před tvrzením o úhradě načti čerstvý důkaz od poskytovatele. amountDueMinor je celé číslo v nejmenších jednotkách dané měny; null znamená, že zůstatek nebyl doložen. status může být unknown. Neznámý stav ani chybějící částka nejsou důkazem úhrady. Detail obsahuje provider, sourceUrl, fetchedAt a evidence: provider_response. invoiceId URL-enkóduj.

Požadavek

bash
curl "https://volai.cz/v1/integrations/int_CONNECTION/invoices/123" -H "Authorization: Bearer $VOLAI_API_KEY"

Odpověď

json
{"invoice":{"externalId":"123","status":"unpaid","amountDueMinor":1210000,"currency":"CZK","dueDate":"2026-09-30","sourceRevision":"v3","provider":"fakturoid","fetchedAt":"2026-09-08T09:00:00.000Z","sourceUrl":"https://app.fakturoid.cz/api/v3/accounts/firma/invoices/123.json","evidence":"provider_response"}}

Chybové stavy

  • 400validationinvoiceId v cestě chybí nebo přesahuje 500 znaků.
  • 404integration_not_foundPřipojení s tímto id na účtu není; u detailu také když poskytovatel fakturu s tímto invoiceId nezná (u výpisu 404 od poskytovatele přijde jako provider_error).
  • 400integration_unsupportedPřipojení není fakturační (např. kalendář) - faktury umí jen Fakturoid a ABRA Flexi.
  • 400integration_invalid_providerServer ABRA Flexi uložený u připojení není v seznamu povolených hostitelů provozovatele. Napiš podpoře.
  • 409integration_busyPřihlašovací údaje připojení se právě obnovují a záznam se mezitím změnil - zopakuj požadavek za pár sekund.
  • 409integration_refresh_in_progressJiný požadavek právě obnovuje token připojení - zopakuj požadavek za pár sekund.
  • 409integration_unauthorizedPoskytovatel odmítl uložené přihlašovací údaje (HTTP 401) - připoj účet znovu v Připojení.
  • 409integration_oauth_failedObnovení tokenu Fakturoidu selhalo - připoj účet znovu v Připojení.
  • 409integration_credentials_unavailableUložené přihlašovací údaje nejde dešifrovat - připoj účet znovu v Připojení.
  • 409integration_invalid_credentialsUložené přihlašovací údaje nemají očekávaný tvar (chybí token, slug nebo uživatel API) - připoj účet znovu v Připojení.
  • 503integration_not_configuredProvozovatel nemá nastavené klíče poskytovatele (např. Fakturoid OAuth) - nic neplatíš, napiš podpoře.
  • 502integration_provider_errorPoskytovatel odpověděl chybou (včetně HTTP 403) nebo neodpověděl - zopakuj požadavek později; stav faktury zatím ber jako neznámý.
  • 502integration_invalid_provider_responsePoskytovatel vrátil odpověď v nečekaném tvaru - zopakuj požadavek později; když to trvá, napiš podpoře.

Objednávky Na klíč (Done for you)

Na klíč postaví funkčního agenta z popisu obyčejným jazykem místo ruční konfigurace v portálu. POST /v1/setup-orders proběhne jako jeden tah rozhovoru: popiš agenta a poradce buď vrátí naceněnou nabídku (quoted), požádá o potvrzení vlastních položek nad prahem (awaiting_confirmation), nebo automatizované nastavení zamítne u případu, který na ně nesedí (declined). Cenu vždy počítá kód z pevného katalogu, nikdy ne model - poradce jen vybírá balíček, doplňky a vlastní položky. Zaplacení nabídky (POST .../checkout, Stripe Checkout session za zřízení plus první dobití kreditu) spustí stavbu; volai postaví agenta ručně a objednávka prochází paid -> building -> ready. Ve stavu ready zavolej na přidělené číslo a vyzkoušej ho, pošli zprávou požadavek na úpravu (revision), pak POST .../launch pro spuštění. GET .../{id} vždy nese nextStep - anglickou větu, co dělat dál pro aktuální stav (REST mluví vždy anglicky).

POST/v1/setup-orders

Jeden tah: popiš agenta, kterého potřebuješ, v brief (aspoň 20 znaků), a dostaneš zpátky nabídku nebo žádost o potvrzení vlastních položek. locale řídí jazyk rozhovoru s poradcem a případných e-mailů k téhle objednávce (výchozí en) - jazyk TÉHLE odpovědi to nemění, REST mluví vždy anglicky. contactPhone se připojí k briefu jako poznámka pro volai, nevaliduje se. Strop 5 nových objednávek na účet za den. Když tah poradce selže (advisor_unavailable, advisor_no_quote), objednávka už vznikla a započítala se do stropu - dohledej ji přes GET /v1/setup-orders a další pokus pošli přes POST .../messages, ne opakovaným voláním tohohle endpointu. Bez Idempotency-Key: každé volání zakládá NOVOU objednávku, jedinou pojistkou je strop 5/den - po timeoutu si nejdřív ověř stav přes GET /v1/setup-orders.

Požadavek

bash
curl -X POST https://volai.cz/v1/setup-orders \
  -H "Authorization: Bearer $VOLAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"brief":"Reception for a Prague fitness studio, Czech and English, takes bookings and answers pricing questions.","locale":"en"}'

201 Created

json
{"order":{"id":"so_AbCdEf123456","status":"quoted","nextStep":"Start the checkout to pay the setup fee and begin the build.","locale":"en","source":"api","createdAt":1757980800000,"updatedAt":1757980810000,"revisionsUsed":0,"extraRevisionHal":49000,"extraRevisionsCharged":0,"revisionsIncluded":2,"quote":{"lines":[{"kind":"package","id":"provozovna","label":"Business","quantity":1,"unitHal":299000,"totalHal":299000},{"kind":"addon","id":"calendar","label":"Calendar booking","quantity":1,"unitHal":99000,"totalHal":99000}],"setupNetHal":398000,"needsConfirmation":false,"revisionsIncluded":2,"monthly":{"callsPerDay":20,"avgMinutes":3,"totalMonthlyHal":542500,"callMinutes":1800}},"selection":{"segment":"provozovna","packageId":"provozovna","addons":[{"id":"calendar","quantity":1}],"customItems":[],"industry":"fitness studio","agentLanguages":["cs","en"],"callsPerDay":20,"avgMinutes":3,"cannotDo":[],"assumptions":["Business hours 9am-6pm"]},"messages":[{"role":"user","content":"Reception for a Prague fitness studio...","at":1757980800000},{"role":"assistant","content":"Here is your quote for a location agent...","at":1757980810000}]}}

Chybové stavy

  • 400validationTělo nesedí na schéma (brief chybí nebo má míň než 20 znaků, contactPhone je moc dlouhý, nebo locale není cs/en).
  • 422advisor_no_quotePoradce se rozhodl položit otázku, ale jednorázový tah vyžaduje nabídku nebo zamítnutí - zkus to znovu nebo dopiš do briefu víc podrobností.
  • 503advisor_unavailablePoradce je dočasně nedostupný (chybí API klíč, výpadek sítě, nebo model odmítl odpovědět) - zkus to za chvíli znovu.
  • 503setup_orders_disabledNa klíč je dočasně vypnuté pro celý účet; už zaplacené objednávky pokračují normálně dál.

GET/v1/setup-orders

Objednávky Na klíč tohoto účtu, bez stránkování.

Požadavek

bash
curl "https://volai.cz/v1/setup-orders" -H "Authorization: Bearer $VOLAI_API_KEY"

Odpověď

json
{"orders":[{"id":"so_AbCdEf123456","status":"quoted","nextStep":"Start the checkout to pay the setup fee and begin the build.","locale":"en","source":"api","createdAt":1757980800000,"updatedAt":1757980810000,"revisionsUsed":0,"extraRevisionHal":49000,"extraRevisionsCharged":0,"messages":[]}]}

GET/v1/setup-orders/{id}

Detail objednávky včetně VŠECH zpráv v rozhovoru (i odpovědí majitele od volai) a pole nextStep.

Požadavek

bash
curl "https://volai.cz/v1/setup-orders/so_AbCdEf123456" -H "Authorization: Bearer $VOLAI_API_KEY"

Odpověď

json
{"order":{"id":"so_AbCdEf123456","status":"ready","nextStep":"Call the number, then send a message for changes or launch.","locale":"en","source":"api","createdAt":1757980800000,"updatedAt":1758153600000,"readyAt":1758153600000,"revisionsUsed":0,"extraRevisionHal":49000,"extraRevisionsCharged":0,"revisionsIncluded":2,"agentId":"ag_XyZ987","numberE164":"+420999123456","messages":[{"role":"user","content":"Reception for a Prague fitness studio...","at":1757980800000},{"role":"owner","content":"Your agent is ready - give it a call.","at":1758153600000}]}}

Chybové stavy

  • 404setup_order_not_foundObjednávka s tímhle id na účtu neexistuje.

POST/v1/setup-orders/{id}/messages

Pošle zprávu poradci. Dokud je objednávka quoted, nová zpráva může spustit další tah poradce, který nabídku přepíše - pokud zrovna neběží otevřený checkout (checkout_open), který nabídku zmrazí, dokud ta Stripe session nevyprší nebo se nevyužije. V ostatních stavech (ready, paid, building, revision) se zpráva jen zapíše pro tým volai; poslání zprávy ve stavu ready přesune objednávku do revision. Kola úprav v ceně balíčku (order.quote.revisionsIncluded, už vyčerpaná order.revisionsUsed) jsou zdarma; po jejich vyčerpání se za další dokončené kolo strhne z kreditu 490 Kč bez DPH jako pohyb setup_revision.

Požadavek

bash
curl -X POST https://volai.cz/v1/setup-orders/so_AbCdEf123456/messages \
  -H "Authorization: Bearer $VOLAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message":"Can it also speak German?"}'

Odpověď

json
{"order":{"id":"so_AbCdEf123456","status":"quoted","nextStep":"Start the checkout to pay the setup fee and begin the build.","locale":"en","source":"api","createdAt":1757980800000,"updatedAt":1757981200000,"revisionsUsed":0,"extraRevisionHal":49000,"extraRevisionsCharged":0,"messages":[{"role":"user","content":"Reception for a Prague fitness studio...","at":1757980800000},{"role":"assistant","content":"Here is your quote...","at":1757980810000},{"role":"user","content":"Can it also speak German?","at":1757981190000},{"role":"assistant","content":"Updated the quote to add German.","at":1757981200000}]}}

Chybové stavy

  • 400validationmessage chybí, je prázdné, nebo přesahuje 2000 znaků.
  • 404setup_order_not_foundObjednávka s tímhle id na účtu neexistuje.
  • 409order_closedObjednávka je launched nebo cancelled - další zprávy už nepřijímá.
  • 409turn_in_progressNa tuhle objednávku právě běží jiný požadavek - počkej, až doběhne, a zkus to znovu.
  • 409checkout_openPro nabídku téhle objednávky právě běží otevřený checkout - počkej, až vyprší nebo se dokončí, než pošleš zprávu, která by mohla změnit cenu.
  • 422advisor_no_quotePoradce se rozhodl položit otázku, ale jednorázový tah vyžaduje nabídku nebo zamítnutí - zkus to znovu nebo dopiš do briefu víc podrobností.
  • 503advisor_unavailablePoradce je dočasně nedostupný (chybí API klíč, výpadek sítě, nebo model odmítl odpovědět) - zkus to za chvíli znovu.

POST/v1/setup-orders/{id}/checkout

Vytvoří Stripe Checkout session na naceněné zřízení plus první dobití kreditu (creditCzk, jedna z podporovaných částek dobití účtu). Jen ze stavu quoted, a jen vlastník objednávky. Success i cancel přesměrování míří zpátky na stránku objednávky v jazyce ROZHOVORU (order.locale), ne nutně v jazyce tohohle požadavku.

Požadavek

bash
curl -X POST https://volai.cz/v1/setup-orders/so_AbCdEf123456/checkout \
  -H "Authorization: Bearer $VOLAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"creditCzk":500}'

Odpověď

json
{"url":"https://checkout.stripe.com/c/pay/cs_test_...","sessionId":"cs_test_a1B2c3D4"}

Chybové stavy

  • 400validationTělo nesedí na schéma (creditCzk není celé číslo).
  • 400invalid_amountcreditCzk není jedna z podporovaných částek dobití účtu.
  • 400billing_address_requiredÚčet ještě nemá uloženou kompletní fakturační adresu - doplň ji nejdřív (PUT /v1/account/billing).
  • 404setup_order_not_foundObjednávka s tímhle id na účtu neexistuje.
  • 409checkout_openPro nabídku téhle objednávky právě běží otevřený checkout - počkej, až vyprší nebo se dokončí, než pošleš zprávu, která by mohla změnit cenu.
  • 409invalid_transitionObjednávka není quoted, nebo jí chybí nabídka - checkout se otevírá jen z naceněné objednávky.
  • 409quote_pending_confirmationNabídka ještě čeká, až volai potvrdí vlastní položky (awaiting_confirmation), teprve pak se checkout otevře.
  • 409turn_in_progressNa tuhle objednávku právě běží jiný požadavek - počkej, až doběhne, a zkus to znovu.
  • 503setup_orders_disabledNa klíč je dočasně vypnuté pro celý účet; už zaplacené objednávky pokračují normálně dál.

POST/v1/setup-orders/{id}/launch

Spustí hotového agenta (ready -> launched) - jediný přechod, který dělá zákazník sám; všechny ostatní (start stavby, ready, zrušení) dělá tým volai.

Požadavek

bash
curl -X POST https://volai.cz/v1/setup-orders/so_AbCdEf123456/launch -H "Authorization: Bearer $VOLAI_API_KEY"

Odpověď

json
{"order":{"id":"so_AbCdEf123456","status":"launched","nextStep":"Your agent is live.","locale":"en","source":"api","createdAt":1757980800000,"updatedAt":1758160000000,"readyAt":1758153600000,"launchedAt":1758160000000,"revisionsUsed":0,"extraRevisionHal":49000,"extraRevisionsCharged":0,"agentId":"ag_XyZ987","numberE164":"+420999123456","messages":[]}}

Chybové stavy

  • 404setup_order_not_foundObjednávka s tímhle id na účtu neexistuje.
  • 409invalid_transitionObjednávka není ready - zatím není co spouštět.

POST/v1/setup-orders/{id}/cancel

Zruší nezaplacenou objednávku (draft, quoted, awaiting_confirmation nebo declined -> cancelled). Zaplacenou objednávku tímhle endpointem zrušit nejde - kontaktuj volai.

Požadavek

bash
curl -X POST https://volai.cz/v1/setup-orders/so_AbCdEf123456/cancel -H "Authorization: Bearer $VOLAI_API_KEY"

Odpověď

json
{"order":{"id":"so_AbCdEf123456","status":"cancelled","nextStep":"This order was cancelled.","locale":"en","source":"api","createdAt":1757980800000,"updatedAt":1757981500000,"revisionsUsed":0,"extraRevisionHal":49000,"extraRevisionsCharged":0,"messages":[]}}

Chybové stavy

  • 404setup_order_not_foundObjednávka s tímhle id na účtu neexistuje.
  • 409invalid_transitionObjednávku nejde zrušit z jejího aktuálního stavu (už je zaplacená, nebo je už uzavřená).

Changelog

Strojově čitelný changelog veřejného rozhraní - REST v1 i MCP. Jeden dokument na jednu URL (žádné query parametry, filtruj si sám nad vráceným polem entries), proto ho jde bezpečně veřejně cachovat. Stejná data jako /en/changelog v portálu a MCP nástroj get_changelog. Kanonický zdroj pravdy je jeden soubor (content/changelog.json) - odsud se generují všechny pohledy.

GET/v1/changelog

Vrátí aktuální verzi rozhraní a VŠECHNY záznamy changelogu (nejnovější první), anglicky bez ohledu na jazyk účtu. version/technical jsou null, když záznam neměl (staré položky bez verze, nebo bez technického detailu). url je kanonická anglická cesta s kotvou na id.

Požadavek

bash
curl https://volai.cz/v1/changelog

Odpověď

json
{
  "version": "1.34.2",
  "entries": [
    {
      "id": "parita-vlna-a",
      "date": "2026-09-08",
      "version": "1.5.0",
      "areas": ["api", "mcp"],
      "audience": "developer",
      "breaking": false,
      "title": "Account, call annotations, tool test, draft rollback, numbers waitlist and more",
      "body": "See /en/docs/api and /en/docs/mcp for the new endpoints and tools.",
      "technical": null,
      "url": "https://volai.cz/en/changelog#parita-vlna-a"
    }
  ]
}

Chybové stavy

  • 429rate_limitedVyčerpal jsi limit 30 požadavků za minutu z jedné IP adresy (bez API klíče se limituje podle IP, ne podle účtu).

Hlavička x-volai-version

Úplně KAŽDÁ odpověď v1 (úspěšná i chybová, včetně 401/429, binárních rout jako nahrávka hovoru a GET /openapi.json) nese hlavičku x-volai-version s aktuální verzí rozhraní. Liší-li se od poslední verze, kterou jsi viděl, zavolej GET /v1/changelog (nebo MCP get_changelog) a zjisti, co je nového - stejné doporučení dostává MCP klient přímo v SERVER_INSTRUCTIONS serveru.

bash
x-volai-version: 1.34.2

MCP protějšek je nástroj get_changelog (READ_ONLY) - stejná data, navíc si umí filtrovat podle since (datum nebo verze - jen novější záznamy), area a omezit počet limit přímo na serveru.