Към съдържанието

Ръководства

Задачи

Задачата набира списък от получатели с един агент по правила, които задавате предварително - бюджет, времеви прозорец за обаждания, брой опити. Нищо не се набира, докато сами не пуснете задачата.

Какво е задача

Задачата (task_...) е изходяща кампания: един агент на двигателя на volai, списък от получатели с телефонен номер и по избор променливи за системния промпт, и правила (бюджет, времеви прозорец за обаждания, максимален брой опити и продължителност на обаждането). Три типа според целта - invoice (напомняне за плащане), appointment (потвърждение на час) и custom (всичко останало) - при първите два, когато source е integration, всеки получател допълнително носи препратка към документа или събитието (externalRefs), които агентът споменава по време на обаждането.

Агентът трябва да е на собствения двигател на volai (useForOutboundTasks: true при създаването, вижте POST /v1/agents), а не на ElevenLabs - двигателят поддържа променливи за системния промпт, ограничения на продължителността на обаждането и проверка, че конфигурацията му не се е променила, докато кампанията е вървяла. Черновата на агента трябва да е ПУБЛИКУВАНА (PATCH /v1/agents/{id} или POST /v1/agents/{id}/draft/publish) И публикуваната версия трябва да е ТЕСТВАНА (POST /v1/agents/{id}/simulate) - ако липсва някоя от стъпките, start връща agent_not_ready.

Състояния и преходи

На практика задачата винаги минава от `draft` направо в `running` (`start`); изтриване (`DELETE`) е възможно само в отбелязаните състояния.

draft
Какво означаваТоку-що създадена, start още не е минавал. Може да се редактира (PATCH) и да се изтрие.
ready
Какво означаваСъстояние на модела между draft и running, което start приема като начална точка - но никоя публична крайна точка (нито REST, нито MCP) днес не прехвърля задача в него, така че на практика няма да го срещнете. Ако все пак настъпи, може да се редактира и изтрива като draft.
running
Какво означаваНабира получатели. НЕ СЕ РЕДАКТИРА - PATCH връща task_not_editable без изключение. Който трябва да промени променливи, първо трябва да спре задачата (POST .../pause) и да я редактира в paused - и то само докато още не е набиран никой получател; след първия опит recipients (а с тях и променливите) вече не могат да се сменят (items_immutable) и остава да създадете нова задача. Не може да се изтрие. Когато достигне лимит на обажданията (rate_limited - лимит на изходящите минути на собствения двигател, таван на едновременните му изходящи обаждания, по подразбиране 10 обаждания в минута или 1000 на ден), остава running: задава lastError: "rate_limited", опитът не се брои, а чакащите контакти получават items[].nextAttemptAt в края на лимита (според Retry-After, иначе след 5 минути; при тавана на едновременните обаждания на двигателя той изпраща Retry-After: 30). След изтичането му продължава сама. По същия начин изчаква, когато е запълнена квотата на акаунта за едновременни обаждания на агента (relay_lease_limit, по подразбиране 2 текущи обаждания): lastError: "relay_lease_limit", следващ опит след минута, опитът не се брои. Зает номер (destination_busy - към същия номер в момента тече друго обаждане, например от друга задача или от ръчно обаждане, или номерът е заключен 15 минути след несигурно набиране) кара да чака само засегнатия контакт, а не цялата задача: items[].lastError: "destination_busy", items[].nextAttemptAt според оставащото време на заключването на номера (най-малко 30 с, най-много 16 мин, без заключване 90 с), опитът не се брои (countsAsAttempt: false), а останалите контакти продължават да се набират. След 6-ото заето подред (т.е. след 5 изчаквания) контактът отива в review с reviewReason: "dial:destination_busy". Траен проблем с номера (on_dnc, destination_auto_blocked, cannot_call_own_number, cannot_call_volai_number, invalid_number, unsupported_country, blocked_destination) също не спира задачата: контактът отива в review с reviewReason: "dial:<code>" (обаждане не е възникнало, затова опитът има countsAsAttempt: false и не изразходва maxAttempts), задачата остава running, набира останалите контакти и преминава в needs_attention едва когато не остане друга работа. Ако операторът на volai ръчно спре агента на задачата, задачата остава running и нищо не набира; след възстановяването продължава сама. Спрения агент ще познаете по suspended: true в GET /v1/agents/{id}.
paused
Какво означаваСпряна преди следващото обаждане (POST .../pause); започнатото обаждане приключва. Може да се редактира, изтрива, а start я възобновява. Пауза по средата на обаждане оставя reviewReason: "dispatch_in_flight", докато reconcile (планировчикът го пуска до няколко минути) не уреди това обаждане, а дотогава start се отхвърля с task_needs_attention; след това задача с останала работа остава paused, дори други контакти да чакат в review. Пауза, която попадне точно преди самото обаждане (обаждане още не е възникнало), не оставя dispatch_in_flight: планировчикът връща контакта сред чакащите, а задачата веднага е paused без причина. Контакт, чиято обработка е прекъсната от срив, преди да възникне обаждане, се освобождава от reconcile най-рано 15 минути след началото на набирането (claim от 10 минути плюс резерв от 5 минути) и се връща сред чакащите; задача, спряна междувременно от изтекъл claim (dispatch_claim_expired), след това преминава в paused. Ако започнатото обаждане приключи без позната цена, задачата първо преминава в needs_attention с reviewReason: "billing_unknown" и се връща в paused, щом цената пристигне (reconcile я допълва). Ако междувременно намалите maxAttempts така, че нито един контакт, чакащ следващ опит, да няма свободен опит, задачата остава paused (такъв контакт се брои за работа), но start я отхвърля с task_not_startable, докато не увеличите maxAttempts.
completed
Какво означаваВсички получатели имат краен резултат. Може да се изтрие (вече нищо не се таксува).
needs_attention
Какво означаваНещо изисква намеса - обикновено по време на работа се е променила конфигурацията на агента (agent_version_changed), двигателят временно е бил недостъпен (engine_contract_unavailable) или още не е позната цената на вече проведено обаждане (billing_unknown). POST .../reconcile сверява състоянието; може да се изтрие, докато никоя позиция не е останала in_progress. Контакт в състояние review уреждате чрез POST .../items/{itemId}/resolve (retry/skip); контакт с траен проблем с номера (dial:on_dnc, dial:destination_auto_blocked, dial:cannot_call_own_number, dial:cannot_call_volai_number, dial:invalid_number, dial:unsupported_country, dial:blocked_destination или dial:destination_busy след 6-ото заето подред) спира задачата едва когато останалите контакти приключат. Несигурен резултат от набирането (напр. dial:engine_unavailable) праща задачата в needs_attention веднага и останалите контакти не се набират. Когато обаждането зад такова прекъсване (или неизвестна цена) бъде уредено чрез reconcile, задачата преминава в paused, докато някой контакт чака следващ опит, дори други да са в review; в needs_attention само когато не остава нищо за набиране освен контакт в review, и в completed, когато не остава нищо. Набиране, чийто резултат остава несигурен (обаждането не се намира), държи задачата в needs_attention, докато не вземете решение за контакта. При обичайна причина след последното решение задачата преминава в paused, ако остава работа, или в completed, ако всички контакти са приключени или пропуснати. Причина на ниво цяла задача остава в needs_attention до разрешаването си; изключение е budget_exceeded, при което пропускането на всички контакти, чакащи следващ опит, завършва задачата, ако не чака никакво състояние при доставчика. Специалният ред за billing_unknown е описан по-долу.

Точното условие за изтриване: състоянието е едно от отбелязаните по-горе И НИКОЯ позиция няма status: "in_progress" (започнато обаждане, което още не е приключило) - иначе task_not_deletable.

Импорт на получатели (CSV)

POST /v1/tasks/csv-preview разчита CSV и го връща обратно с грешките по отделните редове - НИЩО не се запазва, това е само преглед преди създаване или редакция на задача. Същата функция извиква и формулярът за нова задача в портала.

  • Разделителят се разпознава автоматично (запетая или точка и запетая) по първия ред.
  • Трябва да съществува колона phone - малките и главните букви, интервалите около името и двоеточието след него нямат значение, приемат се и telefon, tel или mobil; прегледът винаги връща колоната като phone. Минава и просто списък с номера без заглавен ред (по един на ред, една колона). Иначе целият CSV пада с единствената грешка phone при заглавния ред.
  • Всяка следваща колона е променлива за системния промпт на агента ({{column_name}}) - най-много 20 колони, име до 64 знака, стойност до 512 знака.
  • Телефонният номер се нормализира както навсякъде в API (normalizePhone) - невалиден или празен номер е грешка само на ТОЗИ ред, останалата част от CSV се обработва нататък.
  • Клетка, която прилича на формула от електронна таблица (започва с =, +, - или @), се отхвърля - защита срещу CSV инжекция при отваряне в Excel.
  • Най-много 1000 реда в един CSV (същият таван като броя получатели на задача) и 1 MB суров текст; цялата задача заедно с материалите трябва да се събере в 2 MB, иначе създаването връща validation.

Отговорът съдържа headers, delimiter, rows (всеки ред със своите грешки), validRows (само безупречните, точно във вида, който очаква recipients в POST /v1/tasks) и обобщено errors. Изпратете validRows направо като recipients при създаване или редакция на задачата.

Бюджет и времеви прозорец за обаждания

Бюджет

budgetHal е ТАВАНЪТ на разходите за цялата задача в хелери. При създаване трябва да покрива поне един опит с максимална продължителност (иначе budget_too_low); при пускане преди ВСЯКО набиране се резервира най-високата възможна цена на опита (reservedBudgetHal) и едва вдигнатото обаждане я прехвърля в spentHal. Резервацията е цената на обаждане с продължителност точно maxDurationSecs (участъкът на обаждането и участъкът на агента, всеки закръглен нагоре до хелер, както при таксуването), а обаждане, прекратено от тавана на продължителността, се таксува най-много за maxDurationSecs, дори да приключи с част от секундата по-късно. Задачата никога не изразходва повече от budgetHal, който сте ѝ задали.

Времеви прозорец за обаждания

callingWindow е {timezone: "Europe/Prague", start, end, days} - днес единствената поддържана часова зона е пражката, start/end във вида HH:MM (краят включва цялата минута: прозорец 09:00-18:00 пуска последно обаждане в 18:00:59, прозорец до 23:59 покрива целия ден), а days са числа 0-6 (0 = неделя). Без задаване важат работните дни 9:00-18:00. Извън прозореца start връща window_closed - или изчакайте отварянето, или променете прозореца с PATCH. Незадължителното expiresAt е краят на кампанията като положително безопасно цяло число в Unix милисекунди. Проверява се при пускане и преди всяко набиране, включително първото. От този момент ново обаждане не започва и опит не се изразходва; текущо обаждане приключва. Хоризонтът за повторения на политиката lorela_enterprise (retryMaxAgeDays, по подразбиране 5), напротив, се брои като N × 24 часа от първия отчетен опит, а не от създаването на задачата.

Опити и продължителност на обаждането

maxAttempts е 1-3 без политика (по подразбиране 1), с retryPolicy: "lorela_enterprise" 1-4 (при създаване по подразбиране 4). Определя най-големия брой автоматични набирания на един контакт, включително първото (1 = без повторения). Брои се всеки опит, при който обаждането е могло да се състои, т.е. и невдигнато, и отказано от набирания, и опит с несигурен резултат. Не се брои опит, който volai е отхвърлил още преди набирането (например блокиран, собствен или невалиден номер, списък за недопускане на обаждания, номер, към който в момента тече друго обаждане, лимити на обажданията); такъв опит има items[].attempts[].countsAsAttempt: false. Не се брои и обаждане, на което операторът не е получил навреме дестинацията, така че телефонът на набирания изобщо не е звънял (опитът има errorCode: "relay_not_routed", обаждането endReason: "rejected"): контактът получава нов опит най-рано след минута (с политиката lorela_enterprise според нейния график), а след третия такъв опит подред отива за проверка с reviewReason: "call:relay_not_routed". Не се брои и обаждане, за което нашият гласов двигател никога не е съобщил и което сме приключили след два часа без съобщение като ненабрано и без цена (опитът има errorCode: "no_provider_report"): контактът се връща сред чакащите и получава нов опит най-рано след минута (с политиката lorela_enterprise според нейния график). Три неотчетени опита подред, дори смесени с опити relay_not_routed, пращат контакта за проверка с reviewReason според последния от тях, т.е. call:no_provider_report; контакти, които такова обаждане е пратило за проверка по-рано (call:failed, а с политика call:unknown), не се възобновяват сами. Без политика таванът важи само за автоматичните набирания: контакт, на който след изчерпване на опитите зададете resolve с action: "retry", получава още едно набиране отгоре. maxDurationSecs (30-300) е таванът на продължителността на ЕДНО обаждане, след който то автоматично се прекратява - продължителността по подразбиране е 120 секунди. Незадължителната политика lorela_enterprise се включва през API или MCP преди първия опит; съществуващите задачи не се променят сами. Вторият опит е най-рано 8 минути след началото на първия реален опит в часовете 08:00-22:00 Europe/Prague. Третият отива в следващия друг прозорец 08:00-09:00 или 17:00-19:00, четвъртият - в противоположния прозорец на следващия календарен ден. Винаги важат и callingWindow и неговите дни; след хоризонта за повторения политиката вече не набира: незадължителното retryMaxAgeDays е цяло число дни 1-30 (без задаване 5) и важи само с политиката. Хоризонтът свършва N × 24 часа след първия отчетен опит на контакта (твърди 24-часови дни, така че при смяна на часа местното време на края се измества с час). Може да се променя с PATCH и след началото на опитите (не при задача running или completed, която не може да се редактира): увеличаването при спряна задача връща контактите, отпаднали само заради хоризонта (reviewReason: "retry_age_expired"), обратно в състояние failed (чакат следващ опит) с нов nextAttemptAt, ако още имат свободен опит и най-близкият слот от графика попада в новия хоризонт и преди expiresAt; намаляването не отваря нищо. Отваря отново само PATCH, който увеличава хоризонта, затова изпращайте retryMaxAgeDays и удължения expiresAt в една заявка. Повтаря само no_answer, missed, busy, приключила гласова поща и вдигнато обаждане, в което набираният нищо не е казал (всичко без контакт с човек). Контакт с човек, отказ и грешка на двигателя спират автоматичното повтаряне; неясен резултат отива за проверка. Последният позволен опит (maxAttempts), който завърши с такъв повторяем резултат (невдигнато, заето, гласова поща или вдигнато обаждане, в което набираният нищо не е казал), приключва контакта като skipped с reviewReason: "retry_policy_exhausted" (както retry_age_expired след изтичане на хоризонта): няма какво да се проверява или повтаря. Неясен резултат от последния опит остава в review със същата причина. Без политика нищо не се променя: последният невдигнат опит си остава review. Проверено обратно обаждане от човек след изчерпване на опитите превежда контакта в completed. DNC, бюджетът и лимитите остават в сила: автоматичното блокиране след 3 неуспеха за 24 часа може да спре и четвъртия опит. При PATCH без maxAttempts досегашният лимит се запазва. С политиката ръчният resolve с action: "retry" връща грешка validation; позволени са само автоматично планираните ѝ опити. Изключение е възстановяването на доказано ненабран изтекъл claim преди първия реален опит: без активен работник, намерение за изпращане, идентификатор на обаждане при доставчика, резервация, цена или обратно извикване и само с доказани откази преди набирането в историята. Възстановяването запазва историята и ограниченията и връща контакта сред чакащите в спряна задача; само не набира. Напомняния не могат да се отварят отново по този начин. Ръчното повторение следователно не заобикаля четирите опита, хоризонта за повторения нито прекратяването след контакт с човек.

Глобална пропускателна способност

Набирането на всички задачи в ЦЯЛАТА платформа (не само във Вашия акаунт) минава през един cron, който работи на всеки 2 минути и обработва най-много 20 позиции на изпълнение - НО този таван се дели между едновременно работещите задачи, а не може да бъде изчерпан от една задача: от ВСЯКА активна задача при едно изпълнение се набира точно ЕДИН получател. Затова дори на съвсем празна платформа една задача никога не набира повече от един получател на 2 минути - сто получатели не приключват за ~20 минути, а за около 200 минути (100 x 2 минути). Планирайте според това времевия прозорец при кампании, чувствителни към времето (например appointment ден преди часа).

Ред за агента

Типичен ход в този ред:

  1. Агентът на задачата трябва да е ПУБЛИКУВАН, а публикуваната версия ТЕСТВАНА (POST /v1/agents/{id}/draft/publish, после POST /v1/agents/{id}/simulate) - иначе по-късният start връща agent_not_ready.
  2. По избор POST /v1/tasks/csv-preview - проверява CSV на получателите, преди да се създаде задачата.
  3. POST /v1/tasks - създава задачата в състояние draft. Нищо не се набира.
  4. GET /v1/tasks/{id} - прочетете issues (проверка на сухо) и се уверете, че задачата е готова за пускане.
  5. При нужда PATCH /v1/tasks/{id} - променете правилата, ВИНАГИ с revision от последното четене.
  6. POST /v1/tasks/{id}/start - пуска набирането. Всяко вдигнато обаждане се таксува веднага.
  7. POST /v1/tasks/{id}/pause по всяко време по време на работа, POST /v1/tasks/{id}/start я възобновява.
  8. След прекъсване (пауза по средата на обаждане, изтекъл claim, обаждане с още непозната цена или несигурно набиране, за което обаждането е намерено) POST /v1/tasks/{id}/reconcile сверява състоянието с реалните обаждания; такава задача с останала работа завършва в paused, дори някои контакти да чакат в review, и може да се пусне отново чрез start. Работеща задача и друга причина (variables_limit, неуспешна проверка на фактура или календар, несигурно набиране, за което обаждането не е намерено) reconcile не променя; контактите за проверка уреждайте чрез resolve.
  9. Контакт, който е приключил за проверка (items[].status: "review"), уредете с POST /v1/tasks/{id}/items/{itemId}/resolve с action: "retry" или "skip". При обичайна причина retry връща контакта сред чакащите, а задачата в paused; skip го приключва и задачата преминава в paused, ако остава работа, или в completed, ако всички контакти са приключени или пропуснати. Задача в paused после възобновете с POST /v1/tasks/{id}/start.
  10. Когато контактът има reviewReason: "dial:destination_auto_blocked", номерът е автоматично блокиран за 30 дни след многократно невдигнати обаждания и самият retry не го отблокира - контактът веднага би се върнал в review. Първо премахнете блокировката (POST /v1/dnc/{e164}/unblock, в MCP unblock_destination, номерът е в items[].phone), после resolve с action: "retry". При dial:on_dnc първо отстранете номера от списъка за недопускане на обаждания (DELETE /v1/dnc/{e164}), иначе пропуснете контакта. При собствен номер на акаунта, номер на друг акаунт във volai, невалиден номер, номер извън Чехия и Словакия и линия с повишена тарифа (dial:cannot_call_own_number, dial:cannot_call_volai_number, dial:invalid_number, dial:unsupported_country, dial:blocked_destination) нов опит не помага: използвайте само skip.
  11. Когато контактът има reviewReason: "billing_unknown", първоначалното обаждане може вече да е протекло, само че цената му още не е позната. Първо извикайте reconcile и проверете свързаното обаждане. retry запомня, че контактът трябва да се опита отново, но ново обаждане не се позволява, докато цената на първоначалното не се установи; после задачата преминава в paused и изисква нов start. skip предотвратява ново обаждане; първоначалното обаждане остава свързано, а резервацията му продължава да се брои в бюджета, докато цената не се установи. Ако остава работа, след skip задачата преминава направо в paused и може да се пусне; без друга работа изчаква в needs_attention и след установяване на цената завършва в completed. Обаждане, което изобщо не е набирано (двигателят го е отхвърлил), никога не се таксува и се установява веднага на 0.
  12. Контакт, който междувременно е купил или се е отписал, пропуснете без обаждане чрез resolve с action: "skip", reason: "customer_purchased" или "unsubscribed", revision, постоянен operationId и evidenceRef, който сочи към документа за покупката или отписването (никога към телефон или имейл). Става при pending контакт в draft задача и при pending/failed в paused задача без неуредено обаждане; опитите, цените и списъкът за недопускане на обаждания се запазват. GET /v1/tasks/{id} после показва доказателството в items[].resolution (reason, operationId, evidenceRef, resolvedAt); същото тяло може безопасно да се повтори, а друг reason или evidenceRef за същия контакт е 409 revision_conflict. Пропускането не записва номера в списъка за недопускане на обаждания: ако контактът не бива да бъде търсен повече, добавете го чрез POST /v1/dnc.
  13. DELETE /v1/tasks/{id} накрая, ако задачата никога не е протекла или вече е приключила и искате записът да се изтрие.

Почти всяко записващо извикване (PATCH, start, pause, DELETE) взема revision от последното прочитане на задачата - същата оптимистична съвместимост като при чернови на агента. Несъответствие връща revision_conflict: заредете задачата отново (GET) и повторете с текущата revision. reconcile е изключение - revision там не е задължителна.

MCP

Същите инструменти като REST (list_tasks, get_task, create_task, update_task, pause_task, resolve_task_item, reconcile_task, start_task) - освен една разлика при пускането.

start_task потвърждава числата

MCP start_task взема допълнително confirmedRecipients и confirmedBudgetHal - те трябва да съвпадат точно с реалния брой получатели и бюджета на задачата, иначе връща validation с изписаните реални стойности и НИЩО не пуска. Така чужд AI агент, действащ по една неясно разбрана инструкция, не може по погрешка да стартира платена кампания с измислени числа - REST POST .../start няма този механизъм, защото там го извиква директно собственикът на акаунта със собствения си ключ.

delete_task и прегледът на CSV в MCP ги няма - изтриването остава в REST и в портала (необратимо действие с преписване на името), а прегледът на CSV е само стъпка от формуляра.

Грешки

  • task_not_found - В акаунта няма задача с този id.
  • task_not_editable - Задачата работи или е приключила (running/completed), или е needs_attention без възможност за възстановяване - правилата ѝ в момента не могат да се променят. Същия код връща и POST .../items/{itemId}/resolve при работеща задача: контакт в review се появява още докато задачата набира останалите контакти и е running - изчакайте да премине в needs_attention или я спрете (POST .../pause).
  • items_immutable - Списъкът с получатели не може да се замени, след като е проведен поне един опит за набиране или контакт е пропуснат с reason (покупка или отписване).
  • budget_too_low - Бюджетът не покрива дори един опит с максимална продължителност на обаждането - увеличете budgetHal или намалете maxDurationSecs.
  • task_not_startable - Задачата не е в състояние draft/ready/paused или няма нито един чакащ получател.
  • task_needs_attention - Преди следващото пускане е нужен POST .../reconcile - нещо (версията на агента, двигателят) се е променило по време на работа.
  • agent_not_ready - Черновата на агента или не е публикувана, или публикуваната версия не е тествана - публикувайте я (POST /v1/agents/{id}/draft/publish) и после тествайте публикуваната версия (POST /v1/agents/{id}/simulate) преди пускането.
  • agent_version_changed - Конфигурацията на агента се е променила ПО ВРЕМЕ на работеща задача - тествайте и рестартирайте чрез reconcile.
  • engine_required - Агентът на задачата не е на двигателя на volai (useForOutboundTasks: true) - изходящите кампании днес изискват двигателя, а не ElevenLabs.
  • engine_contract_unavailable - Двигателят в момента не обявява поддръжка на променливи и ограничения за изходящи задачи - опитайте да пуснете отново по-късно.
  • window_closed - Извън зададения времеви прозорец за обаждания - изчакайте отварянето или променете callingWindow.
  • task_not_running - Пауза е възможна само при работеща задача.
  • item_not_resolvable - Контактът не е в състояние review - решение (retry/skip) е възможно само за контакт, чакащ проверка, и то само при задача, която не работи. Изключение е пропускането с reason (customer_purchased, unsubscribed): то изисква неактивен pending контакт в draft задача или pending/failed в paused, без неуредено обаждане.
  • task_not_deletable - Може да се изтрие само задача без позиция in_progress в състояние draft/ready/paused/completed/needs_attention.
  • revision_conflict - Задачата междувременно се е променила - заредете я отново (GET) и повторете с текущата revision.

Пълната таблица с грешките за всяка крайна точка ще намерите в раздела Задачи в справочника за REST API.