Koppelen
REST API
De volledige referentie van de v1 API. Alles is JSON en fouten hebben één gemeenschappelijke vorm. De prijzen van volai gebruiken gehele honderdsten van een kroon (het achtervoegsel Hal); gekoppelde facturen gebruiken de kleinste eenheid van hun vermelde valuta.
Authenticatie
Elk verzoek bevat een API-sleutel in de header Authorization: Bearer vk_... . U haalt uw sleutel op in het portaal, onder Toegang en API (meer details in de Quickstart).
curl https://volai.cz/v1/balance \
-H "Authorization: Bearer vk_YOUR_KEY"Deze hele API heeft ook een machineleesbare beschrijving op GET /openapi.json (OpenAPI 3.1, geen API-sleutel nodig) - genereer er een client uit, een Postman-collectie, of koppel hem rechtstreeks aan n8n.
Foutformaat
Een fout heeft altijd dezelfde vorm, wat er ook misging:
{
"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"
}
}Deze vijf kunnen vrijwel overal optreden - de endpointreferentie hieronder noemt daarbovenop alleen situatiegebonden codes:
| HTTP | Code | Cause | Betekenis |
|---|---|---|---|
| 401 | unauthorized | request | De Authorization-header ontbreekt of is ongeldig. |
| 402 | insufficient_credit | account | Er is niet genoeg tegoed over voor deze actie. |
| 403 | account_suspended | account | De volai-beheerder heeft het account handmatig opgeschort - schrijf naar support. Belastingdocumenten (GET /v1/billing/documents, /{id} en /{id}/pdf, de MCP-tools list_billing_documents en get_billing_document) blijven tijdens de opschorting beschikbaar. |
| 429 | rate_limited | busy | Snelheidslimiet overschreden - wacht bij de limiet per API-sleutel zo lang als de header Retry-After aangeeft. Niet elke 429 bevat die header (bijvoorbeeld het dagelijkse sms-plafond, een aankoop die nog loopt, of het openbare GET /v1/changelog), volg dan de foutmelding. |
| 500 | internal_error | service | Een fout aan onze kant - probeer het opnieuw. |
400 (ongeldige invoer) en 404 (record niet gevonden) kunnen ook bijna overal optreden, maar hun code is specifiek voor het veld of endpoint - de exacte lijst staat altijd bij de afzonderlijke aanroep hieronder. Een paar acties kunnen ook 502 (een fout van een externe provider - het telefoonnetwerk, ElevenLabs) of 503 (tijdelijk niet beschikbaar, probeer het zo meteen opnieuw) teruggeven.
Wiens probleem het is: cause en action
Elke fout bevat ook cause (vier waarden) en action (één Engelse zin over wat te doen, die u de gebruiker letterlijk kunt voorlezen). cause is een machineleesbaar antwoord op de vraag of uw code, uw account, de tijd of wij de fout moeten oplossen:
| Cause | Wie lost het op |
|---|---|
request | De body, URL, headers of toolargumenten van het verzoek - inclusief een ontbrekende of ongeldige API-sleutel. Corrigeer de invoer en verstuur opnieuw; volai is bereikbaar. |
account | De toestand van uw account: tegoed, limieten, eigendom van een nummer of agent, e-mailverificatie, configuratie, of een record dat op dit account niet bestaat. De body was in orde; wijzig de accounttoestand (in het portaal of met een andere aanroep), of neem het juiste id uit een lijst, en probeer het opnieuw. |
busy | Een tijdelijke vergrendeling of een limiet voor dit moment (een andere bewerking op hetzelfde record loopt nog, een snelheidslimiet). Wacht een paar seconden en verstuur HETZELFDE verzoek opnieuw, wijzig niets. |
service | Wij of onze provider (het telefoonnetwerk, het spraakplatform). Aan uw kant valt niets op te lossen; probeer het zo meteen opnieuw en schrijf bij aanhoudende problemen met de requestId naar support. |
Voor AI-agents (Claude Code, Cursor, uw eigen scripts): ga uit van cause om de gebruiker te vertellen wie de fout oplost. Noem bij request het veld uit message, zeg bij account wat er in het account moet veranderen of welk id gecontroleerd moet worden, wacht en probeer opnieuw bij busy, en spreek alleen bij service over een probleem aan de kant van volai. Meld request, account of busy nooit als een storing bij volai.
De oorzaak wordt bepaald door de code, niet door de HTTP-status: accountquota (agent_limit, tool_limit, api_key_limit, relay_lease_limit) geven 400 en zijn toch account; not_found op een record is account (het antwoord mag niet verraden of het id van iemand anders bestaat), een onbekend pad onder /v1 is request. De volledige toewijzing van code naar oorzaak voor elke bewerking staat in GET /openapi.json (de beschrijving van elk foutantwoord).
De melding bij de code validation noemt het veld (een pad met punten in de body, bijvoorbeeld dataFields.0.type), de feitelijke waarde en het toegestane maximum, bijvoorbeeld Field "knowledge" is 210000 characters long, the maximum accepted is 200000. of Field "systemPrompt" is required. Productlimieten (60 / 6000 / 300 / 20000 tekens voor de naam, instructies, het doel en de kennis van een agent) komen als eigen codes invalid_name, invalid_system_prompt, invalid_goal, invalid_knowledge met het exacte getal. Een onbekend veld in een strikte body (agentconcept) geeft Unknown field "revision". Remove it; the documentation lists the accepted fields.
requestId en docsUrl
Elk v1-antwoord (succes of fout) bevat een uniek verzoek-ID in de header X-Request-Id (req_ gevolgd door 16 hexadecimale tekens) - bij een fout staat het ook in de body als requestId. Een opnieuw afgespeeld idempotent antwoord krijgt een EIGEN actuele requestId - header en body spreken elkaar nooit tegen, ook al is de rest van het antwoord verder identiek aan de eerste keer. Stuur dit ID naar support en zij vinden precies dit ene verzoek in het logboek.
Foutbodies bevatten ook docsUrl - een link die rechtstreeks terugverwijst naar dit onderdeel.
Bij agentconcepten (zie het onderdeel Agentconcepten hieronder) bevat de fout revision_conflict ook currentRevision - de huidige revisie. Laad die via GET .../draft en bekijk het verschil voordat u het opslaan opnieuw probeert - stuur niet blindelings dezelfde body met dezelfde expectedRevision opnieuw.
{
"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"
}
}Een fout subscription_confirmation_required (409) bevat ook confirmUrl - een link naar de portaalpagina waar een bij het account ingelogd persoon een wijziging bevestigt die verhoogt wat van de opgeslagen kaart mag worden afgeschreven (zie PATCH /v1/subscription). Het verzoek heeft niets gewijzigd; de link is 24 uur geldig en kan eenmaal worden gebruikt.
{
"error": {
"code": "subscription_confirmation_required",
"cause": "request",
"message": "This change was not applied, because it raises what may be charged to the saved card, and only a person signed in to the portal can confirm that. Nothing was changed. Open confirmUrl in a browser signed in to the account, review the change and confirm it; the link is valid for 24 hours and can be used once.",
"action": "Nothing was changed, on purpose: either the change raises what may be charged to the saved card, or the account is a consumer who must make the request to start the plan in person, so only that person can confirm it. Give the confirmUrl from this error to the account owner to open while signed in to the volai portal; a confirmation link is valid for 24 hours and works once, a link to the plan form stays valid. Do not send the same request again: it only produces a new link. This is not a volai outage.",
"confirmUrl": "https://volai.cz/kredit/pausal/potvrdit?t=eyJ1aWQiOiJ1X3NhbXBsZSJ9.4f1a9c0e",
"requestId": "req_9f2b7a1c4e6d8f0a",
"docsUrl": "https://volai.cz/en/docs/api#errors"
}
}Headers
Nadat de sleutel is gecontroleerd, bevat ELK antwoord (succes of fout) RateLimit-Limit, RateLimit-Remaining en RateLimit-Reset (seconden tot het huidige venster wordt gereset) - een 429 door de limiet per API-sleutel voegt ook Retry-After toe met hetzelfde aantal seconden. Een 429 rate_limited door de limiet voor uitgaande gesprekken (POST /v1/calls) bevat zijn eigen wachttijd in Retry-After - hoe lang het duurt tot die limiet vrijkomt, wanneer dat bekend is.
401 bevat een header WWW-Authenticate: Bearer realm="volai", error="invalid_token" (RFC 6750), zodat een HTTP-clientbibliotheek kan zien waarom het mislukte zonder de body te parsen.
Onbekend pad en verkeerde methode
Een onbekend pad onder /v1 (ook kaal /v1) geeft een JSON-404 met dezelfde foutvorm als overal elders - {"error":{"code":"not_found", ...}} - voor elke methode (GET, POST, PUT, PATCH, DELETE, OPTIONS en HEAD). Uitzondering: de verkeerde METHODE op een BESTAAND pad (bijvoorbeeld DELETE /v1/balance) blijft een gewone Next.js-405 zonder JSON-body - de enige plek in heel v1 waar een fout geen gedocumenteerde vorm heeft.
Idempotentie
POST /v1/messages, POST /v1/calls, POST /v1/rings, POST /v1/voice-messages, POST /v1/relay, POST /v1/numbers, POST /v1/numbers/orders, POST /v1/sms-numbers, POST /v1/voice-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, POST /v1/credit/auto-topup/setup, POST /v1/subscription/checkout, POST /v1/subscription/pay en POST /v1/subscription/card accepteren een header Idempotency-Key. De sleutel is globaal voor die API-sleutel, ongeacht HTTP-methode, pad of body. Geef elke afzonderlijke logische actie, over alle endpoints en request bodies heen, een nieuwe waarde; hergebruik een waarde alleen bij het opnieuw proberen van exact hetzelfde logische verzoek, meestal na een timeout waarbij u niet weet of de eerste poging is doorgekomen. Binnen 24 uur krijgt u dan exact hetzelfde antwoord als de eerste keer, ook een foutantwoord als de eerste poging mislukte, en wordt niets twee keer verstuurd of aangeroepen. U kunt het order-ID van uw eigen app gebruiken plus de naam van de specifieke actie. Een gelijktijdig verzoek met dezelfde sleutel dat nog loopt (het antwoord is nog niet opgeslagen) geeft 409 idempotency_in_progress - probeer het zo meteen opnieuw.
Bij POST /v1/numbers/orders wordt de sleutel duurzaam aan de bestelling gekoppeld voordat de operator wordt aangeroepen; hergebruik met een ander adres geeft 409 idempotency_conflict. Geeft de operator na een aankoop een dubbelzinnig antwoord, dan moet de status van de bestelling mogelijk worden nagekeken.
curl -X POST https://volai.cz/v1/messages \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: objednavka-4471" \
-d '{"to": "+420777123456", "body": "Bedankt voor uw bestelling."}'Snelheidslimieten
60 verzoeken per minuut per API-sleutel (MCP deelt dezelfde limiet - het gebruikt dezelfde sleutel). Overschrijding geeft 429 en een header Retry-After met het aantal seconden tot de volgende poging.
POST /v1/messages heeft daar bovenop een eigen plafond: hoogstens één sms per 2 seconden en 100 sms per account per dag. Verzenden vanaf een mobiel nummer (fromNumber) heeft een extra dagplafond per account: 20 berichten in de eerste 30 dagen nadat u het nummer hebt gekocht, daarna 50. GET /v1/sms-numbers toont hoeveel er over zijn en wanneer de limiet wordt gereset (dailySendRemaining, dailySendResetsAt).
Uitgaande gesprekken (POST /v1/calls, relay en testgesprekken) delen een eigen plafond: 10 gesprekken per minuut en 1000 per dag per account, met hoogstens 2 gelijktijdig lopende gesprekken van de ingebouwde agent.
Hoeveel u van de limiet van 60/min nog over hebt, ziet u al voordat u 429 raakt - in de headers RateLimit-Limit, RateLimit-Remaining en RateLimit-Reset, die elk antwoord bevat (zie het onderdeel Foutformaat > Headers hierboven).
Paginering
GET /v1/messages en GET /v1/calls accepteren limit (maximaal aantal records om terug te geven - standaard 50, begrensd op 200 ook als u meer vraagt) en before (een tijdstempel in milliseconden - geeft alleen oudere records terug).
hasMore en nextBefore
Beide antwoorden bevatten ook hasMore (boolean - er bestaat een oudere pagina) en nextBefore (de tijdstempel van het laatst teruggegeven record, of null wanneer hasMore false is). Stuur voor de volgende pagina de nextBefore van het vorige antwoord als nieuwe before - geen handmatige offsetberekening.
before is EXCLUSIEF (geeft alleen records terug die strikt ouder zijn, nooit even oud) - het laatste record van de vorige pagina komt op de volgende niet opnieuw voor. In het zeldzame geval dat twee records precies dezelfde milliseconde-tijdstempel hebben op de paginagrens, kan de tweede wegvallen. Ontdubbel op id - reken er niet op dat pagina's nooit iets overslaan.
GET /v1/calls?direction=in of ?direction=out doorzoekt de geschiedenis in batches met een vast maximum aan batches - hasMore: true kan dus betekenen "er is zeker nog een pagina" of "we hebben batches tot het maximum doorzocht en het einde van de geschiedenis nog niet bereikt". false betekent altijd dat de geschiedenis in die richting werkelijk is afgelopen.
Versiebeheer
De huidige versie van de openbare interface is 1.58.0 (GET /openapi.json, veld info.version - zie de alinea over de machineleesbare API-beschrijving in de inleiding hierboven). Drie regels bepalen hoe de versie verandert:
- Binnen
v1komen alleen OPTIONELE velden en nieuwe endpoints erbij - bestaande velden verdwijnen nooit, veranderen niet van type en niet van betekenis. Een integratie die alleen velden leest die ze kent, wordt door een update nooit gebroken. De enige uitzondering was versie 1.5.1, dieansweredByniet meer teruggaf voor INKOMENDE gesprekken - het veld had daar nooit een zinvolle waarde (zie Changelog hieronder). - Een verplicht veld, of een wijziging in de betekenis van een bestaand veld, verschijnt alleen als nieuwe hoofdversie (
v2) - nooit als stille wijziging inv1. - Stuur bij agentconcepten (zie Agentconcepten hierboven) altijd precies het object terug dat u uit
GET .../drafthebt gelezen - de body vanPUT/POSTis.strict(), dus een onbekende sleutel (bijvoorbeeld uit een nieuwere versie die uw integratie nog niet kent) geeft 400validationin plaats van stilzwijgend te worden weggelaten.
Changelog
- 5 Nov 2026 (1.58.0): The new
POST /v1/voice-sms-numbers/countries/{country}/waitlist(no body) and the MCP tooljoin_voice_sms_country_waitlistsign the account up for the waitlist of a country that is not in the offer; the response is{country, joined: true, alreadyJoined}and nothing is charged. A country in the offer or outside the map returnsvalidation.GET /v1/voice-sms-numbers/countriesand MCPlist_voice_sms_countrieshave a newwaitlistedfield (ISO codes of the countries the account signed up for). Ordering a number does not change. - 5 Nov 2026 (1.57.0): The agent
languageaccepts 20 values:cs,sk,en,de,pl,nl,pt,sl,ro,da,et,lv,bg,fi,el,fr,it,hu,lt,sv. A number in another country can be routed to an ElevenLabs agent; the errorel_outbound_trunk_not_configured(HTTP 503, causeservice) means our outbound trunk is missing. REST and MCP messages stay in English. - 4 Nov 2026 (1.56.0):
GET /v1/voice-sms-numbers/countriesand the MCP toollist_voice_sms_countriesreturnmonthlyFeeHalfor every country (hellers per 30 days excluding VAT).POST /v1/voice-sms-numberswithcountryorders the number at that country's fee from the offer; no fee confirmation is added. The MCP toolorder_voice_sms_numberrequiresconfirmedMonthlyFeeHalequal to the fee of the chosen country; any other value is avalidationerror and nothing is ordered, while the Prague number still confirms 4900. A number record carries themonthlyFeeHalof its country and the same amount is charged by the first fee and every renewal. A country without a set fee does not appear in the offer. - 4 Nov 2026 (1.55.0):
POST /v1/voice-sms-numbersaccepts an optionalcountry(ISO 3166-1 alpha-2, uppercase); without it the Prague number is ordered as before. The countries the account may order are returned byGET /v1/voice-sms-numbers/countriesand the MCP toollist_voice_sms_countries; a country outside that list is rejected withcountry_not_offeredand nothing is charged. Number records carrycountryandsms, and a number outside Czechia rejects sending SMS. Outbound calls go only to Czechia, and to the country of the number only where thecountry_and_czzone is enabled; the gate rejects any other destination. At most 2 numbers per country. - 4 Nov 2026 (1.54.0):
regionsinGET /v1/numbers/available, theregionparameter ofPOST /v1/numbersand thesearch_available_numbersandbuy_numbertools now knowjihocesky,karlovarsky,kralovehradecky,liberecky,moravskoslezsky,olomoucky,pardubicky,plzensky,stredocesky,ustecky,vysocina,zlinsky. Regional stock is small, sopool_emptyis more common there: usejoin_number_waitlistororder_number_from_region. - 4 Nov 2026 (1.53.0): The offer catalogue now knows all 14 regions. A regional offer (
jihocesky,karlovarsky,kralovehradecky,liberecky,moravskoslezsky,olomoucky,pardubicky,plzensky,stredocesky,ustecky,vysocina,zlinsky) appears inregionsofGET /v1/numbers/availableand in the MCP descriptions only once it is on sale; until then it is rejected as an unknownregion. TheregionLabeltext changes: a number from a region carries the English region name, such asMoravian-Silesian Region. - 4 Nov 2026 (1.52.1): A header value with three asterisks in a row (a leftover of the mask, such as
Bear***abc) is rejected byPUT /v1/webhookandset_webhookwithinvalid_webhook_headers; previously only the whole mask was. The descriptions ofget_webhook,send_test_webhookand the MCP server instructions mentionheaders, and the docs now say the test event carries custom headers. Value limits (1 to 1000 printable ASCII characters) added to OpenAPI and the webhooks page. - 3 Nov 2026 (1.52.0):
PUT /v1/webhookand MCPset_webhookaccept optionalheaders(at most 5 headers, values up to 1000 characters, printable ASCII only).GET/PUT /v1/webhookandget_webhook/set_webhookreturn a newheadersfield with masked values (Bear***,{}when none are set). Omittingheaderskeeps the stored ones,{}removes them, and a value equal to the mask keeps the stored one. Changing the URL to a different domain drops stored headers, and a mask is rejected there.host,content-type,content-length,volai-signatureand hop-by-hop headers cannot be set. Headers go with every event includingwebhook.testand are not sent when a delivery is redirected to a different domain. New errorinvalid_webhook_headers(400). - 3 Nov 2026 (1.51.0): REST
POST /v1/voice-messageswith the body{to, text, repeat?}(text1 to 500 characters,repeat1 to 3, default 1; an unknown field includingringingTimeoutSecsis rejected withvalidation) returns{id, status, from}and accepts anIdempotency-Keyheader likePOST /v1/calls. The MCP toolsend_voice_messagecalls the same service. The result is inGET /v1/calls/{id}askind: "voice_message"with avoiceMessageblock (text,repeat,repeatPlayed,completed) and in thecall.completedorcall.no_answerwebhook; the call summary e-mail is not sent for a voice message. New errorsvoice_messages_disabled(503, the feature is switched off),voice_message_unavailable(503, the voice engine cannot accept messages) andemail_unverified(403). - 1 Nov 2026 (1.50.0): REST
GET,PUTandDELETE /v1/account/spend-cap(response{spendCap: {enabled, capHal, warnShare, inboundAboveCap, month, monthSpendHal, remainingHal, reached, updatedAt}};PUTtakescapHalin whole hellers excluding VAT from 5,000 to 100,000,000,warnSharefrom 0.5 to 0.99 with a default of 0.8, andinboundAboveCap, which is not overwritten on a change when the field is omitted) and MCPget_spend_cap,set_spend_capandclear_spend_cap, all overlib/service/spend-cap.tslike the Monthly spending cap section under Credit. Once the cap is reached,POST /v1/calls,/v1/messages,/v1/rings,/v1/relay, the test call and task dialling are refused with the new errorspend_cap_reached(HTTP 402, nothing is charged); an inbound agent call withoutinboundAboveCap: trueends after one sentence and the call carriesendReason: spend_cap. The month's spend (Europe/Prague) is counted on every account, also without a cap, and includes the part covered by the monthly plan credit. Number fees, the Done for you round and the monthly plan are not counted; the cap does not stop calls already running or CDRs booked after midnight, forwarding without an agent, or outbound calls from a number in Own SIP PBX mode withinboundAboveCap: true. Warning and reached e-mails go at most once a month and are released again when the cap changes. RESTPOST /v1/agents/{id}/draft/knowledge-importwith a body of{url}or{text, filename}(a.txtor.mdfile up to 1 MB; the portal takes a file up to 800 kB), optionallymode(appendby default, orreplace),preview(only returns the result, writes nothing) andexpectedRevision. The MCP toolimport_agent_knowledge(id,urlortext+filename, the same optional fields) calls the same service,lib/service/knowledge-import.ts. The text is written intoknowledgeof the DRAFT and the source into the newknowledgeSourcesfield (at most 20 sources:urlorfilename,importedAt,chars), which the agent shape now also returns (GET /v1/agents/{id}, list and detail). What does not fit into 20,000 characters is summarized by a model (method: summarized) or cut (truncated). Errors:invalid_import_url,private_address,invalid_knowledge,import_too_large,revision_conflict,rate_limited(10 imports per hour per account). A fetched page is third-party text: the preview and the misuse check when the draft is saved guard against it, and REST saves it right away withoutpreview. RESTPOST /v1/account/cancellation-requestwith a body of{reason, note?}(reasonis one oftoo_expensive,callers_hang_up,missing_feature,not_needed,other;noteat most 500 characters) and the MCP toolrequest_account_cancellationcalllib/service/account-cancellation.ts, the same service as the form in Settings. Response{cancellationRequest: {reason, note, requestedAt, alreadyRequested}}; a repeated request returns the original record withalreadyRequested: true(HTTP 200) and sends no further e-mail. There is at most one request per account, it changes the account in no way and deletes nothing; the volai owner gets an alert with the reason and the customer a confirmation in the account language. Errors:validation,user_not_found. When you click thumbs down in the call e-mail, the rating is saved right away and on the page that opens you can pick a reason and add a note in a single form. Before, there were only five separate buttons and no note. When you write to podpora@volai.cz, you get an automatic confirmation that the message arrived, saying that we usually reply within one business day, with a link to the status page. At volai.cz/en/trust (in Czech volai.cz/duvera) you find in one place where the data of calls, SMS and your account lies, who touches it, how long we keep it, how we protect it and what we cannot do yet. The website gains a Call from your browser button that lets you try the volai demo agent with a call right from your browser, without signing up. The call is a demo, it is recorded and the agent speaks Czech. We are switching it on gradually, so it may appear only after a while.public/llms.txt,public/SKILL.md,/docs/apiand/docs/mcp(cs and en) match the code:create_calendar_eventcreates an appointment only withconfirm: trueand the calendar deletes nothing; the number for calls and SMS is in pilot mode (voiceSmsNumbersMode()isadmin), other accounts getvoice_sms_numbers_unavailable(until 3 Nov 2026; since then the mode isonand any account orders the number itself). The trial call'sremainingLinestates that an unanswered call also counts towards the limit of 3 calls per 24 hours. - 1 Nov 2026 (1.49.1):
CAMPAIGN_QUOTA_EXPIRES_ATinlib/service/call-quotas.tsis2026-11-15T23:00:00+01:00(22:00 UTC; previously2026-11-14T23:00:00+01:00) andPETCENTER_REMINDER_DISPATCH_ENDstays2026-11-21T19:55:00+01:00(18:55 UTC).petcenterCampaignQuotaActive()and therunPetcenterOutboundTaskTicktick therefore dial the main campaign until 15 November 22:00 UTC andcoupon_remindertasks still until 21 November 18:55 UTC. The stored quota of the campaign agent (call-quota:agent) has its ownvalidUntil, which is moved separately viaPUT /api/admin/call-quotasfrom2026-11-14T22:00:00Zto2026-11-15T22:00:00Z. The other conditions of the exception (theVOLAI_PETCENTER_CALL_QUOTA_ENABLEDswitch, account, agent, outbound task, eight relay names) are unchanged. - 30 Oct 2026 (1.49.0):
POST /v1/tools,PATCH /v1/tools/{id}and MCPcreate_toolandupdate_toolaccept an optionalrequireConfirmationfield (boolean, defaultfalse; any other type is400 validation). Responses that carry a tool (GET /v1/tools,GET /v1/tools/{id},POST,PATCH, MCPlist_tools) always return it as a boolean;POST /v1/tools/testdoes not have it. APATCHwithout the field keeps the stored value (neither an older client nor a stale portal page switches the gate off),falseswitches it off. The engine receivesrequire_confirmation: trueonly for a tool with the gate on and only for an agent on the engine incsorsk;falseis never sent explicitly. An agent inen,deorpland an ElevenLabs agent ignore the field, and saving the tool is not refused because of language, since a tool belongs to the account and is shared by several agents. Editing a tool does not rewrite agents on the engine: the flag reaches them at the agent's next save (PATCH /v1/agents/{id}) or publish (POST /v1/agents/{id}/draft/publish). With a running campaign, re-publishing an unchanged tested draft keeps the tested mark and the task carries on, now with a gate the test did not cover; a directPATCH /v1/agents/{id}clears the mark and the task stops withagent_version_changed. APATCHthat changes onlyrequireConfirmationdoes not call ElevenLabs and does not re-verify the tool address (no DNS lookup), so the gate can be switched even while the tool's domain is unreachable. The gate reads only the caller's last utterance and does not know the agent's question: it catches an unclear consent, not every “yes” given to a different question. Portal: the tool editor (/en/agent/tools/newand/en/agent/tools/[id]) stores the choice in the same field with the switch Require the caller's clear consent; a hidden form flag distinguishes an unchecked switch from a form that did not know the switch, so a stale page cannot silently switch the gate off, and after saving a changed gate the form says that for a running agent it takes effect only after that agent is published. The method, the parameter type and source are submitted from form state and the description is a controlled field, because React 19 resets the form after every form action and the second save after an error used to send the first option of a select and the old description.GET /v1/subscriptionand MCPget_subscription:availablesays whether a NEW plan can be started right now, and it istrueonly when the offer is on for the account and the account has no running plan (active,past_due); otherwise it isfalse. It used to betruefor a subscriber too, althoughPOST /v1/subscription/checkoutreturned409 subscription_exists. It istrueagain once the plan has ended (canceled). A client that usedavailableto tell whether the account has a plan should readsubscriptioninstead.recommendationdoes not depend onavailableand a subscriber still receives it.PATCH /v1/subscriptionand MCPupdate_subscription: a tier the account is already switching to (nextTier) counts as done (200, not400 invalid_tier), so the same body can be sent again after an outage in the middle and finishes what is left. Exceptions: the current tier with no scheduled change staysinvalid_tier; going back to the current tier (it cancels the scheduled change) does not count as done on a repeat and ends withinvalid_tier; on a plan set to end atieralone still returnsinvalid_state(withcancelAtPeriodEnd: truein the body it is a repeat and goes through).invalid_stateis now also listed among the errors ofPATCH /v1/subscriptionin OpenAPI and in the documentation.GET /v1/balanceand MCPget_balance: the noticessubscription_exhausted_topup,subscription_cap_reachedandsubscription_runwaycarry a new optional fieldcancelAtPeriodEnd: true(present only when true: the plan does not renew,renewsAtis the end of the plan and no new monthly credit arrives). The format ofrenewsAt(epoch ms) does not change and is documented as the same moment assubscription.periodEnd(ISO 8601). The e-mails about a failed renewal and about the end of a plan have their own wording when the account has no saved card (the card is missing, nothing was charged). An outbound agent call on the engine that stayedringingorinitiatedwithout aconversationIdand was closed asfailedwith price 0 by the watchdog (or by the taskreconcile) after two hours now behaves in a task likerelay_not_routed: the attempt hasitems[].attempts[].countsAsAttempt: falseanderrorCode: "no_provider_report", uses up neithermaxAttemptsnor thelorela_enterpriseschedule, the budget reservation is released, nothing is charged, and the contact getsnextAttemptAtno sooner than 60 seconds later.relay_not_routedandno_provider_reportattempts are added together in one series: after the third uncounted attempt in a row the contact goes toreviewwith thereviewReasonof the last one, that iscall:no_provider_report; earlier only when the next attempt does not fit beforeexpiresAtor thelorela_enterprisepolicy allows no further attempt. A call that a late post-call completed with aconversationIdwas seen by the engine, so it counts as before. Items that an earlier call of this kind had already sent to review (call:failed, orcall:unknownwith the policy) are not reopened automatically.errorCodeandreviewReasonare free strings, only a new value was added; an integration that reads reports from tasks should expect one more uncounted attempt and one more review reason. A pause (POST .../pause, the batch pause for a campaign) between the dial intent being written and the provider request used to leavereviewReason: "dispatch_in_flight", the active claim, the reservation and anin_progressitem with no call and no attempt;startthen returnedtask_needs_attention,resolvereturneditem_not_resolvable,DELETEreturnedtask_not_deletableandreconcilechanged nothing. After such a pause the task processing now releases the intent, the claim and the reservation itself and returnstask_paused(no call was placed): the contact ispendingagain (orfailedwith a free attempt) and the task ispausedwithout areviewReason, so the firststartsucceeds. The cleanup is retried up to three times on a concurrent write (revision_conflict), as is the cleanup at the last gate before the provider, whose result used to be discarded.reconcile(the cron,POST /v1/tasks/{id}/reconcile, MCPreconcile_task) additionally releases an intent that has no call record and no attempt result and whose claim and five-minute margin after it have both expired, that is at the earliest 15 minutes after the dial began (the claim lives for 10 minutes): it returns the reservation and puts the contact back to waiting, or completes it after a verified callback. A task that the expired claim had meanwhile stopped (dispatch_claim_expired) endspausedwithout a reason and has to be started again (start); a task the claim did not stop keeps running. An uncertain dial and a call that may have been created are left as they are. Tasks that got stuck earlier are repaired by a singlereconcileand end aspausedwithout a reason (the pause was intentional), they do not restart on their own. Response shapes do not change.POST /v1/dncand MCPadd_to_dncaccept eithere164(one number, the response{ "added": true, "e164": ... }is unchanged) or the new fielde164s(1 to 500 numbers, written with a singleSADDcommand). Both fields together, neither, an empty array and more than 500 entries are400 validation; in OpenAPI both fields are optional and the rule "exactly one of them" is enforced by validation only. An entry ofe164smust be a string of at most 256 characters, any other entry fails the whole request with400. The response toe164scarriesaddedCount,alreadyOnListCount,rejectedCountandrejected(entries{ index, input, reason: "invalid_number" }, whereinputis the entry as sent, cut to 32 characters).addedCount + alreadyOnListCount + rejectedCountalways equals the number of entries sent; a number repeated in the request or written in another format counts as already on the list. An entry that is not a valid number (including longer text, for example two numbers in one cell) does not fail the request, the valid entries are always added, and an input of only invalid entries is still200, so checkrejectedCountafter every response. The request is idempotent (repeating it returnsaddedCount: 0). In OpenAPI the response ofPOST /v1/dncis ananyOfof both shapes and the booleanaddedstays only in the single-number response. Numbers are still removed one at a time (DELETE /v1/dnc/{e164}). The destination check at dial time reads oneSISMEMBERinstead of the whole set, so a list with thousands of numbers no longer transfers tens of kB on every dial; the listingsGET /v1/dncandlist_dncare unchanged. RESTPOST /v1/tasks/{id}/items/{itemId}/resolveand MCPresolve_task_itemacceptreasonwith the valuecustomer_purchasedor, new,unsubscribedon the audited skip (action: "skip"withoperationIdandevidenceRef).evidenceRefis an opaque reference to the purchase or unsubscribe record, never a phone number or e-mail. Conditions are unchanged: adrafttask (contactpending) or apausedtask (contactpendingorfailed), no active claim, dial intent or reservation, and the skip does not resolve contacts inreview. Attempts, costs and the do-not-call list stay as they are. The reason is part of the fingerprint (resolution.fingerprint), so repeating the same operation writes nothing and the sameoperationIdwith a differentreasonorevidenceRefreturns409 revision_conflict.items[].resolution.reasonin every response that carries a task (GET /v1/tasks,GET /v1/tasks/{id},POST .../resolve, MCPlist_tasks,get_task,resolve_task_item) is now the enumcustomer_purchased|unsubscribed(a constant in OpenAPI before): a client that validates responses against the old specification must allowunsubscribed. An unknownreasonin the request returns400 validation. Descriptions in OpenAPI, MCP and the API, MCP and Tasks documentation are updated. - 26 Oct 2026 (1.48.0): REST:
GET /v1/voice-sms-numbers,POST /v1/voice-sms-numbers(no body,Idempotency-Keysupported, returns 202 with the number in theawaiting_registrationstate),GET /v1/voice-sms-numbers/{e164},PATCH /v1/voice-sms-numbers/{e164}(routing:agenton the in-house engine, ornone),POST /v1/voice-sms-numbers/{e164}/cancel(cancelling before the registration is approved is free) andDELETE /v1/voice-sms-numbers/{e164}(releasing an active number is irreversible and the period already paid is not refunded; when the cancellation with the carrier fails, the number stays in thereleasingstate and the response is 502release_failed). MCP:list_voice_sms_numbers,order_voice_sms_number(confirmedMonthlyFeeHalmust be 39000, otherwise nothing is ordered),set_voice_sms_number_routingandcancel_voice_sms_number; releasing is deliberately not available over MCP. Number states:awaiting_registration,active,rejected,releasing. SMS from the number are sent withfromNumberinPOST /v1/messagesandsend_sms(+420 recipients only, nodeliveryStatus), received SMS are still read withGET /v1/messages?direction=inand themessage.receivedwebhook; calls from the number are placed by an agent (POST /v1/callswithagentId, to +420 only, one outbound call at a time); a bridge between two numbers withfromand a ring from it are not available (unsupported_for_number_type). New error codes:voice_sms_numbers_unavailable,voice_sms_number_limit(at most 2 numbers per account, ordering needs a first top-up),voice_sms_order_limit,voice_sms_number_out_of_stock,unsupported_for_number_type(forwardandsipare not supported for this number) anddestination_not_allowed(the number can call +420 numbers only). The fee of 390 CZK per 30 days is charged asnumber_fee; thenumber_feenotice inGET /v1/balanceand MCPget_balancecounts these numbers too, and a debt older than 7 days releases the number just like other numbers. - 26 Oct 2026 (1.47.0):
GET /v1/accountand MCPget_accountreturn a newtestCallsfield next toaccount, withmax(3),remaining(test calls left in the current window) andresetAt(when the window ends, epoch ms; the window runs 24 hours from the first attempt;nullwhen there was no attempt in the last 24 hours).POST /v1/agents/{id}/test-calland MCPtest_callcarry the same field right after dialing. Every dialed attempt counts, including an unanswered one, which is not charged; an attempt that never dialed (agent, credit or capacity error) does not count. The field is additive, existing response shapes do not change.PATCH /v1/accountdoes not return it. - 23 Oct 2026 (1.46.1):
POST /v1/tasks/{id}/reconcile, MCPreconcile_taskand the task cron settle a task after recovery by the work that is left:pausedwhile any contact is waiting for another attempt (pending,failed,in_progress), even if other contacts are inreview;needs_attentiononly when nothing is left to dial but a contact inreview(the task'sreviewReasonis the reason of the first such item);completedwhen nothing is left.reviewReasonis set only forneeds_attentionand empty otherwise; after a pause in the middle of a dial and after an expired claimactiveItemId,activeClaimTokenandactiveClaimUntilare released as well. It applies to four recoveries: after a pause in the middle of a dial (dispatch_in_flight), after an expired claim (dispatch_claim_expired), after a call with a previously unknown price is settled (billing_unknown) and after an uncertain dial is resolved (dial:*). Before, recovery put review before work, so pausing a running task sent a task with hundreds of waiting contacts toneeds_attention, whichstartandPATCH(task_not_editable) refused. An uncertain dial for which no call can be found keeps the task inneeds_attention(the contact holds an unresolved dial intent andstartwould refuse it) untilPOST .../items/{itemId}/resolvedecides that contact; the same applies after a call with an unknown price is settled (billing_unknown) while another contact holds an unresolved dial intent. A running task and task-level reasons (for examplevariables_limit) are not changed byreconcile. With theretryPolicy: "lorela_enterprise"policy the state of a contact after the last allowed attempt (maxAttempts) changes too: ano_answer,missed,busy, completed voicemail without a human or an answered call in which the person said nothing closes it asskippedwithreviewReason: "retry_policy_exhausted", likeretry_age_expiredwhen the horizon runs out (before:review); the link to the last call (providerCallId) stays. An uncertain result of the last attempt stays inreviewwith the same reason, and the policy still rejects a manualresolvewithretry. A verified human callback after this closing turns the contact intocompleted(afterretry_age_expiredandtask_expiredit staysskippedwithcallbackContactCallId). Tasks without the policy do not change.POST .../items/{itemId}/resolvedoes not change: the last resolved item in review turns a task with work left intopaused. Tasks that got stuck inneeds_attentionbefore the rollout do not repair themselves (their reason is derived from an item):resolvewithskipover the items in review frees them once. - 23 Oct 2026 (1.46.0):
POST /v1/tasks,PATCH /v1/tasks/{id}, MCPcreate_taskandupdate_taskaccept optionalretryMaxAgeDays: a whole number 1 to 30, the retry horizon of theretryPolicy: "lorela_enterprise"policy in days from the recipient's first counted attempt. The public task (GET /v1/tasks,GET /v1/tasks/{id}, the create and PATCH responses, MCPget_taskandlist_tasks) returns it only when it was set; an absent field means the default 5 days as before, so tasks without the field behave unchanged. The field needs the policy (in the same body at creation, in the body or already stored on the task on PATCH), otherwisevalidationis returned with the fieldretryMaxAgeDays; so is a value outside 1 to 30 or a non-integer. Omitting it on PATCH preserves the value. UnlikeretryPolicy, it can be changed after attempts have started, in the states PATCH has always accepted (runningandcompletedstill not:task_not_editable). Raising the value on a paused task returns items that areskippedwithreviewReason: "retry_age_expired"topendingwith a newnextAttemptAtwhen they have a counted attempt, a free attempt belowmaxAttempts, no verified callback and their first counted attempt + the new horizon (andexpiresAt, when set) is in the future; lowering it reopens nothing and contacts outside the new horizon stay skipped. The attempt ladder (8 minutes after the first, the 08:00-09:00 and 17:00-19:00 windows, four attempts) does not change, only the horizon does. Portal: the task detail of a task with the policy shows a line about the retry schedule and horizon, the calling rules have a Retry horizon (days) field and offer four attempts for a task with the policy. OpenAPI, the MCP tool descriptions, SKILL.md and the Tasks guide are updated. - 23 Oct 2026 (1.45.1):
CAMPAIGN_QUOTA_EXPIRES_ATinlib/service/call-quotas.tsis2026-11-14T23:00:00+01:00(22:00 UTC; previously2026-10-31T23:00:00Z) andPETCENTER_REMINDER_DISPATCH_ENDis2026-11-21T19:55:00+01:00(18:55 UTC; previously 5 November).petcenterCampaignQuotaActive()and therunPetcenterOutboundTaskTicktick therefore dial the main campaign until 14 November 22:00 UTC andcoupon_remindertasks until 21 November 18:55 UTC. The stored quota of the campaign agent (call-quota:agent) has its ownvalidUntiland is moved separately viaPUT /api/admin/call-quotas. The other conditions of the exception (theVOLAI_PETCENTER_CALL_QUOTA_ENABLEDswitch, account, agent, outbound task, eight relay names) are unchanged. - 23 Oct 2026 (1.45.0): The number's relay names are set up in the background right after purchase (
POST /v1/numbers,buy_number, portal) and after an order is fulfilled (POST /v1/numbers/orders,order_number_from_region), whatever the number's routing. A lease (POST /v1/relay,create_relay_lease), an agent call or a transfer from a number that does not have them yet triggers the setup again. Thecreate_relay_leasedescription spells out a rule that already applied: a lease connects exactly one call, dialing the samesipNameagain is rejected with SIP 603, so create a new lease before every attempt and turn off automatic redialing in your platform. Response shapes are unchanged. - 22 Oct 2026 (1.43.0): New agent field
recordingNotice(boolean) inPOST/PATCH /v1/agents, MCPcreate_agent/update_agentand agent drafts (PUT /v1/agents/{id}/draft,save_agent_draft). The response always carries it (falsewhen unset). The sentence is appended only withrecordingNotice: trueandrecordCallson; in a draft an absent key means unchanged. The greeting length estimate (first_message_too_long) counts the sentence only when it is on. This changes whatrecordCallsimplies: recording alone no longer adds the sentence. It was removed on 22 Oct 2026 from existing agents that had it added automatically; a sentence written intofirstMessageitself stays as it is. - 22 Oct 2026 (1.42.3): An attempt's reservation (
reservedBudgetHal, the item'sestimatedCostHal) is the call leg plus the agent leg, each rounded up to a heller exactly as billing does (previously a single rounding of the combined rate, which came out one heller lower for some caps). A call with a duration cap (outbound task) is billed formin(durationSecs, maxDurationSecs); the storeddurationSecsstays as measured. A transferred leg of such a call gets at most the remainder up to the attempt's highest price. An agent withrecordCalls: falseon volai's engine (provider: "engine") has no recording:hasRecordingisfalseandGET /v1/calls/{id}/recordingreturnsno_recording. Saving such an agent requires an engine that knows the flag; otherwise the engine write fails instead of recording silently. - 21 Oct 2026 (1.42.2):
checkCallRateLimit(POST /v1/calls, MCPmake_call, outbound tasks) counts the attempts per 24 hours of a call covered by an agent's stored quota (call-quota:agent:{agentId}) or by the PetCenter campaign exception with a daily limit above 1,000 in its own counterratelimit:callsday-agent:{userId}:{agentId}and compares them with the quota's daily limit. Calls with the default limits, calls without an agent (the website test call, bridge, relay) and calls with a quota that does not raise the daily limit (1,000 or less) still share the account counterratelimit:callsday:{userId}; a call of the same agent outside the quota's scope (outboundTaskOnly,taskPurposes, aftervalidUntil) follows the campaign exception when it applies, and otherwise uses the default limit of 1,000 and the account counter. The concurrency slotsagentcallslot:{userId}(relay_lease_limit) and the per-minute counterratelimit:calls:{userId}stay shared per account. The coderate_limited, HTTP status 429, the message texts and the default limits of 2 concurrent, 10 per minute and 1,000 per 24 hours do not change; only an admin stores a quota.resolveAgentCallLimitsWithSourceinlib/service/call-quotas.tsreturns the source of the limits (stored,campaign,default) next to them from a single quota read, andusesAgentDailyCounterpicks the counter from them. - 18 Oct 2026 (1.42.1): An inbound call that the voice engine turned away because the cap on concurrent calls was used up now has
status: "missed",endReason: "capacity",durationSecs: 0andpriceHal: 0in REST (GET /v1/calls,GET /v1/calls/{id}) and MCP (list_calls,get_call); no credit is charged. Thecall.missedwebhook is sent once per call, and an SMS to the caller only for an agent withmissedCallSmsturned on (as for every missed inbound call); no summary e-mail is sent for it. When the carrier reported the unanswered call first,call.missedis sent at that moment (possibly withoutendReason) and the existing record is then completed withendReason: "capacity"; no second record and no second webhook are created. Until now such a call stayed instatus: "in_progress"forever, because the engine sends no post-call for it. The record is now created by the reconciliation of engine calls, within a few minutes of the rejection. Existing stuck records from the last seven days that are older than two hours are closed retroactively by the hourly check, only with evidence from the engine's call log and without a webhook; if the carrier created its own record for them, they are merged into it. The valuesmissedandcapacityalready existed in the enumerations, and the response shapes do not change. In theoutcomefilter (GET /v1/calls,list_calls) such a call counts asno_answer; for an outbound call nothing changes aboutcapacity(relay pool full). - 18 Oct 2026 (1.42.0):
GET /v1/subscriptionand MCPget_subscription:recommendationhas a new fieldsteady, whether the spend is not just a one-off peak: usage over the last 14 Prague days scaled to 30 days, plus the monthly number fees of the 30-day window counted once, reaches at least the price of the recommended tier. The portal offers the plan personally and the MCP sentence suggests it only whenworthItandsteadyare both true;worthItand the other fields do not change. The recommendation comes from one function for the portal, REST and MCP; for REST and MCP it is cached next to the credit runway (10 minutes, cleared by every credit movement). Nothing else in the API changes. - 18 Oct 2026 (1.41.0):
GET /v1/subscriptionand MCPget_subscription: new fieldpreviousPeriod, a summary of the paid period before the current one (subscription.periodStart) with the same figures as the renewal e-mail:tier,periodStartandperiodEnd(ISO 8601),creditHal,usedHal,usedPercent(rounded down),overHal(spending beyond the monthly credit; with top-ups without debt that the next credit paid off, with the cap including debt;nullwhen the credit history cannot prove it),expiredHal,exhaustionModeandexhaustedAt(ISO 8601,nullwhen the credit did not run out or the history cannot show when). It is saved at every renewal, by card (also when thepayment_intent.succeededwebhook completes it) or by a manual payment, and kept for 45 days; it is returned only while the plan is active, whateveravailablesays, and isnullwithout a plan, with statuspast_dueorcanceled, in the first period and when the usage cannot be proven. MCPget_subscriptionalso adds a sentence about the last period with Prague dates.GET /v1/balanceand MCPget_balance: on an active plan thenotice{kind:"subscription_runway",renewsAt,runsOutAt}now computesrunsOutAtfrom the plan's cautious pace, the same as the outlook on the plan card in the portal: the highest of the 28-day average (with a complete credit history), the average usage over the last 7 Prague days without number fees and the average since the current period started (after its first week), instead of the 28-day average. The shape and conditions of the notice do not change (nothing while auto top-up is on) andrunway.dailyAverageHalandrunway.runwayDaysstay the 28-day average. The renewal e-mail for a renewal that the webhook completes now also leaves out the advice when a tier change is scheduled. Nothing else in the API changes. - 18 Oct 2026 (1.40.1):
POST /v1/outbound/dispatch-scopes/petcenter/registrationsdecoded the result of its Lua script withJSON.parseeven when the default Upstash client (redis()with automatic deserialization; onlyredisRaw()has none) already returns it as an object. Every request therefore ended with the exceptionSyntaxError: "[object Object]" is not valid JSONand a 500 response. The exception happened after the script had run and before the result was mapped, so a newly stored registration (or a replay of the same operation) showed up as a 500 to the client, and a Lua refusal never reached it as409 revision_conflict(operation_conflict,manifest_cas_conflict,task_changed_during_registrationwitherror.details),404 not_foundor400 validation.registerPetcenterDispatchScopenow decodes the result with the helperdecodeLuaJsonObject:JSON.parseonly over a real string, and an object passes unchanged. A script reply that is not an object (valid JSON of another shape, or text that is not JSON) throws and the route answers 500, because the script may already have written the change and the outcome is uncertain; the client then asks for the operation result withGET /v1/outbound/dispatch-scopes/petcenter/registrations?operationId=.... An empty result still ends inregistration_failed. The batch pause decodes its results with the same helper and its behavior and error message do not change. The contract does not change: request and response shapes, idempotency byoperationId(replayed: trueon a repeat),expectedManifestSha, the status codes and theproviderActionStartedflag. The only client-visible difference is that the former 500 responses are now 200, 409, 404 or 400 according to the script result; 500 remains only for an unexpected shape of its reply. - 18 Oct 2026 (1.40.0):
GET /v1/subscriptionand MCPget_subscription: new fieldrecommendation, ornull(no plan offer for the account, or less than 30 Prague days of credit history). It holdsspendHal(spend over the last 30 Prague days on calls, the agent, numbers and SMS, minus SMS refunds),days(30),cheapestPath(per_secondorplan),tierandtierTotalHal(the cheapest tier with top-ups and its cost at that spend),savingsHal,worthIt(a saving of at least 50 CZK and 5%, the same threshold as the portal) andoptions(every way to pay for the same spend:{path, tier, totalHal}for paying by the second and for each tier). For a subscriber MCPget_subscriptionalso adds a sentence with advice under the same rule as the portal and the e-mail. Nothing else in the API changes. - 18 Oct 2026 (1.39.1):
PUT /api/admin/call-quotasloads the agent (getAgent) and refuses the write when the agent does not exist or is deleted (404agent_not_found), or whenuserIdin the body is not the agent's owner (400validation, the message does not reveal the real owner). A refused write stores nothing incall-quota:agent:{agentId}, in the indexcall-quota:indexor inuser:{userId}:admin-log.userIdstays required in the body so the stored shape fromGETcan be sent back. Therelay_lease_limitmessage when placing an agent call (POST /v1/calls, MCPmake_call, outbound tasks) now readsThis agent can have at most 3 running calls at once, and that many are already running on your account. Try again in a moment.(Czech:Tento agent může mít najednou nejvýš 3 běžící hovory a tolik jich teď na tvém účtu už běží. Zkus to za chvíli znovu.; the Czech noun and adjective agree with the count: 1 readsběžící hovor, 2 to 4běžící hovory, 5 or moreběžících hovorů). The number is the threshold of the calling agent fromresolveAgentCallLimits(a stored quota, otherwise the default 2, after the pool clamp); the running calls come from the account-wide counteragentcallslot:{userId}, so a slot may be held by another agent of the same account. Before, the message said the maximum was per account although the agent decides the threshold, and the Czech text used the same plural form for every count. The coderelay_lease_limit, the HTTP status 400, the counter, the threshold and the way outbound tasks wait on this code do not change. The history table heading on the customer detail page in the admin is nowAdmin action history(Historie admin akcí), the rows do not change. - 18 Oct 2026 (1.39.0): New admin endpoint
GET/PUT/DELETE /api/admin/call-quotas(admin session, not an API key; writes check the Origin) stores an agent's quota incall-quota:agent:{agentId}:concurrent1 to 30,perMinute1 to 60,daily1 to 20,000, a mandatorynote,outboundTaskOnlyand the optionaltaskPurposes(standard,coupon_reminder) andvalidUntil(milliseconds since the epoch). Every change is written touser:{userId}:admin-logascall_quota_setorcall_quota_deleted.resolveAgentCallLimitsinlib/service/call-quotas.tsapplies it ahead of the PetCenter exception and the default{ concurrent: 2, daily: 1000, perMinute: 10 }, only for the same account, an outbound task (whenoutboundTaskOnlyis set), an allowed purpose and a time beforevalidUntil. Concurrency is clamped tomin(stored, max(2, number of distinct names in ODORIK_RELAY_NAMES - 2)), the per-minute and daily limits are not. Without a stored quota behaviour is unchanged. Placing a call on a relay name waits for its turn (one shared pointer in Redis, spacingRELAY_PLACEMENT_GAP_MS3 s, at mostRELAY_PLACEMENT_MAX_WAIT_MS10 s; on a Redis outage the call is placed at once andrelay placement spacing unavailableis logged): agent calls (POST /v1/calls, MCP, tasks) and trial calls on the engine and on ElevenLabs, the transfer bridge claim for at most 4 s; not the BYO relay lease (POST /v1/relay) and not callback calls without a relay name.POST /v1/callsand a trial call can therefore take up to 10 s longer during a burst of calls;maxDurationis 100 s forPOST /v1/callsand 70 s forPOST /v1/agents/{id}/test-call. The outbound task tick starts no further task afterTICK_DISPATCH_BUDGET_MS(90 s), the rest waits for the next run. An engine rejection because of the concurrent-call cap carriesRetry-After: 30, which volai already forwards (retryAfterSecs, theRetry-Afterheader,items[].nextAttemptAt): once the engine with this cap is deployed, contacts wait 30 s instead of 5 minutes; an engine without the header still means 300 s. - 18 Oct 2026 (1.38.3): No change in API behaviour. The plan offer is on for every account (
PAUSAL_MODE=on), soGET /v1/subscriptionreturnsavailable: true;pausal_unavailable(404) remains for when the offer is switched off. The documentation (/en/docs/credit, SKILL.md, llms.txt) no longer says the plan is limited to some accounts and describes renewals as hourly. - 18 Oct 2026 (1.38.2):
agentCallLimitsinlib/service/call-quotas.tskeeps returning{ concurrent: 6, daily: 6000, perMinute: 15 }afterCAMPAIGN_QUOTA_EXPIRES_AT(2026-10-31T23:00:00Z) when the quota context carries the new optionaltaskPurpose: "coupon_reminder"andpetcenterReminderQuotaActive()holds, that is, theVOLAI_PETCENTER_CALL_QUOTA_ENABLEDswitch is on and the time is beforePETCENTER_REMINDER_DISPATCH_END(2026-11-05T19:55:00+01:00, 18:55 UTC; the constant moved intocall-quotas.tsand the dispatch tick reads it from there).makeAgentCallpasses the purpose to the quota only after it has verified the link to an approved reminder. A call without a purpose gets the default{ concurrent: 2, daily: 1000, perMinute: 10 }after 31 October. The other conditions (the exact account, agent and internal outbound-task context match and at least eight distinctODORIK_RELAY_NAMES) apply to reminders as well; the main campaign still dials nothing after 31 October,petcenterCampaignQuotaActiveand the tick logic are unchanged. API behaviour for other accounts is unchanged. - 18 Oct 2026 (1.38.1):
GET /v1/balanceand MCPget_balance: an eighthnoticeshape{kind:"subscription_runway",renewsAt,runsOutAt}(at the recent pace the balance runs out before the renewal; returned to subscribers instead of{kind:"runway"}, nothing while auto top-up is on).POST /v1/subscription/checkoutand MCPcreate_subscription_link:consumerConsentis ignored for an account with a company ID, VAT ID or company name (Stripe metadata get0); a consumer without a company ID still gets aconfirmUrl. Nothing else in the API changes. - 17 Oct 2026 (1.38.0): New REST endpoints:
GET /v1/subscription(response{subscription, tiers, topupBalanceHal, available};subscriptionisnullwithout a plan,tierslists{id, priceHal, creditHal}in hellers excluding VAT,availablesays whether a plan can be started on the account),POST /v1/subscription/checkout(body{tier, exhaustionMode, consumerConsent?}wheretieris"490","990"or"1990"andexhaustionModeistopuporcapwith no default; returns{checkoutUrl}, nothing is charged until a person pays in Checkout; an account without a company ID must sendconsumerConsent),PATCH /v1/subscription(body{tier?, cancelAtPeriodEnd?, exhaustionMode?}),POST /v1/subscription/pay(a Checkout link to pay a failed renewal by hand, only in statuspast_due, otherwiseinvalid_state) andPOST /v1/subscription/card(a Checkout link in setup mode to replace the saved card, nothing is charged); the POST endpoints acceptIdempotency-Key.PATCHapplies a lower tier (from the next period),cancelAtPeriodEnd: trueandexhaustionMode: "cap"at once and returns{subscription}; a higher tier,cancelAtPeriodEnd: false(resuming a plan set to end) andexhaustionMode: "topup"change nothing and return 409subscription_confirmation_requiredwith aconfirmUrl(single use, valid for 24 hours) that a person must open and confirm in the portal. Thesubscriptionobject carriestier,status(active,past_dueorcanceled),periodStartandperiodEndas ISO 8601 strings (unliketsin the ledger, which is epoch milliseconds),monthlyCreditHal,monthlyRemainingHal,exhaustionMode,cancelAtPeriodEnd,nextTierandcard(brand,last4).GET /v1/balanceand the MCP toolget_balancenow also carrysubscription(nullwithout a plan) andtopupBalanceHal(top-up credit alone, negative when the account is in debt);balanceHalis still the total of both. The ledger (GET /v1/credit/ledger, MCPlist_ledger) has two newtypevalues,subscription_grantandsubscription_expire; a ledger entry for a charge covered by the monthly credit also carriesfromSubscriptionHal(the part the monthly credit paid), other entries do not have the field. Five new MCP tools:get_subscription,create_subscription_link,update_subscription,cancel_subscriptionandcreate_subscription_payment_link; replacing the card stays REST and portal only. New error codes: 402subscription_cap_reached(returned instead ofinsufficient_creditby calls, test calls, gate rings, SMS and purchases of numbers, SMS numbers and relay leases when the plan runs withexhaustionMode: "cap", the monthly credit is used up and the remaining credit does not cover the action), 409subscription_exists, 404subscription_missing, 409subscription_canceled, 409subscription_confirmation_required, 400consumer_consent_required, 404pausal_unavailable(the offer is not enabled for the account), 400invalid_tierand 409subscription_cap_active(auto top-up cannot be turned on while the plan runs with the cap); the confirmation page in the portal (not REST) can also end in the stateconfirm_invalid,confirm_expiredorconfirm_used. Once the offer is on for an account, the portal shows a plan card on the Credit page and pages to start or change the plan and to confirm aconfirmUrllink. Accounts without a plan see no change in existing fields, statuses or codes. - 16 Oct 2026 (1.37.5): The engine sends
rationaleandreasonasnullin every item ofanalysis.evaluation_criteria_resultswhen they have no value. The post-call webhook schema rejectednulland dropped the evaluation of all criteria, sogoalinGET /v1/calls,GET /v1/calls/{id}and in the MCP toolslist_callsandget_callwasnullfor calls on the engine, thegoalfilter counted them asnot_evaluatedand the after-call e-mail had no goal line. The schema now treatsnullas a missing field for every optional field wherenullmeans nothing, and a malformed evaluation item drops only itself. Wherenullcarries meaning (for examplerecording_url: null= a call without a recording), it stays. No field or endpoint changes. Calls from 16 September to 16 October 2026 do not have an evaluation yet. - 15 Oct 2026 (1.37.4): Three new agent fields
turnDigitTailFloorMs,turnLongSpeechFloorMs(integer,0= off, otherwise 400 to 2300 ms; the gap 1 to 399 returnsvalidation) andturnLongSpeechSecs(1 to 30, decimals allowed) onPOST /v1/agents,PATCH /v1/agents/{id}(a number sets it,nullremoves the stored value, an omitted key leaves it unchanged),GET /v1/agentsandGET /v1/agents/{id}(always present, a number ornullwhen not set, the engine uses its defaults 0 / 0 / 5), in the draft snapshot (PUT/GET /v1/agents/{id}/draft; an absent key means unchanged,nullremoves it) and in the MCP toolscreate_agent/update_agent/save_agent_draft. They are sent to the engine in theturnblock asdigit_tail_floor_ms,long_speech_floor_msandlong_speech_secson EVERY save of the agent (the engine does not carry the fields over from the previous version; a missing value goes as the engine default) and the read-back compares them (engine contract ch. 3.5af and 3.5ag). Stored for either provider, read only on the engine in smart_turn mode; ElevenLabs ignores them. Engine guidance: step 1 = 800 for Czech agents that take dictated numbers, step 2 = 1200, practical ceiling 1500; dates, postcodes and years get the floor too. The counters of applied floors stay in the engine (its dump andGET /v1/calls), they are not in volai webhooks. In the portal, the "End of reply" card in the Advanced tab. - 15 Oct 2026 (1.37.3): New agent field
sttLanguageHintsStrict: booleanonPOST /v1/agents,PATCH /v1/agents/{id}(trueturns it on,falseornullturns it off, an omitted key leaves it unchanged),GET /v1/agentsandGET /v1/agents/{id}(always a boolean,falsewhen not set), in the draft snapshot (PUT/GET /v1/agents/{id}/draft; an absent key means unchanged,nullturns it off) and in the MCP toolscreate_agent/update_agent/save_agent_draft. It is sent to the engine asstt.language_hints_stricton EVERY save of the agent (the engine does not carry the field over from the previous version) and the read-back compares it. Stored for either provider, read only by the engine (Soniox); best effort, only for engine agents with languagecsand outbound calls, never forsk. The call detailGET /v1/calls/{id}, MCPget_calland thecall.completedwebhook (andcall.failedwithendReason: "engine_error") carry a newengine: { protectedUtterances: [{ timeInCallSecs?, atSecs, message, words, reason }], protectedUtterancesCount }block - utterances of the person who answered that the engine dropped during the greeting window (metadata.engine.protected_utterancesfrom the engine post-call, contract ch. 3.5ae): not in the transcript, the agent did not react to them;reasonisfirst_message_protect,greeting_courtesyorgreeting_identification(open list), at most 10 items,messageup to 200 characters, the count covers them all. The block is present only on engine calls from the 15 Oct 2026 build on ([]and0when nothing was dropped); older calls, theGET /v1/callslist and agents on ElevenLabs do not have it. In the portal a switch on the Advanced tab and a "What the person said during the greeting" card in the call detail. - 15 Oct 2026 (1.37.2): cdr-sync: the second leg of an answered ring (same
from/to, differentactive_call_id, starting shortly after the first leg, first leganswered) is matched to the samekind: "ring"record without the per-attempt fee; seconds are billed only whenlength > 0. Thering.rangSecsdescription in OpenAPI and the docs: the carrier's figure is usually 1 to 3 s aboveringSecs. No field or endpoint changes. - 15 Oct 2026 (1.37.1): New optional agent field
inboundVariables: Record<string, string>onPOST /v1/agents,PATCH /v1/agents/{id},GET /v1/agentsandGET /v1/agents/{id}, in the draft snapshot (PUT /v1/agents/{id}/draft,GET .../draft) and in the MCP toolscreate_agent/update_agent/save_agent_draft: default values for{{variable}}placeholders on INBOUND calls. The initiation webhook merges them intodynamic_variablesonly for names the call does not supply itself (caller_number,called_number,attempt_id,volai_*,trial_*andsystem__*take precedence and validation rejects such names); outbound calls (POST /v1/calls, relay, tasks) do not use them. At most 10 keys matching[A-Za-z0-9_]{1,40}, values are trimmed strings up to 200 characters. OnPATCHan object replaces the stored values entirely,nullor{}removes them and an omitted key leaves them unchanged; the response omits the key when the agent has none. In a draft an absent key means unchanged. Theprompt_variables_unavailable_inboundandfirst_message_emptywarnings skip a variable that has a default. Works for either provider - the engine and ElevenLabs read the same webhook. - 15 Oct 2026 (1.37.0): New
POST /v1/ringswith body{to, from?, ringSecs?}(from= your active volai number, defaults to the first number of the account;ringSecs3 to 30, default 10) replies right after the carrier accepts the order with{id, status: "initiated", kind: "ring", from, to, requestedSecs, priceHal}. The result arrives by webhook (call.no_answerforrangandended_early,call.completedforanswered) and inGET /v1/calls/{id}(also with?waitSecs=) in the newring: {requestedSecs, outcome, rangSecs}block;outcomeisrang,ended_early,answeredorunknown.kindhas the new valuering; the body of every call webhook now carrieskindand, for rings, theringblock. MCP toolring_numbertakes the same fields. Limits: 30 rings per minute and 500 per day per account, 120 per hour per target; the manual do-not-call list applies, the automatic 30-day block is neither applied nor fed.Idempotency-Keyis supported. New error code:ring_needs_number(account without an active number). - 13 Oct 2026 (1.36.2):
PATCH /v1/numbers/{e164}and MCPset_number_routingreturnvalidationwhenrouting.modeissip,sipUripoints at<credentialId>.sip.vapi.aior<credentialId>.sip.eu.vapi.ai, andsipUsernamewithsipPasswordare sent or stored from before. A Vapi BYO trunk recognises our calls by the IP in its gateways (IP81.31.45.0, netmask24) and does not connect authenticated routing. Vapi's native SIP numbers (sip:<name>@sip.vapi.ai) are not affected. A number that already has such routing stored is fixed by sending both fields as empty strings. Theset_number_routingdescription and the /en/integrations/vapi, /en/docs/bring-your-own-agent and /en/docs/sip pages say so; for a failed call to a Vapi BYO trunk, the portal points at the login fields, the gateways and the region. - 13 Oct 2026 (1.36.1): An outbound agent call through the relay whose route never reached the carrier (the routing webhook did not answer within five seconds) still has
status: "no_answer", but nowendReason: "rejected"instead of"no_answer", and it does not count towards the automatic destination block (3 failures in 24 hours). In tasks such an attempt hascountsAsAttempt: falseanderrorCode: "relay_not_routed", uses up neithermaxAttemptsnor thelorela_enterpriseschedule, and the contact getsnextAttemptAtno sooner than 60 seconds later. After the third such attempt in a row the contact goes toreviewwithreviewReason: "call:relay_not_routed". - 12 Oct 2026 (1.36.0): The new internal
POST /v1/outbound/dispatch-scopes/petcenter/registrationsaccepts a durableoperationId, manifest SHA-256 and ordered task IDs. The first manifest is pinned to the approved 19-task list; later manifests can only append. GET returns registration state and operation readback. The new/api/cron/outbound-tasks/petcentercron uses a separately and fairly rotated queue; the general cron and atomic claim both enforce separation. Limits of 6 concurrent calls, 15 attempts per minute and 6,000 per day remain unchanged.GET /v1/tasks?ids=id1,id2reads one to five selected tasks owned by the authenticated account, reports subset completeness and rejects responses over 4 MiB.POST /v1/outbound/dispatch-scopes/petcenter/pausessafely pauses up to five registered tasks with revision checks; GET returns durable outcomes and an input hash for safe reconciliation. - 11 Oct 2026 (1.35.0):
POST/PATCH /v1/agentsand the MCPcreate_agent/update_agent/save_agent_drafttools take two new fields:firstMessageProtectSecs: number | null(0 to 20 seconds, decimals allowed, outbound calls only; while the first line plays, a short reaction from the person who answered does not interrupt it: a reply without an interrupting word (for example stop, no, wait) is dropped in the protected window, so it does not interrupt the line, is not in the transcript and the agent does not react to it, while an interrupting word always interrupts it; the interrupting words follow the agent's language, so an English agent is interrupted by stop, no, wait or hold;nullor0turns the protection off) andsilencePromptText: string | null(your own silence reminder sentence, up to 80 characters, spoken word for word instead of the default reminder for the agent's language ("Are you still there?" in English); an empty string ornullrestores the default text). UnlikesilencePromptSecs, WITHOUT a forced default: a missing field onPOSTis not stored,GET /v1/agentsreturns it asnullfor an older agent, and aPATCHwithout the key leaves the stored value alone, so nothing changes for existing agents. Both fields are accepted and stored for EITHER provider, but only heard on an agent running on volai's own engine. A value outside the bounds fails with the generic validation error (validation, 400), no new code. A transcript turn (transcript[]inGET /v1/calls/{id}, MCPget_calland thecall.completedwebhook) carries an optionalinterrupted: trueon an agent line the caller interrupted, withmessagestill the full planned text; it is absent on calls handled by ElevenLabs and on calls without an interruption. - 10 Oct 2026 (1.34.5):
GETandPOST /v1/sms-numbersand the MCP toolslist_sms_numbersandbuy_sms_numberreturndailySendLimitfor each SMS number (20 SMS per 24-hour window during the number's first 30 days, 50 after that),dailySendRemaining(0 for a number being released) anddailySendResetsAt(Unix ms when the window ends,nullwhen none is running). The window starts with the first SMS from any SMS number of the account, is shared by all of them and also counts attempts over the limit and SMS the carrier rejected. Themessage.sentwebhook body has a newsmsNumberfield: the E.164 SMS number for a message sent from an SMS number,nullfor the shared sender SMSinfo. Theinternal_errorandsend_failed_operatormessages for SMS now say "The error is on our side and we know about it",sms_number_out_of_stockno longer promises a restock and therate_limitedmessage for a used-up SMS number limit says how long until the window ends; error codes do not change. In the portal the /en/messages form shows the limit of the selected SMS number and the SMS messages card on the overview also counts received SMS. - 10 Oct 2026 (1.34.4):
agentCallLimitsinlib/service/call-quotas.tsreturns{ concurrent: 6, daily: 6000, perMinute: 15 }for the campaign scope; the condition of at least eight distinctODORIK_RELAY_NAMESstays and guarantees two free names for other callers. The account default (2, 1000, 10), the enablement conditions and the shared account counters are unchanged; API behaviour for other accounts is unchanged. - 10 Oct 2026 (1.34.3): The action for
send_unknownno longer says thatunknownis always final: for an SMS sent from an SMS number it can still change on its own tosentwith adeliveryStatus, or tofailedwith the credit refunded; for an SMS from the shared sender SMSinfo it stays final. The action forrate_limitedno longer promises aRetry-Afterheader in every case: the daily cap for sending from an SMS number (20 SMS a day per account during the first 30 days after buying the number, 50 after that) resets the next day, and a number purchase already running finishes within seconds.POST /v1/messageslistsblocked_destination(400, premium-rate numbers) among its errors. Messages received on an SMS number havesource: "inbound", the received-message list holds at most the 1,000 newest messages per account, and themessage.receivedwebhook is not sent above 300 received messages per hour on one number (the messages are still stored). Inmessage.received,fromis an E.164 number or a text sender name (at most 32 characters); a message that was not picked up at once is added by a periodic check within about 15 minutes and its event then arrives late. Neither the delivery report (deliveryStatus) nor a latefailedtriggers an event; read them fromGET /v1/messages/{id}. Releasing an SMS number is not available over MCP, only in the portal and withDELETE /v1/sms-numbers/{e164}. - 9 Oct 2026 (1.34.2):
agentCallLimitsinlib/service/call-quotas.tsreturns{ concurrent: 8, daily: 6000, perMinute: 15 }for the campaign scope; the account default (2, 1000, 10), the enablement conditions and the shared account counters are unchanged. Therate_limitedmessage for the daily limit now says 6000. - 9 Oct 2026 (1.34.1):
CAMPAIGN_QUOTA_EXPIRES_ATinlib/service/call-quotas.tsis2026-10-31T23:00:00Z; theVOLAI_PETCENTER_CALL_QUOTA_ENABLEDswitch, the exact account, agent and internal outbound-task context match and at least eight distinctODORIK_RELAY_NAMESstill apply. API behaviour for other accounts is unchanged. - 9 Oct 2026 (1.34.0): New endpoints
GET /v1/sms-numbers,POST /v1/sms-numbers(no body,Idempotency-Keysupported, returns 201) andDELETE /v1/sms-numbers/{e164}(irreversible, the paid period is not refunded), and MCP toolslist_sms_numbersandbuy_sms_number(confirmedMonthlyFeeHalmust equal the current fee in hellers, otherwise nothing is bought).GET /v1/messagesand MCPlist_messagestakedirection(outby default,in,all) and also return messages received on an SMS number; messages now carrydirection,smsNumberanddeliveryStatus(deliveredorundelivered, only for SMS sent from a number),statushas a new valuereceivedandfailReasona new valueopted_out.POST /v1/messagesand MCPsend_smstakefromNumber(an active SMS number of the account; Czech +420 recipients only; 1.90 CZK per segment). New webhook eventmessage.receivedwith{id, from, to, body, segments, receivedAt}(receivedAtis Unix ms); the text of a received SMS is untrusted third-party content and MCP marks it withsecurityNotice. Received SMS are kept for 90 days. New error codes:sms_numbers_unavailable,sms_number_limit,sms_number_out_of_stock,invalid_from_number,from_number_unsupported_destinationandrecipient_opted_out; the existing codesrecipient_rejected,sms_gateway_failed,recipient_is_virtual_number(an SMS from the shared sender to a volai SMS number) andrelease_failedalso apply to SMS numbers. The messagefromfield is still the internal labelvolaifor a message sent withoutfromNumber, the SMS number itself for one sent from it, and the sender for a received message. - 8 Oct 2026 (1.33.0): The
callback-routingpolicy accepts an optionalmissedFirstMessage(up to 500 characters): it replacesfirstMessagewhen the verified preceding outbound call had no conversation with the customer (not answered, voicemail, voice menu, nobody spoke, or the call is not closed yet). Initiation sends the enginevolai_callback_kind(missed/contacted) next tovolai_call_mode=campaign_callback. The engine post-call may carrycaller_optout_requested(outbound calls only): the call then exposesoptOutRequested: truein the list and detail, the called number is added to the account's do-not-call list (GET /v1/dnc) and thelorela_enterprisepolicy schedules no further attempt for that item. An engine call withanswered_by: unknownstaysunknown- the portal no longer guesses a voicemail from a short reply ("Yes." after the intro), sohasConversationof such a call follows the transcript; a voicemail detected by the engine (voicemailReasonpresent) gets another attempt under thelorela_enterprisepolicy despite the carrier greeting in the transcript. New reasonvoicemailReason: digits(a carrier greeting reading the called number digit by digit). Everything is additive; existing policies, calls and tasks are unchanged. - 8 Oct 2026 (1.32.1): The message
fromfield stays the internal labelvolai, and OpenAPI now describes it; thesourcefield description says the valueinboundnever occurs for messages. The MCP server instructions, SKILL.md and the llms files say the same. API behaviour is unchanged. - 8 Oct 2026 (1.32.0): Save an explicit allowlist of owned active engine agents for returning callers with a unique real outbound call from this number within
lookbackSecs(60-604800 seconds). Unknown, ambiguous or unverified callers keep the default agent. EmptyagentIdsdisables the policy. OptionalfirstMessageapplies only to verified callbacks.engineSyncRequired: truemeans the default agent engine configuration must separately activatewebhooks.inbound_route_urlwith readback; this request does not rewrite live engine configuration. Verified incoming calls exposecallbackOf.callIdand optional server-ownedtaskIdandtaskItemIdin call details. New REST GET/PUT/v1/numbers/{e164}/callback-routingand MCPget_callback_routing/set_callback_routing. Signed incoming tool context carries direction=in and empty dispatch/task/item fields. Server-ownedvolai_call_mode=campaign_callbackis set only for verified callbacks. Verified inbound human contact stops later scheduled retries of the same task item without creating an outbound attempt or charge. An already running call and its billing finish normally. The server-owned dispatch/task/item binding is retained for eight days to cover the callback window. - 8 Oct 2026 (1.31.0):
POST /v1/tasks,PATCH /v1/tasks/{id},create_taskandupdate_taskaccept optionalretryPolicy: "lorela_enterprise"andexpiresAt(positive safe-integer Unix milliseconds); both fields are returned in the public task. Without a policy,maxAttemptsremains 1-3 with a creation default of 1; with the policy it is 1-4 with a creation default of 4. PATCH without a limit preserves it. The policy can be enabled only before the first dispatch attempt. The second attempt is no earlier than eight minutes after the first real attempt starts, within 08:00-22:00 Europe/Prague; the third uses the next other 08:00-09:00 or 17:00-19:00 window; the fourth uses the opposite window on the following calendar day. A narrowercallingWindow, including weekdays, still applies. Onlyno_answer,missed,busyor completed voicemail without human contact can retry; uncertain results and engine failures need review. The policy rejects manualresolvewithaction: "retry"and stops calling five days after the first real attempt.expiresAtalso works without a policy and prevents any new dial at or after the deadline, including the first attempt; a call already in progress finishes normally. DNC and automatic blocking after three failures within 24 hours are not bypassed, so the fourth attempt is not guaranteed. - 5 Oct 2026 (1.30.2): A call for which the engine does not send
caller_loopin the post-call (the caller first talked to the agent and only afterwards just a repeated message was left on the line) hasendReason: "completed"and the regular price, like any other completed call. The fallback sync of a lost post-call (the engine'sGET /v1/calls) now readsmetrics.caller_loop_reported: the value1means the engine sentcaller_loopin the post-call, andcaller_loopis taken over as before; the value0means an ordinary call, which is closed and billed withendReason: "completed"regardless ofmetrics.caller_loop_released. For an older engine without this field, the existing rule withmetrics.caller_loop_releasedapplies (a positive value means an ordinary call). REST and MCP response shapes, theendReasonvalues and theoutcomefilter do not change. - 4 Oct 2026 (1.30.1):
endReason: "caller_loop"andpriceHal: 0are now given only to an inbound call with a positivedurationSecs; an outbound call, or a call with zero duration, for which the engine sendstermination_reason: "caller_loop"takes the regular path (endReason: "completed"and regular billing; a zero-duration call stays unclosed as before). The fallback sync of a lost post-call (the engine'sGET /v1/calls) takes overcaller_looponly whenmetrics.caller_loop_releasedis missing or0; with a positive value (a person spoke up during the wait) the call is closed and billed like any other completed call withendReason: "completed", andcall.completedcarries the price, the same as when the post-call arrives on its own. A call withendReason: "caller_loop"is never listed in the portal among calls needing attention (Overview and the Calls filter), and the weekly e-mail summary does not count it as a handled call or a caller and does not list it among calls that need a reaction. REST and MCP response shapes, theendReasonvalues and theoutcomefilter do not change. - 4 Oct 2026 (1.30.0): New
endReason: "caller_loop"value (RESTGET /v1/callsandGET /v1/calls/{id}, MCPlist_callsandget_call, webhookcall.completed): an inbound call whose caller was an automated system repeating the same message (a queue, a dialer), ended by the engine's guard. The source ismetadata.engine.termination_reason: "caller_loop"in the engine's post-call (for a lost post-call, the same field from the engine'sGET /v1/calls). The call hasstatus: "completed", a positivedurationSecsandpriceHal: 0, and no credit is charged; the price of 0 applies up to five minutes of call duration - a longer call is billed at the regular rates and keepsendReason: "caller_loop". Thecall.completedwebhook carriesendReason: "caller_loop". A call where a person spoke up during the wait and the engine carried on with them does not getcaller_loop. In theoutcomefilter (GET /v1/calls,list_calls) such a call counts asagent. - 4 Oct 2026 (1.29.6):
PATCH /v1/agents/{id},update_agentand draft publish: changinglanguagefrom a language in which new agents start on ElevenLabs (todayen,de,pl) to one in which they start on the engine (cs,sk) triggers, for an agent withprovider: "elevenlabs", the same post-save engine switch attempt as turning ontransferTo. On success the agent hasprovider: "engine"and the engine's default voice for the new language (milena,katarina); on failure it stays on ElevenLabs. A change betweencsandskmoves nothing.voicemail_requires_enginehas new wording: on creation it points touseForOutboundTasks: true, on an existing agent to support. - 4 Oct 2026 (1.29.5):
POST /v1/agentsandcreate_agentreturn a new codeelevenlabs_by_explicit_choice({code, message: {cs, en}}) inwarningswhen the request sentuseForOutboundTasks: false, the deployment default would have picked the engine for the agent's language, and the agent really stayed on ElevenLabs (atransferTohandoff may have switched it to the engine). The agent is created as before and the code blocks nothing.PATCH /v1/agents/{id}, draft publish and rollback never return it. - 3 Oct 2026 (1.29.4): Documentation only, the API behavior does not change.
maxAttempts(1-3) is the highest number of automatic dials of one contact, the first call included;1means no retries. A contact that you giveresolvewithaction: "retry"after its attempts are used up gets one more dial. Every attempt where the call could have happened counts (unanswered, declined by the person called, or with an uncertain result). An attempt rejected before dialing does not count and hasitems[].attempts[].countsAsAttempt: false. - 3 Oct 2026 (1.29.3): For attempts recorded from this version on,
items[].attempts[].countsAsAttemptisfalsefor every code where the call provably never happened, so they do not count towardmaxAttempts: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_configuredanddestination_busy. Previously the permanent problems with the number (invalid_number,unsupported_country,blocked_destination,on_dnc,destination_auto_blocked,cannot_call_own_number,cannot_call_volai_number) were counted, so after lifting a block and another rejection a contact showed more attempts than allowed although nothing was dialed. Attempts written before this version are not rewritten retroactively (the exception is an attempt whose dial intent the accounting repair closes). Uncertain dial errors (for exampleengine_unavailable) still count, because the call may have happened. A permanent problem still sends the contact toreview(you decide), a repairable code (missing credit, an agent without a number and the like) pauses the task;rate_limitedandrelay_lease_limitleave the task running and only wait. Nothing is dialed in a loop because of this. A contact you return viaresolvewithretryafter its attempts are used up now stayspendingafter a repairable dial error without a call (for exampleinsufficient_credit) and is dialed again once the cause is fixed; before, it stayedfailedand the task could not be started (task_not_startable). - 3 Oct 2026 (1.29.2):
destination_busywhile dialing from a task (the call provably never happened): the contact keeps waiting (failedwhen it already had a counted attempt and has another free one, otherwisepending) withitems[].lastError: "destination_busy"anditems[].nextAttemptAtfrom the remaining time of the number lock (at least 30 s, at most 16 min, 90 s without a lock), the attempt hascountsAsAttempt: false, the reservation inreservedBudgetHalis released and the other contacts keep running; the task'slastErroris not set, only a stale waitingrate_limitedorrelay_lease_limitis cleared, because a busy result proves the limit passed. After the 6th busy result in a row (5 waits) the contact goes toreviewwithreviewReason: "dial:destination_busy". The seven permanent codes (on_dnc,destination_auto_blocked,cannot_call_own_number,cannot_call_volai_number,invalid_number,unsupported_country,blocked_destination) send the contact toreviewwithreviewReason: "dial:<code>", but the task staysrunningand moves toneeds_attentiononly when no other work is left (previously immediately). Over the API and MCP a block is lifted with two calls:POST /v1/dnc/{e164}/unblock(unblock_destination), thenresolvewithaction: "retry"(resolve_task_item); for your own number, a number of another volai account, an invalid number, a number outside Czechia and Slovakia and a premium-rate line onlyskiphelps. A contact inreviewcan now appear while the task is still running;resolvethen returns 409task_not_editable, so wait forneeds_attentionor pause the task.POST /v1/callsdoes not change. - 2 Oct 2026 (1.29.1):
items[].providerCallIdpoints only to the call of the latest attempt: a contact infailedthat is waiting for another attempt does not carry it; the call of every attempt stays initems[].attempts[].providerCallId. Previously the item kept the call of the first attempt, the next dialed call was not recorded on the task, and the contact was dialed again without counting the attempt and without adding the price tospentHal. The errorson_dnc,destination_auto_blocked,cannot_call_own_numberandcannot_call_volai_numberwhile dialing from a task are provably no call: the attempt getsoutcome: "failed", the reservation inreservedBudgetHalis released right away and the contact goes toreviewwithreviewReason: "dial:<code>"(previouslyoutcome: "unknown"and a held reservation).POST /v1/tasks/{id}/startno longer returns 409task_needs_attentionbecause of a reservation that no contact holds;POST /v1/tasks/{id}/reconcilesettles such a reservation and also closes an older open dispatch intent left after an attempt that was never dialed. - 2 Oct 2026 (1.29.0): New endpoint
POST /v1/numbers/orders/{id}/cancel(no body) and MCP toolcancel_number_order: apendingorder moves to the new statuscancelled(a newstatusvalue of the order inGET /v1/numbers/ordersandlist_number_orders);provisioningreturns 409in_progress,doneandfailed409invalid_transition, a foreign or unknown id 404not_found; cancelling again returns the order unchanged. A cancelled order no longer counts toward the first top-up gate (one number per account without a top-up).callingWindow.endof tasks is inclusive of the whole minute (current <= end): a00:00-23:59window covers the whole day andwindow_closedno longer occurs in theendminute itself. - 30 Sep 2026 (1.28.1):
PATCH /v1/numbers/{e164}(MCPset_number_routing) withmode: "forward"returns 402insufficient_creditwhen the account has never topped up and the number is not forwarded yet. A number that is already inforwardcan be pointed at a different target without a top-up.POST /v1/numbers(MCPbuy_number) andPOST /v1/numbers/orders(MCPorder_number_from_region) return 402insufficient_creditwhen asked for an additional number while an account without a top-up already holds an active number or has an open order (pending,provisioning). Same code and cause (account) as for SMS, different message text - it says what to do (make the first credit top-up); nothing is charged. After the first credit top-up (card, auto top-up) the restriction is gone. - 25 Sep 2026 (1.28.0): New
suspended: booleanfield on the agent object (REST and MCP,GET/POST/PATCH /v1/agents). New error codesagent_suspendedandaccount_suspended(403,cause: "account") -account_suspendedon every REST call with an API key and every MCP tool (after the rate limit) except reading tax documents (GET /v1/billing/documents,/{id}and/{id}/pdf, the MCP toolslist_billing_documentsandget_billing_document), which stay available;agent_suspendedonPOST /v1/calls,POST /v1/agents/{id}/test-callandPOST /v1/tasks/{id}/start. An inbound call blocked by a suspension of the agent or the account carriesendReason: "suspended"(a new value next tocredit_blocked, price0). Anaccount_suspended/agent_suspendedresponse is never stored under anIdempotency-Key, so the same key succeeds once restored. Custom variable keys (variablesonPOST /v1/callsandmake_call) can no longer start with thevolai_prefix - it is reserved for variables volai fills in itself (such asvolai_block_reason), and such a request returns 400validation; in tasks, such a key is only refused when the contact is dialed. Suspending and resuming is done only by the operator in the admin panel - there is no REST or MCP tool for it. The sentence a caller hears on a blocked line (debt or suspension) is now spoken in the agent's language (cs, sk, en, de, pl). - 25 Sep 2026 (1.27.3): No interface change: the same
warningscodeagent_misuse_suspectedin the same REST and MCP responses, only broader recognition. - 24 Sep 2026 (1.27.2): Outbound tasks:
relay_lease_limitfrom dialing (the quota of 2 concurrent agent calls per account) now behaves likerate_limited- the task staysrunning, setslastError: "relay_lease_limit", the attempt does not count (countsAsAttempt: false) and waiting contacts getitems[].nextAttemptAtone minute later. Previously the item went toreviewwithreviewReason: "dial:relay_lease_limit"and the task toneeds_attention. The engine sync (GET /v1/calls) reads the newattempt_idfield of an outbound call and uses it to match aninitiatedrecord after an uncertain dial (nocall_sid, also for an agent without its own number); once matched,call.completedis sent with the price and the relay line and quota are released. Requires an engine that listsattempt_id; an older listing behaves as before. - 24 Sep 2026 (1.27.1):
POST /v1/callswithagentIdon an agent on volai's own engine, a trial call withsystemPrompt, MCPmake_calland outbound tasks: when dialing on the engine ends with a timeout, a dropped connection, a 500/504 response or an unreadable 2xx, the response is still502 engine_unavailable, but the call staysinitiated(nopriceHal), the relay line is not released and the sametostays locked for 15 minutes (destination_busy); the concurrent call quota is released right away. If the call happened, the post-call or the engine sync fills in the outcome and price (the post-call now finds it by the attempt identifier even without acall_sid, including agents without a number of their own) andcall.completedis sent; if not, the watchdog closes it after 2 h asfailedwithpriceHal: 0. Thecall.failedwebhook is not sent for an uncertain dial. A certain failure (no connection to the engine at all or a timed-out connection attempt, 4xx, 502/503) behaves as before:failed,priceHal: 0,call.failed. - 24 Sep 2026 (1.27.0): New
warningscodeagent_misuse_suspected({code, message: {cs, en}}). Appears inPOST/PATCH /v1/agents,PUT /v1/agents/{id}/draft,POST /v1/agents/{id}/simulate,POST .../draft/publish,POST .../draft/rollback,POST /v1/calls(the trial call prompt orvariables),POST/PATCH /v1/tasks,POST/PATCH /v1/toolsand the matching MCP tools (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). Thewarningsfield is optional, absent when there is nothing to flag, and /openapi.json describes it on all of these responses. - 24 Sep 2026 (1.26.3):
POST /v1/callswithfrom(bridge) and MCPmake_call: when the connection order to the phone operator ends with a network error, a timeout or a 429/5xx response, the call is returned as a success withstatus: "initiated"instead of502 callback_failedand staysinitiateduntil cdr-sync pairs it with the CDR (and bills it), or the watchdog closes it after 24 h asfailedwithpriceHal: 0.callback_failednow means only a provable rejection by the operator (an error response or 4xx). Thecall.failedwebhook is not sent for an uncertain order; the sametonumber stays locked for about 10 minutes (destination_busy). - 24 Sep 2026 (1.26.2): The per-account cap on calls within 24 hours (shared by
POST /v1/callswith an agent or a bridge, relay and trial calls, MCPmake_calland outbound tasks) is now 1000 instead of 200. Exceeding it still returns429 rate_limitedwith the daily-limit message;10/min, concurrency and the engine budget are unchanged. - 24 Sep 2026 (1.26.1): Outbound tasks: a call record in
failedwith noconversationIdand nopriceHal(the engine refused or did not confirm the dial) now settles at 0 inreconcile- previously the item keptreviewReason: "billing_unknown"and the task stayedneeds_attentionforever.resolvewithskipon abilling_unknownitem moves a task with remaining work topaused(the original call's reservation keeps counting inreservedBudgetHal, andstartaccepts it);retrystill waits for the price. A decision right after a dial error (reviewReason: "dial:...") no longer loses the link to the call or its reservation.rate_limitedwhile dialing (engine limit, 10 calls per minute, 200 per day) keeps the taskrunningwithlastError: "rate_limited"and sets waiting items'nextAttemptAtto the end of the limit (fromRetry-After, otherwise in 5 minutes, at most 24 hours) instead ofpaused; the attempt does not count.invalid_workflowandengine_not_configuredwhile dialing are provably no call (the task becomespaused, the attempt does not count). Every call by an agent on volai's own engine that the engine never dialed (outbound tasks as well asPOST /v1/calls, MCPmake_calland the portal) haspriceHal: 0anddurationSecs: 0inGET /v1/callsinstead of missing values; thecall.failedwebhook is unchanged. - 24 Sep 2026 (1.26.0):
POST /v1/callswithagentIdon an agent on volai's own engine (and MCPmake_call): a rejection by the engine's outbound-minute limit (hourly and daily bucket per account) or by full engine concurrency is now429 rate_limitedinstead of502 engine_unavailable; the minute limit adds aRetry-Afterheader (seconds until the end of the hourly window, or until midnight UTC for the daily one). Nothing was dialed or charged; the call stays in the history asfailedand thecall.failedwebhook carriesreason: "rate_limited". Outbound tasks treatrate_limited(including the 10 calls/min and 200/day limits) as provably no call: the item isfailedwithout counting an attempt, and the task ispausedinstead ofneeds_attention. ThePOST /v1/callserror table now also lists502 engine_unavailable- for an agent on volai's own engine it means only a real engine outage, not a limit. - 23 Sep 2026 (1.25.0): New optional
event_idfield in the webhook envelope ({event, data, ts, event_id}) forcall.completed/call.failed/call.no_answer/call.missed-message.sentand thewebhook.testtest event are unchanged. A failed delivery of those four events no longer stops after 3 attempts (3s apart): it stays queued and retries over the following minutes, up to 5 rounds; a repeat delivery carries a byte-identical body.GET /v1/webhook/deliveries(and MCPlist_webhook_deliveries) now also show rounds before the final one,attemptsis the sum of attempts across all rounds, and there is a new optionaleventIdfield. - 23 Sep 2026 (1.24.1): Two new
warningscodes:prompt_variables_unavailable_inbound(an agent withnumberE164whosesystemPrompt/firstMessageuses a{{variable}}outside the set an inbound call actually supplies) andfirst_message_empty(afirstMessagewith no letter or digit left in it outside the variables once every{{...}}block is removed). Both REST (POST/PATCH /v1/agents, publish and rollback) and MCP (create_agent,update_agent,publish_agent_draft,rollback_agent_draft) read the same source as the portal's agent editor (agentWarnings). - 20 Sep 2026 (1.24.0): New optional
busyCalendarIdsfield on the agent (POST/PATCH /v1/agents, MCPcreate_agent/update_agent, drafts) and on agent-less slot search and booking (POST /v1/integrations/{id}/availability,POST /v1/integrations/{id}/events, MCPfind_free_slots,create_calendar_event). The calendars are read in parallel; if any of them fails, the whole lookup fails, so no time is offered over an event we simply could not see. At most four, without duplicates and withoutcalendarId; otherwise 400calendar_invalid_rules, an unknown calendar 400calendar_invalid_calendar. - 20 Sep 2026 (1.23.4): The error tables of
POST /v1/agents,PATCH /v1/agents/{id}and draft publish/rollback add the six codesvalidateCalendarreturns: 404calendar_integration_not_foundand 400calendar_invalid_calendar,calendar_invalid_timezone,calendar_invalid_windows,calendar_no_windows,calendar_invalid_rules. This flows intoopenapi.json, the /docs/api reference,SKILL.mdandllms-full.txt. - 20 Sep 2026 (1.23.3):
maybeQueueMissedCallSmsnow also fires for an outbound call withansweredBy: "voicemail"(previously onlystatus: "no_answer"and inboundmissed). A voice menu (ivr) orunknowndoes not trigger it. All other conditions (CZ/SK number only, DNC, blocked destinations, one SMS per number per 24 h, quiet hours, last task attempt) are unchanged;CallRecord.missedSmsis written the same way. - 19 Sep 2026 (1.23.2): A timestamp with milliseconds was formatted with a time zone offset one minute short (
+01:59instead of+02:00) - this affected thenowfield in the agent tools and the start of a created event when a client sentstartwith fractional seconds. Alternatives returned with 409calendar_slot_taken(POST /v1/integrations/{id}/events, MCPcreate_calendar_event) are now computed from the requested time, the same asfind_free_slotswith a time. - 19 Sep 2026 (1.23.1): The agent
calendarfield (POST/PATCH /v1/agents, MCPcreate_agent/update_agentand drafts) rejects a calendar whoseaccessRoleisreaderorfreeBusyReaderwith 400calendar_invalid_calendar, because the agent writes appointments into the same calendar.list_calendarsreturns the role asaccessRolewhere the provider reports it; a calendar without a role is accepted as before. No other error codes change. - 19 Sep 2026 (1.23.0): New
CallRecord.ratingfield (value: "good" | "bad",reason?,note?,at,source: "portal" | "email" | "api" | "mcp") andCallRecord.missedSms;PATCH /v1/calls/{id}and MCPannotate_callacceptrating/ratingReason/ratingNote(patchCallAnnotationcallsrateCallwithsource: "api"/"mcp"). NewAgentRecord.missedCallSms { enabled, text? }field (variables{firma},{cislo},{agent}) onPOST/PATCH /v1/agents,GET /v1/agents/{id}and MCPcreate_agent/update_agent/get_agent/save_agent_draft.GET/PATCH /v1/accountand MCPget_account/update_accounthavecompanyName(max 80 chars, empty string clears it), and GET now returnsnotifications.weeklySummary. The test call in the agent editor shows the outcome based on the call goal (call.evaluation.goal) and the collected data. - 18 Sep 2026 (1.22.0): New endpoints
POST /v1/integrations/{id}/availability(free slots without a saved agent -windowsomitted means 24 hours every day) andPOST /v1/integrations/{id}/events(create an appointment, requiresconfirm: true, optionalidempotencyKeyfor safe retries; 409calendar_slot_takenon a collision), MCP toolsfind_free_slotsandcreate_calendar_eventwith the same shape.DELETE /v1/integrations/{id}now returnsaffectedAgents- agents whosecalendarfield was turned off by disconnecting this integration. Newcalendarfield onPOST/PATCH /v1/agents,GET /v1/agents/{id}and MCPcreate_agent/update_agent/get_agent:integrationId,calendarId,timezone,slotMinutes,minNoticeMinutes,horizonDays,bufferMinutes,windows(an array of{from,to}windows per weekday). Works only on volai's own engine (provider: "engine") - on an agent hosted on ElevenLabs the field is saved and waits for thecalendar_pending_engine_switchwarning until the agent switches. Eleven new error codes withcause/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 Sep 2026 (1.21.0): New ledger movement type
setup_revision(GET /v1/credit/ledger, MCP) for a done-for-you setup order's revision round beyond the package, amountSETUP_EXTRA_REVISION_HAL(490 CZK excl. VAT). Tax documents (GET /v1/billing/documents, .../{id}, list_billing_documents, get_billing_document) now always carry akindfield ("invoice" - default, or "credit_note"), plus optionalcorrectsNumberandcorrectionReasonfields; a done-for-you setup refund's credit note is numbered in its own OD series and carries positive amounts (the minus sign is added by the PDF).credit()(lib/service/balance.ts) gained an internalnotifyTelegramparameter - a done-for-you payment now sends just one Telegram alert ("Na klíč: zaplaceno"), not two. New public landing page at /receptionist-done-for-you (Czech canonical /recepcni-na-klic). - 18 Sep 2026 (1.20.0): New
laughterfield (auto/on/off) onPOST/PATCH /v1/agentsand MCPcreate_agent/update_agent:autofollows the engine's sensitive-topic detection (debt collection, healthcare, authorities, funeral services),onoverrides that detection,offalways suppresses it; an omitted key is stored and returned asauto, never absent.GET /v1/agents/{id}and the new MCP toolget_agent(previously missing parity for a single agent's detail) addlaughterEffective({active, reason, sensitiveCategory} | null) nested inside the returned agent object, computed fresh for the published configuration;nullmeans it could not be determined right now (the engine did not respond within 3 seconds, was down, or returned a silent 404 on an older build) - never a guess. MCPupdate_agentreturns the same object as a top-level field next toagentinstead, sincePATCH /v1/agents/{id}never returns it. The field is accepted and stored for an agent on ElevenLabs too, but has no audible effect there - only a Czech Cartesia voice on volai's own engine plays it. - 18 Sep 2026 (1.19.0): New REST API v1 and MCP area - source setup-orders, capability area setup (after credit). Seven endpoints (POST /v1/setup-orders, GET /v1/setup-orders, GET/POST /v1/setup-orders/{id}, .../messages, .../checkout, .../launch, .../cancel) and seven matching MCP tools (create_setup_order, list_setup_orders, get_setup_order, message_setup_order, checkout_setup_order, launch_setup_order, cancel_setup_order) over the same service layer, lib/service/setup-orders.ts. The quote is always computed by the catalog in code (lib/setup-catalog.ts); the model only picks items from the conversation. New error codes: setup_order_not_found, order_closed, invalid_transition, quote_pending_confirmation, checkout_open, turn_in_progress, advisor_no_quote, advisor_unavailable, setup_orders_disabled. Documentation at /docs/done-for-you (Czech /docs/na-klic).
- 17 Sep 2026 (1.18.0): The agent draft (
PUT/GET /v1/agents/{id}/draft,POST /v1/agents/{id}/draft/publish) and the MCPsave_agent_drafttool now treatnumberE164as three-state: an absent key leaves the agent's number untouched,nulldisconnects it, and a string attaches that number - parity with the semanticsPATCH /v1/agents/{id}already had. Reading a draft also reconciles it against the live agent:published.numberE164always mirrors the live number, anddraft.numberE164does too whenever the draft made no explicit choice, so the editor's pre-publish check and outbound tasks see the true state even after a number was attached or moved outside the editor; "no explicit choice" also covers a string that matches the agent's current LIVE number - that value behaves the same as an absent key, re-checked on every draft read against whatever number is live at that moment; if the number later moves to a different agent outside this draft, the same stored value becomes an explicit choice again and the next publish or rollback reattaches it here - not silently: publish and rollback responses (REST and both MCP tools) now carry a newnumber_changed_by_publishwarning withfrom/to(E.164, ornullfor no number) in that case, without blocking the operation either. One read-side compatibility note:draft.numberE164can now come back asnull(an explicit "no number" choice) where the key used to be simply absent - treat it the same as an absent key, meaning the agent has no number. The "tested" marker and the agent fingerprint pinned to a running outbound task are now computed WITHOUT the phone number too (behaviorFingerprint, notdraftFingerprint), so attaching or moving a number outside the editor no longer requires a fresh test and no longer stops a running campaign; a draft with a number saved BEFORE this change has its marker computed the old way, including the number, so it asks to be re-earned once - one test or publish after the update restores it, and the new rule applies from then on. - 15 Sep 2026 (1.17.0): A new goal display state,
GoalDisplayState(success/failure/unclear/no_conversation/not_evaluated), replaces theAgentGoalResulttriple in the portal's display, in the/hovoryfilters and in the overview. REST and MCP gainedansweredBy: "ivr",goal.reason("no_caller_speech"/"evaluation_error"), andhasConversation(boolean | null). Thegoalparameter (GET /v1/calls, MCPlist_calls) accepts five new values alongside the existingunknown, which stays backward compatible - it still covers records with no clear result (goal.resultmissing orunknown): alwaysunclearandnot_evaluated, andno_conversationonly when its result is alsounknown. - 13 Sep 2026 (1.16.0): A new
endReason: "engine_error"value (statusfailed) appears when the engine sendsmetadata.engine.termination_reasonasengine_stallorengine_errorfor a call that was actually handled - previously that field was discarded for such calls.priceHalis set to0only when the call has no price yet (otherwise it is left unchanged, with an ALERT log). Thecall.failedcustomer webhook gained four new optional fields:durationSecs,priceHal,transcript,summary- populated only for this technical branch, missing everywhere else as before.GET /v1/calls/{id}and MCPget_callreturn the sameendReasonvalue. Outbound tasks (POST /v1/outbound-tasks, MCP) treatengine_erroras a retryable attempt (the same path asno_answer), not a reason to stop the campaign, and do not record it toward the automatic 30-day destination block. - 13 Sep 2026 (1.15.0):
POST/PATCH /v1/agentsand the MCPcreate_agent/update_agent/save_agent_drafttools take a newsilencePromptSecs: number | nullfield (a whole number 3 to 25,nullturns it off), bounds matching the engine (engine/types.py, U1). A missing field onPOST/create_agentsaves the default of 10 - unlikevoicemail, the field is accepted and returned for EITHER provider (GET /v1/agentsreturns the stored value even forprovider: "elevenlabs"). A value outside the bounds fails with the generic validation error (validation, 400), no new code. Existing agents on the engine get the default of 10 via a one-off re-sync script (scripts/resync-silence-prompt.mts) that changes only this one field. - 13 Sep 2026 (1.14.7): OpenAPI adds descriptions to every operation and request field, including nested variants. Agent, tool, SMS and call constraints use product limits shared with the service layer instead of permissive parser ceilings. Automated checks validate all request and response examples against their schemas. API behavior, error codes and provider settings are unchanged.
- 13 Sep 2026 (1.14.6):
POST /v1/messagesand MCPsend_smsreturninsufficient_credit(HTTP 402, causeaccount) until the account has a positivetopup(card or auto top-up) oradmin(manual owner adjustment) ledger entry. The previous cap of 3 SMS for accounts without an active number of their own is gone, and an active number no longer grants an exception. The first top-up is kept as a permanent account flag, so a long ledger history never blocks a customer. - 13 Sep 2026 (1.14.5): New
workflow_node_tool_missingcode inwarningsonPOST/PATCH /v1/agents, on draft publish and rollback, and on MCPcreate_agent,update_agent,publish_agent_draftandrollback_agent_draft. It applies to both providers, because a deleted tool does not reach the phase on volai's own engine either.workflow_node_tool_not_syncednow only covers a tool that is on the account but not among the agent's own. The Reception template also recommends two sentences for the base prompt: do not guess operational details, and ask about one thing at a time. - 12 Sep 2026 (1.14.4): Setup distinguishes OAuth grant identifiers from API keys when restoring older saved choices. An unavailable status check is not treated as revoked access.
- 12 Sep 2026 (1.14.3): Added the catalog files katty-studio.mp3 and katty-telefon.mp3 using the same generator and content as the other voices. The agent voice, provider and phone service settings are unchanged.
- 12 Sep 2026 (1.14.2): Copy labels and next-step guidance match the selected connection method. Codex desktop with an API key points to config.toml. Closing the mobile menu preserves focus restoration.
- 12 Sep 2026 (1.14.1): Sign-in preserves the assistant setup destination. The workspace preference is stored per account separately from actual MCP verification. Manual guides cover Claude Code, Codex CLI and desktop, with API keys as an alternative. Help and the names Voice agents, Phone numbers and Data & integrations are consistent.
- 12 Sep 2026 (1.14.0): When
useForOutboundTasksis omitted, selection depends onVOLAI_ENGINE_DEFAULT_FOR_NEW, engine availability andVOLAI_ENGINE_DEFAULT_FOR_NEW_LANGUAGES(unset meanscs,sk). Language alone does not guarantee the engine. Explicittruerequests the engine;falsestarts on ElevenLabs. Applies to the portal,POST /v1/agentsand MCPcreate_agent. Read backproviderandvoiceIdafter creation; handoff may trigger a subsequent provider switch attempt. - 12 Sep 2026 (1.13.0): The workflow field is available in POST and PATCH /v1/agents, in the agent draft, and in the create_agent, update_agent and save_agent_draft MCP tools. A call that went through phases carries workflowPath in GET /v1/calls and GET /v1/calls/{id}. New workflow_*, workflow_not_synced_to_provider and workflow_node_tool_not_synced warnings flag phase configuration issues.
- 12 Sep 2026 (1.12.5):
voiceIdfor an ElevenLabs agent:katty(default, the template voice) or a raw ElevenLabs id;janais gone from the catalog and from ElevenLabs (the two agents from the afternoon of 12 Sep were switched to Katty and the voice was removed from the workspace).GET /v1/voices?provider=elevenlabsand MCPlist_voiceswithprovider: elevenlabsreturnkatty. The engine's ElevenLabs fallback for Milena, Tereza and Katarina is Katty as well. - 12 Sep 2026 (1.12.4): Polling no longer interrupts a slow status request. Successful MCP read evidence is found by active access even if writing the helper index failed. Verification still requires get_balance or list_agents through MCP.
- 12 Sep 2026 (1.12.3): Fixed the English calendar privacy link and kept an active goal filter visible even without previous evaluations. Both languages have regression coverage for links and unavailable integrations.
- 12 Sep 2026 (1.12.2): The MCP guide uses existing evidence of successful reads through an active API key or OAuth grant, with no simulated verification. The goal=unknown filter in GET /v1/calls and MCP list_calls also includes records without a goal evaluation. Technical completion does not mean a goal was met. REST and MCP still return individual records; the portal groups them into conversations.
- 12 Sep 2026 (1.12.1):
voiceIdfor an ElevenLabs agent:jana(default, the template voice) or a raw ElevenLabs id;anetpasses only on an agent that already has it, otherwise a 400validationwith an explanation (previously a 502eleven_labs_errorfrom ElevenLabsvoice_live_moderated_not_allowed).GET /v1/voices?provider=elevenlabsand MCPlist_voiceswithprovider: elevenlabsreturnjana. An ElevenLabs agent withoutvoiceIdgets Jana explicitly (the record carries her id). SIP registration: the carrier returned403 INVALID_USERfor lines bought through its API until the line's SIP protocol was toggled off and on once; fixed for all lines and done automatically when a new line is bought. - 12 Sep 2026 (1.12.0): OAuth authorization code + PKCE S256, DCR, resource-bound opaque tokens, refresh rotation/replay detection, OIDC discovery/JWKS/userinfo and revocation. Access tokens last 10 minutes, refresh tokens up to 30 days, grants up to 90 days. Password changes invalidate OAuth access. MCP annotations explicitly distinguish overwrites, paid actions, messages and external side effects.
- 12 Sep 2026 (1.11.0):
GET /v1/numbers/{e164}/sipand MCPget_sip_credentials:outboundTrunkAddress=sip.volai.cz(volai's own SIP proxy with digest auth, realmsip.volai.cz; measured: the platform refuses an INVITE larger than the UDP MTU, the same call passes over TCP),transport=tcp(schema is nowenum [tcp, udp], UDP works too),port5060 unchanged. The watchdog monitors the proxy healthz (SIP_PROXY_HEALTHZ_URL): ops e-mail when unreachable, when the auth data sync stalls or when the certificate has under 14 days left. The ElevenLabs post-call webhook has its HMAC secret in production and is enabled per agent (ELEVENLABS_POST_CALL_WEBHOOK_ID). - 12 Sep 2026 (1.10.0):
GET /v1/numbers/{e164}/sipand MCPget_sip_credentials:outboundTrunkAddressis driven by its own configuration (it may equalserver), field descriptions no longer mention a realm;inboundSignallingCidrsunchanged. The ElevenLabs post-call webhook matches inbound calls viametadata.phone_call.call_sid(it used to read the SIP Call-IDcall_id, which never matched thecall:sidkey written by the initiation webhook) throughfindExistingCallByKeysshared with theconversations-synccron (ordercall:conv->call:attempt->call:sid). Per-agent post-call webhook settings (platform_settings.workspace_overrides.webhooks) are migrated by the cron onceELEVENLABS_POST_CALL_WEBHOOK_IDis set; until then the cron keeps closing calls. The engine post-call acceptsphone_call.external_number: nullandcaller_presentation; theuri_caller prefix is stripped as in the initiation webhook. The call transfer target (transfer_to_number) is built from a separateVOLAI_SIP_TRANSFER_HOST, not from the public SIP domain. New internal source of auth data for the SIP proxy (GET /api/internal/sip-subscribers, outside v1, bearer + IP allowlist + rate limit). - 12 Sep 2026 (1.9.0):
POST/PATCH /v1/agentsand MCPcreate_agent/update_agentcan now return a secondwarningscode:first_message_too_long, independent offirst_message_no_ai_disclosure- and publishing an agent draft (POST /v1/agents/{id}/draft/publish, MCPpublish_agent_draft) and rolling it back to an earlier revision (POST /v1/agents/{id}/draft/rollback, MCProllback_agent_draft) now return the samewarningstoo, which they previously omitted entirely - both of them write the first line to the live agent. The estimate counts words, digits per character, and all-caps abbreviations up to four characters; a longer all-caps word counts as one word, so a company name written in capitals no longer inflates the estimate. The single threshold (an estimated duration over 7 seconds) is measured against what the caller actually hears - the greeting plus the call-recording notice, when enabled. An unchanged default greeting template never gets the warning, only a line the customer has edited. - 12 Sep 2026 (1.8.2): Documentation, integration guides and error actions point to AI assistant for access management. MCP makes clear that the voice engine is required to start an outbound task; a task can also be created with an ElevenLabs agent.
- 12 Sep 2026 (1.8.1): OpenAPI now describes Idempotency-Key for 14 supported operations, response headers and the optional revision JSON body when deleting a task. Documentation corrections without changing paid-action behavior:
useForOutboundTasksdistinguishes true/false/omission, tasks support 1000 recipients andbilling_unknownretains the original call and reservation until its price is known. Credit tax documents are available over REST and MCP; an API key can revoke only itself over REST; integration disconnect is REST/portal. Correction to 1.6.1:preview_task_csvis not an MCP tool; usePOST /v1/tasks/csv-previewor the portal. Install commands share the onboarding generator, example tabs have unique anchors and Python preserves JSON values. - 11 Sep 2026 (1.8.0): The new REST
POST /v1/dnc/{e164}/unblockand the corresponding MCP toolunblock_destinationlift an automatic destination block; they also work for blocks created before this deployment. Older blocks may be absent fromGET /v1/dncandlist_dncbecause they predate the listing index, but direct unblock by phone number still works. Indexed blocks are returned in ablocked: [{e164, blockedAt, expiresAt}]array (times are Unix milliseconds) alongside the unchangednumbers. After a block is lifted, the failed-attempt counter starts over - three new failed attempts to the same number block it again, and lifting it again is not limited.groupCallsByConversation(the shared primitive behind /hovory, the CSV export, the Previous/Next navigation, and the stats) now sorts byrepresentative.startedAtdescending; it used to sort bykey, i.e. alphabetically by conversation id.TestCallOutcomehas a newno_answervalue kept separate fromfailed, so the panel never claims a cause it doesn't know. A name written in ALL CAPS now gets an ALL CAPS vocative form (RADEK->RADKU) in both the e-mail and the Overview header. - 11 Sep 2026 (1.7.1):
agent_busy(HTTP 409,cause: "busy") was added to the shared error mapPUBLIC_ERROR_HTTP/PUBLIC_ERROR_CAUSE(lib/types.ts). It is returned byswitchAgentProvider(lib/service/engine-provider.ts) - the only portal admin action that switches an agent's provider; REST v1 and MCP have no such input (agents.switch_providerin the capability catalog,lib/capabilities.ts). The check uses a newGET /v1/calls/activeclient (lib/engine.tslistActiveCalls): the agent's call is matched PRIMARILY by theagent_idfield, which the engine carries on an active call since the version that added it was deployed; the number stays a fallback for an older engine without it. - 11 Sep 2026 (1.7.0):
POST/PATCH /v1/agentsand the MCPcreate_agent/update_agent/save_agent_drafttools take a newvoicemail: {enabled, action: "mark"|"hangup"|"message", message?}field - only for an agent withprovider: "engine", otherwisevoicemail_requires_engine;messageis required whenaction: "message", up to 400 characters (invalid_voicemail_message).GET /v1/agentsreturns the configured value. The engine's post-call now carriesanswered_by/voicemail_reason/voicemail_message_left/voicemail_detected_at_secs;answered_bytakes precedence over the previous transcript-based estimate (lib/answered-by.ts) only withhumanandvoicemail-unknownis treated as "the engine didn't decide" and the transcript-based estimate is used instead.GET/list_calls/get_callfor an outbound call handled by the engine also returnvoicemailReason(a fourth value,human_reply, for a short human reply),voicemailMessageLeft, andvoicemailDetectedAtSecs. - 11 Sep 2026 (1.6.5): For an outbound call ended before answer the engine deletes the room and delivers a post-call webhook: empty transcript,
call_duration_secs: 0and the new additive fieldmetadata.engine.termination_reason(no_answerordisconnected_before_answer). The call getsstatus: no_answer,endReason: no_answerand price 0 as before, only immediately. TheGET /v1/callsreconciliation pairs engine outbound calls bycall_sidand sends zero duration for calls that never connected.GET /openapi.json:answeredBynow carries adescriptionabout the outbound-only limitation. - 11 Sep 2026 (1.6.4):
POST /v1/tasksandPATCH /v1/tasks/{id}(andcreate_task/update_taskin MCP) accept up to 1000recipients,POST /v1/tasks/csv-previewup to 1000 rows. A new task record size check (2 MB of serialized JSON) returnsvalidationwith fieldrecipients. - 11 Sep 2026 (1.6.3): ElevenLabs initiation webhook: the payload's
agent_idis now checked against the routed agent'selevenAgentIdORid- a foreignagent_idgets only a minimal response withoutconversation_config_override; the same check guards closing the conversation in the post-call webhook and the conversations-sync cron. CDR-sync cron: an unrecognized outbound CDR record from your own line (excluding the demo number), older than 600s and without an ambiguously matching existing record, is now billed askind: "sip"at the outbound rate with no agent surcharge (it shows up inGET /v1/callswithkind: "sip"; thechargedSipcounter is only in the cron's own response, not in the public API); a younger or ambiguously matching record is still just held for manual review.CallKindextended with"sip". The CDR shape of a real SIP client call has no production evidence yet (no registered softphone) - hence both safeguards. - 11 Sep 2026 (1.6.2): New endpoint
POST /v1/tasks/{id}/items/{itemId}/resolvewith body{ action: "retry" | "skip", revision }and the MCP toolresolve_task_item; new error codeitem_not_resolvable(409, account),task_not_editablefor a running or completed task. The engine call sync matches the account's own outbound leg by relay line and start time, closes an unanswered outbound call asno_answerand releases the tenant concurrency quota after closing.relay_lease_limitwhile dialing a task is a transient state that does not consume an attempt. - 11 Sep 2026 (1.6.1):
POST /v1/tasks/csv-previewand the portal recognise the phone column regardless of case, surrounding spaces and a trailing colon and accept aliases (telefon,tel,mobile,phone number); the response always names the columnphone. A single-column input without a header whose first value is a phone is treated as data. Without a phone column the preview returns a singlephoneerror on the header instead ofPhone is requiredon every row. Theagent_not_readycontract is unchanged. Documentation correction, 12 Sep 2026: the original MCP-tool claim was incorrect; CSV preview is REST/portal-only. - 11 Sep 2026 (1.6.0):
GET /v1/numbers/{e164}/sipand the MCPget_sip_credentialstool now also returnoutboundTrunkAddress,port,transportandinboundSignallingCidrsalongsideserver/username/password; MCPcreate_relay_leaseand theget_sip_credentialsdescription now explicitly distinguishserver(registration) fromoutboundTrunkAddress(a platform's outbound trunk). The/en/docs/bring-your-own-agentguide, the ElevenLabs integration page and the blog post no longer putserverintooutbound_trunk_config.address- onlyoutboundTrunkAddress. - 11 Sep 2026 (1.5.1):
GET /v1/calls,GET /v1/calls/{id}, thecall.completedwebhook and MCPlist_calls/get_call:answeredByis present only whendirection: "out"(older records included).POST /v1/messagesandsend_sms: an account without an active number and without a positivetopup/refund/adminledger entry getsinsufficient_credit(HTTP 402, causeaccount) after three sent SMS. - 8 Sep 2026 (1.5.0): New REST endpoints:
GET/PATCH /v1/account,PUT /v1/account/billing,GET /v1/api-keys,DELETE /v1/api-keys/{id}(self-revoke);PATCH /v1/calls/{id}andGET /v1/callsextended withfrom,to,agentId,flagged,goal,outcomequery parameters;POST /v1/tools/test,POST /v1/tools/{id}/test,DELETE /v1/webhook, andGET /v1/tools/{id}now additionally returnsagents;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(no API key,x-volai-versionon every v1 response including errors, 429 and/openapi.json). 16 matching new MCP tools: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- revoking someone else's API key, creating an API key, and disconnecting an integration or card stay REST-only or portal-only (spec §3). The new capability catalog holds acapabilityfield on every endpoint and tool; guard tests verify the error table and response example on/docs/apimatch the real error codes for every endpoint. Additive only - no existing status or code changed. The newcontent/changelog.jsonfile is the single source of truth:/en/changelog, the changelog section on/docs/api, the Versioning section in SKILL.md, RSS (/en/feed/<area>),GET /v1/changelogand the MCPget_changelogtool are all generated views over it. Additive only - no existing content disappeared. New REST endpoints: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. 7 matching new MCP tools:list_tasks,get_task,create_task,update_task,pause_task,reconcile_task,start_task(which requires repeating the real recipient count and budget, otherwise it returns an error with the real numbers) - deleting a task and previewing a CSV stay REST and portal only (spec §3). Portal: a Delete task button on the task detail page with type-to-confirm. Additive only - no existing status or code changed. New REST endpoints: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/balancenow additionally returnsrunwayandnotice. 6 matching new MCP tools:list_ledger,create_topup_link,get_auto_topup,disable_auto_topup,list_billing_documents,get_billing_document(get_balanceextended with the samerunway/notice) -get_billing_documentdoes not return the PDF, which stays REST-only viaGET /v1/billing/documents/{id}/pdf(binary output); turning auto top-up on and changing its amount or card stay behind a Stripe Checkout link only, MCP has neither (spec §2, §3). Additive only - no existing status or code changed.GET /v1/accountnow returns a newnotifications.productUpdatesfield (on by default); there is a matching toggle in portal Settings. The daily email digest (cron0 6 * * *UTC) only sends to accounts with a verified email andproductUpdates !== false, at most one email per account per day. TheList-Unsubscribeheader and cookie-free one-click unsubscribe now also cover the reactivation email whenUNSUBSCRIBE_SECRETis configured on the server - without it the email still sends, just without the unsubscribe link. Additive only - no existing status or code changed. - 8 Sep 2026 (1.4.3): Every input property of all 44 MCP tools carries its own
description(clients see it intools/list; thePATCH .../events/{eventId}andPUT /v1/agents/{id}/draftbodies expose it inopenapi.jsontoo). The invoice endpointsGET /v1/integrations/{id}/invoices[/{invoiceId}]list their error codes by real reachability in the docs and in OpenAPI -integration_storage_errorwas removed, it never came from the API - and their query validation returns the human-readable message like the rest of v1 (same forGET .../events). Additive only - no status or code changed. - 8 Sep 2026 (1.4.2): Fixed Apple event reads and confirmed updates: iCloud rejects UID searches, so event lists now return opaque CalDAV resource IDs. Pass externalId unchanged as eventId; reload any Apple event IDs saved from earlier versions. Invalid Apple IDs return 400 calendar_invalid_query. Google identifiers are unchanged.
- 8 Sep 2026 (1.4.1):
POST /v1/agentsandcreate_agentacceptuseForOutboundTasks(choosing volai's own voice engine at creation, the same choice as the portal checkbox "Use for outbound tasks"; omitted = the deployment default, today ElevenLabs). Additive only - no status or code changed. - 8 Sep 2026 (1.4.0): Apple Calendar shares the same calendar flow over REST and MCP as Google. Fakturoid and ABRA Flexi added
GET /v1/integrations/{id}/invoices[/{invoiceId}]plus thelist_invoicesandget_invoicetools (the interface only reads invoices).GET /v1/integrationsandlist_integrationsalso returnproviderswith setup readiness and a browser connection URL. Provider credentials still require connection in the portal. Additive only - no status or code changed. - 7 Sep 2026 (1.3.0): Google Calendar entered the public interface -
GET /v1/integrations,GET /v1/integrations/{id}/calendars,GET /v1/integrations/{id}/events,GET/PATCH /v1/integrations/{id}/events/{eventId}and six MCP tools (list_integrations,list_calendars,list_calendar_events,get_calendar_event,propose_calendar_update,confirm_calendar_update), which takes REST to 50 endpoints and MCP to 42 tools. Every error response also carriescause(request/account/busy/service- who fixes it) andaction(one sentence on what to do).validationmessages name the field, the actual value and the accepted maximum instead of the raw validator text; the input caps for an agent's name, instructions, goal and knowledge are ten times the product limits, so the limit is always reported by the product error with the exact number. MCP errors carry the same fields instructuredContent.error(includingrequestId) and a text prefixed by cause. Additive only, no status or code changed. - 6 Sep 2026 (1.2.0): The
/v1/agents/{id}/draft*and/simulateendpoints lost theokwrapper and plain-string errors - errors now share the same shape,{error:{code,message,requestId,docsUrl}}, as the rest of v1;revisionin request bodies was renamed toexpectedRevision; addedhasMore/nextBeforepagination fields,X-Request-IdandRateLimit-*headers,POST /v1/webhook/test,GET /v1/webhook/deliveries, andGET /openapi.json. The draft endpoints were already public from 5 Sep 2026, but without a stable contract - they are therefore excluded from the versioning guarantee (rule 1 of the Versioning section) up to and including 1.2.0.
De volledige, doorlopend bijgewerkte lijst staat op de pagina Changelog (ook beschikbaar als RSS-feed).
Conventies
Gewoonten die voor alle endpoints gelden - niet slechts voor één.
Identifiers
Elk id heeft een voorvoegsel dat het recordtype aangeeft en is verder ondoorzichtig (ontleed het niet verder):
vk_ API-sleutel, ag_ agent, ad_ agentconcept, ado_ conceptbewerking, c_ gesprek, msg_ sms-bericht, tl_ tool, whd_ webhookaflevering, rl_ relay-lease, int_ gekoppelde integratie, inv_ belastingdocument (GET /v1/billing/documents), l_ tegoedboeking, task_ uitgaande taak, item_ taakontvanger, attempt_ een belpoging bij een taakitem, req_ verzoek-id (zie Foutformaat hierboven).
Tijdstempels
Tijdstempels zijn vrijwel overal unix-milliseconden (createdAt, startedAt, boughtAt, issuedAt, taxableAt) - de UITZONDERING zijn de velden van gekoppelde agenda's EN gekoppelde facturen: start, end, fetchedAt (agenda) en de parameters timeMin/timeMax zijn RFC 3339-strings (2026-09-08T10:00:00.000Z), nooit een getal; GET /v1/integrations/{id}/invoices en .../invoices/{externalId} zijn ook strings - fetchedAt heeft dezelfde RFC 3339-vorm, dueDate is een gewone datum (2026-09-08) rechtstreeks van Fakturoid/ABRA Flexi, of null als de provider er geen gaf. Het maandabonnement is een tweede uitzondering: periodStart en periodEnd (/v1/subscription, /v1/balance) zijn ISO 8601-strings in UTC, geen milliseconden.
Paginering - drie schema's, plus lijsten zonder
GET /v1/messages, GET /v1/calls en GET /v1/billing/documents accepteren limit + before (een ms-tijdstempel, exclusief) en antwoorden met hasMore/nextBefore - zie Paginering hierboven voor de details. Afspraken uit gekoppelde agenda's (GET /v1/integrations/{id}/events) gebruiken in plaats daarvan een ondoorzichtige pageToken/nextPageToken - timeMin/timeMax/search moeten tussen pagina's ongewijzigd blijven, pageToken alleen garandeert dat niet. GET /v1/credit/ledger accepteert alleen limit (1-200, standaard 50), helemaal geen cursor - het begint altijd bij de meest recente boeking. GET /v1/tasks zonder ids geeft hoogstens de 100 meest recente taken terug; ids leest tot vijf opgegeven taken per verzoek zonder een volledige inventaris van de tenant te beweren. GET /v1/webhook/deliveries heeft ook geen cursor en geeft hoogstens de laatste 50 afleveringen terug (geslaagde, mislukte en testafleveringen). Ook het zoeken in gekoppelde facturen houdt stilzwijgend op - de eerste 40 rijen van Fakturoid of 50 van ABRA Flexi, zonder cursor om meer te zien; verfijn in plaats daarvan de zoekopdracht. Al het andere dat een lijst teruggeeft (agents, nummers, mobiele nummers, tools, API-sleutels, de bel-mij-niet-lijst, stemmen) geeft de volledige set in één antwoord - er valt niets te pagineren.
Onbekende en Tsjechisch genoemde parameters
Een onbekende queryparameter wordt vrijwel overal stilzwijgend genegeerd (zodat een oudere client niet breekt op één extra parameter) - de uitzondering zijn de endpoints voor gekoppelde agenda's en facturen (/v1/integrations/{id}/...), die een onbekende parameter weigeren met 400 validation, omdat ze die doorsturen naar de upstream-provider en een stilzwijgend weggelaten filter daar eruit zou zien als ontbrekende gegevens in plaats van als een fout; en, om een niet-gerelateerde reden, GET /v1/credit/ledger en GET /v1/billing/documents, waarvan de queryschema's bewust strikt zijn, zodat een typefout in limit/before luid faalt in plaats van stilzwijgend te worden genegeerd.
Een handvol velden heeft bewust een Tsjechische naam: de adresflow voor nummers (GET /v1/numbers/address-options, daarna POST /v1/numbers/orders - gewoon POST /v1/numbers accepteert alleen e164/region en negeert een adres) geeft psc, obec, cobce, ulice (alleen waar het adres een straat heeft) en cp door - de exacte veldnamen van het RÚIAN-register (de eigen adrescodelijst van de operator) waaruit het nummer wordt besteld, zodat een waarde die tussen de twee heen en weer gaat nooit stilzwijgend verkeerd wordt vertaald. GET /v1/calls/{id}/recording?stahnout=1 is de enige Tsjechisch genoemde parameter buiten die flow (downloaden als bijlage in plaats van inline). Al het andere - zowel identifiers als velden en parameters - is Engels.
Quota, op één plek verzameld
Elk quotum wordt ook bij zijn eigen endpoint hierboven genoemd - dit is slechts de samenvatting:
- 60 verzoeken/min per API-sleutel (REST en MCP delen die, zie Snelheidslimieten hierboven)
- Sms: 1 per 2 s en 100/dag per account, pas na de eerste opwaardering van het tegoed
- Sms verzonden vanaf een mobiel nummer (
fromNumber): daarnaast 20 berichten per dag per account in de eerste 30 dagen nadat u het nummer hebt gekocht, daarna 50; hoogstens twee mobiele nummers per account - Uitgaande gesprekken (agent, bridge, proef): 10/min en 1000/dag per account, hoogstens 2 gelijktijdig lopende gesprekken van de ingebouwde agent
- Beltonen (
POST /v1/rings): 30/min en 500/dag per account en 120/uur naar één nummer, los van de gesprekslimieten - Spraakberichten (
POST /v1/voice-messages): hoogstens 200 per 24 uur per account, bovenop de gewone gesprekslimieten - Proefgesprek zonder eigen nummer (
POST /v1/callsmetsystemPromptin plaats vanagentId/from): daarnaast 3/24 uur per account EN een gedeeld plafond van 30/uur over ALLE accounts samen - het gedeelde plafond kan u raken buiten uw eigen schuld. POST /v1/agents/{id}/test-call: 3/dag per accountPOST /v1/tools/testen.../{id}/test: 10/min per accountPOST /v1/agents/{id}/simulate: 20/uur per accountPOST /v1/webhook/test: 10/uur per accountGET /v1/changelog: 30/min per IP (geen API-sleutel)- Taken: een lopende taak belt per cron-tik hoogstens ÉÉN ontvanger. De algemene wachtrij blijft op
*/2 * * * *en raakt hoogstens 20 taken over alle accounts; geregistreerde PetCenter-taken gebruiken een aparte tik* * * * *en wachtrij, met dezelfde gelijktijdigheids- en belsnelheidslimieten. Beide wachtrijen roteren eerlijk wanneer meer dan 20 taken wachten.
Webhook-herhalingen
Een mislukte aflevering krijgt tot 3 pogingen, 3 seconden uit elkaar, binnen dezelfde verzending. Voor gespreksgebeurtenissen (call.completed, call.failed, call.no_answer, call.missed) en voor message.received is dat niet het einde: de gebeurtenis blijft in de wachtrij staan en de aflevering wordt de komende minuten nog een paar keer herhaald, tot in totaal 5 rondes - ontdubbel op de event_id van de body voor het zeldzame geval dat hij vaker dan eens aankomt. message.sent en de testgebeurtenis webhook.test hebben geen wachtrij: na een mislukking wordt het alleen gelogd en niet opnieuw verzonden. Controleer GET /v1/webhook/deliveries (of list_webhook_deliveries) om de voortgang te zien, inclusief rondes vóór de laatste - attempts is daar de som van de pogingen over ALLE rondes.
Uitfasering
v1 heeft tot nu toe alleen toegevoegd (zie Versiebeheer hierboven) - de enige uitzondering was versie 1.5.1, die answeredBy niet meer teruggaf voor INKOMENDE gesprekken (het veld had daar nooit een zinvolle waarde; bij uitgaande gesprekken is het ongewijzigd). volai heeft nog geen enkel veld definitief buiten gebruik gesteld. Wanneer dat gebeurt, wordt het veld in GET /openapi.json gemarkeerd als deprecated: true en bevat het antwoord minstens 90 dagen van tevoren de headers Deprecation/Sunset, en een MCP-antwoord dat het veld raakt krijgt een vermelding in warnings (hetzelfde veld dat nu al wordt gebruikt voor de AI Act-waarschuwing) - de verwijdering zelf verschijnt alleen als nieuwe hoofdversie (v2), nooit stilzwijgend binnen v1.
Gesprekken versus conversaties - zie de alinea met dezelfde naam onder Gesprekken hieronder voordat u aantallen optelt.
Inloggegevens (een wachtwoord, een token, een app-specifiek wachtwoord) gaan alleen naar plekken waar de gebruiker ze al neerzette en die u kunt lezen (een omgevingsvariabele of een bestand dat hij heeft genoemd) - vraag er nooit in het gesprek naar en geef ze nooit terug; geldt nu voor Apple Agenda en ABRA Flexi (zie de onderdelen over agenda's en facturen hieronder).
Wat de API niet kan
U bedient de hele telefoniekant via API en MCP - nummers, gesprekken, sms, agents, webhooks, relay en de bel-mij-niet-lijst. Uw profiel, factuuradres en bedrijfsgegevens lopen ook via de API (het onderdeel Account hierboven). Een paar dingen blijven bewust alleen in het portaal:
| Wat | Waar het staat, en waarom |
|---|---|
| Tegoed opwaarderen | Portaal /en/credit (zowel eenmalige opwaarderingen als automatisch opwaarderen). Met een kaart betalen is een menselijke stap - een agent die zijn eigen tegoed opwaardeert om te kunnen blijven bellen, is precies wat we niet willen. Als een actie insufficient_credit raakt, vertel dat de gebruiker en stuur hem hierheen. |
| Een nieuwe API-sleutel aanmaken | Portaal /en/api-and-mcp. Weergeven (GET /v1/api-keys) en UW EIGEN sleutel intrekken (DELETE /v1/api-keys/{id}) lopen via de API - alleen het aanmaken van een nieuwe sleutel niet: een sleutel die een andere sleutel aanmaakt, zou betekenen dat één gelekte sleutel permanente toegang geeft die nooit meer kan worden ingetrokken. |
| Uw wachtwoord wijzigen | Portaal /en/settings. Naam, accounttaal en factuuradres lopen via PATCH /v1/account en PUT /v1/account/billing (het onderdeel Account hierboven) - het wachtwoord niet, dat is een zuiver browsergebonden actie met eigen bevestiging. |
| Belastingfacturen | Portaal /en/credit, onderdeel Facturen - voor elke opwaardering van het tegoed staat daar een downloadbare pdf klaar; dezelfde pdf downloadt u met GET /v1/billing/documents/{id}/pdf. De API kan geen factuur uitgeven: het is een boekhoudkundig document met eigen doorlopende nummering dat na uitgifte nooit meer verandert en nooit twee keer mag worden aangemaakt. |
| Sms op spraaknummers | Nergens - spraaknummers van volai kunnen geen sms ontvangen. De add-on mobiel nummer en het nummer voor bellen en sms ontvangen wel sms, zie de onderdelen over mobiele nummers en Nummer voor bellen en smss hieronder. |
In de andere richting ontbreekt bewust meer dan één ding: het vrijgeven van een gekocht nummer (DELETE /v1/numbers/{e164}), een mobiel nummer (DELETE /v1/sms-numbers/{e164}) of een nummer voor bellen en sms (DELETE /v1/voice-sms-numbers/{e164}) bestaat zowel in de REST API als in het portaal, maar niet onder de MCP-tools - een onomkeerbare actie is daar één verkeerd gelezen zin verwijderd van te riskant. Ook bewust afwezig: het downloaden van een opname (een binaire body, GET /v1/calls/{id}/recording), het detail van één bericht of tool (de lijsten list_messages/list_tools bevatten dezelfde velden al, alleen niet per ID), en een aparte testgesprek-tool - make_call met agentId belt hetzelfde gesprek als POST /v1/agents/{id}/test-call, alleen zonder het strengere dagplafond van 3 gesprekken.
Account
Profiel, noodadres, factuurgegevens en een overzicht van API-sleutels. Het aanmaken van nieuwe API-sleutels (POST /v1/api-keys) wordt hier niet aangeboden - een sleutel die sleutels aanmaakt, zou van het kwijtraken van één sleutel permanente toegang maken. Maak een nieuwe sleutel aan in het portaal (sectie Toegang en API). E-mailmeldingen (callSummary, lowCredit, reactivation, productUpdates) zijn via deze interface alleen LEESBAAR - schrijven kan alleen in het portaal, omdat lowCredit de enige rem is tussen een opgebruikt tegoed en stilzwijgend uitvallen.
GET/v1/account
Profiel, noodadres, factuurgegevens en de status van e-mailmeldingen (alleen lezen).
Verzoek
curl https://volai.cz/v1/account \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"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 }
},
"testCalls": { "max": 3, "remaining": 2, "resetAt": 1755086400000 },
"serviceStopped": null
}Foutcodes
- 404
user_not_foundDe gebruiker die eigenaar is van deze API-sleutel ontbreekt in het account - een ongebruikelijke toestand; neem contact op met support.
PATCH/v1/account
Werkt de naam en de accounttaal bij. locale bepaalt de taal van e-mails en belastingdocumenten, NIET de taal van dit antwoord - REST en MCP antwoorden altijd in het Engels. Minstens één veld is verplicht.
Verzoek
curl -X PATCH https://volai.cz/v1/account \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"locale":"en"}'Antwoord
{
"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 }
}
}Foutcodes
- 400
validationDe request body is geen geldig JSON-object, bevat geen veld om bij te werken,nameis een lege string of langer dan 200 tekens, oflocaleis nochcsnochen. - 400
invalid_nameDe naam is na het wegknippen van spaties aan begin en einde leeg. - 404
user_not_foundDe gebruiker die eigenaar is van deze API-sleutel ontbreekt in het account - een ongebruikelijke toestand; neem contact op met support.
PUT/v1/account/billing
Slaat het noodadres en de factuurgegevens op. address bevat, indien aanwezig, straat, plaats en postcode samen - ze kunnen niet los worden verstuurd. company, ico en dic worden SAMENGEVOEGD met wat al is opgeslagen: een ontbrekend veld blijft ONGEWIJZIGD, alleen een expliciete lege string ("") wist het (bij dic wist u daarmee ook de VIES-verificatie). Een btw-nummer uit een ander EU-land wordt gecontroleerd in het VIES-register; vies in het antwoord wordt alleen ingevuld als het resultaat een gecontroleerd btw-nummer bevat (zojuist of uit een bewaarde eerdere controle). Een Tsjechisch btw-nummer (CZ...) wordt niet gecontroleerd - het heeft geen invloed op het tarief.
Verzoek
curl -X PUT https://volai.cz/v1/account/billing \
-H "Authorization: Bearer vk_YOUR_KEY" \
-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"}}'Antwoord
{
"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." }
}Foutcodes
- 400
validationDe request body is geen geldig JSON-object, bevat een onbekend veld (het schema is strikt),company/ico/dicis langer dan 120/20/20 tekens,addressbevat een onbekend veld,address.streetofaddress.cityis leeg of langer dan 200 tekens, ofaddress.zipis korter dan 3 of langer dan 10 tekens. - 400
invalid_addressStraat of plaats is na het wegknippen van spaties aan begin en einde leeg (bijvoorbeeld een veld met alleen spaties), of de postcode doorstaat de normalisatie niet - ze lijkt niet op een postcode. - 400
invalid_icoHet controlecijfer van het bedrijfsnummer (IČO) klopt niet. - 400
invalid_dicHet btw-nummer heeft geen geldige vorm (TsjechischCZplus 8-10 cijfers, of een buitenlandse landcode plus registratienummer). - 400
foreign_vat_unsupportedDe landcode van het btw-nummer hoort niet bij een EU-lidstaat - verlegging van btw geldt alleen binnen de EU. - 400
vat_id_not_foundHet Europese btw-register (VIES) kent dit btw-nummer niet. Zonder controle wordt Tsjechische btw in rekening gebracht. - 503
vat_registry_unavailableHet VIES-register is tijdelijk niet beschikbaar - probeer het later opnieuw; een eerdere controle (indien aanwezig) blijft geldig. - 404
user_not_foundDe gebruiker die eigenaar is van deze API-sleutel ontbreekt in het account - een ongebruikelijke toestand; neem contact op met support.
POST/v1/account/cancellation-request
Vraagt om opheffing van het account, hetzelfde als het formulier Account opheffen in de instellingen. reason is verplicht (too_expensive, callers_hang_up, missing_feature, not_needed of other), note is een optionele opmerking van maximaal 500 tekens. Het verzoek wijzigt het account NIET en verwijdert niets: het wordt vastgelegd, het volai-team krijgt een melding met de reden en de klant ontvangt een bevestigingsmail in de accounttaal; het account wordt daarna handmatig opgeschort. Opnieuw versturen is onschadelijk: het geeft het oorspronkelijke verzoek terug met alreadyRequested: true en verstuurt geen nieuwe e-mail.
Verzoek
curl -X POST https://volai.cz/v1/account/cancellation-request \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"reason":"callers_hang_up","note":"Volající zavěšují po první větě."}'Antwoord
{
"cancellationRequest": {
"reason": "callers_hang_up",
"note": "Volající zavěšují po první větě.",
"requestedAt": 1793520000000,
"alreadyRequested": false
}
}Foutcodes
- 400
validationDe request body is geen geldig JSON-object, bevat een onbekend veld (het schema is strikt),reasonontbreekt of is niet een van de vijf waarden, ofnoteis langer dan 500 tekens. - 404
user_not_foundDe gebruiker die eigenaar is van deze API-sleutel ontbreekt in het account - een ongebruikelijke toestand; neem contact op met support.
GET/v1/api-keys
API-sleutels van het account. id zijn alleen de eerste 12 tekens van de vingerafdruk van de sleutel - de volledige vingerafdruk wordt nooit teruggegeven.
Verzoek
curl https://volai.cz/v1/api-keys \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"apiKeys": [
{
"id": "3f9a2c8e1b04",
"label": "Production integration",
"prefix": "vk_8h2j4kx",
"createdAt": 1750000000000,
"lastUsedAt": 1757280000000
}
]
}DELETE/v1/api-keys/{id}
Zelf intrekken: via de API mag een sleutel alleen ZICHZELF intrekken, nooit een andere sleutel van het account - anders zou een gestolen sleutel alle andere integraties afsluiten en zelf actief blijven. Andere sleutels intrekken kan alleen in het portaal. Na het intrekken gaat er een e-mail naar de accounteigenaar, ongeacht wie het verzocht.
Verzoek
curl -X DELETE https://volai.cz/v1/api-keys/3f9a2c8e1b04 \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"revoked": true
}Foutcodes
- 404
not_foundEr bestaat geen sleutel met ditidop het account. - 403
api_key_self_onlyHetidin het pad hoort bij een andere sleutel dan die waarmee dit verzoek wordt geauthenticeerd - via de API kunt u alleen uzelf intrekken.
Tegoed
balanceHal en elk tarief in deze API zijn exclusief btw - volai is btw-plichtig, maar btw wordt alleen toegepast wanneer u het tegoed in het portaal met een kaart opwaardeert (zie de pagina /en/pricing), de API zelf houdt er geen rekening mee. Een volledige uitleg met voorbeeldflows staat op /en/docs/credit.
GET/v1/balance
Het huidige tegoedsaldo van het account, plus runway (een schatting van hoeveel dagen het saldo meegaat bij het huidige uitgavetempo) en notice (dezelfde melding die op de overzichtspagina van het portaal staat, null als er geen is) - beide TOEGEVOEGD, zodat een client die alleen balanceHal/balanceCzk/currency leest blijft werken. Ook topupBalanceHal (het opgewaardeerde deel, negatief als het account schuld heeft) en subscription (het maandabonnement, of null): balanceHal blijft het TOTAAL van het tegoed uit het maandabonnement en het opgewaardeerde tegoed.
Verzoek
curl https://volai.cz/v1/balance \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"balanceHal": 8730,
"balanceCzk": 87.30,
"currency": "CZK",
"runway": { "dailyAverageHal": 2910, "runwayDays": 3 },
"notice": { "kind": "runway", "days": 3 },
"topupBalanceHal": 8730,
"subscription": null,
"serviceStopped": null
}Foutcodes
- 503
ledger_unavailableHet saldo of de boekingsgeschiedenis kon nu niet worden gelezen - probeer het zo meteen opnieuw.
GET/v1/credit/ledger
Geschiedenis van tegoedboekingen, meest recente eerst, ZONDER cursor (alleen limit, 1-200, standaard 50). De description van elke boeking is een Engelse beschrijving die uit type wordt SAMENGESTELD - de opgeslagen Tsjechische notitie gaat nooit naar buiten.
Verzoek
curl "https://volai.cz/v1/credit/ledger?limit=3" \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"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" }
]
}Foutcodes
- 400
validationlimitvalt buiten het bereik 1-200, of het verzoek bevat een extra onbekende parameter. - 503
ledger_unavailableHet saldo of de boekingsgeschiedenis kon nu niet worden gelezen - probeer het zo meteen opnieuw.
POST/v1/credit/topup
Maakt een Stripe Checkout-link aan voor een eenmalige opwaardering met een vast bedrag in CZK. Er wordt niets afgeschreven totdat een MENS de betaling in een browser voltooit. Accepteert Idempotency-Key.
Verzoek
curl -X POST https://volai.cz/v1/credit/topup \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: topup-2026-09-08-01" \
-d '{"amountCzk": 500}'Antwoord
{
"checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3d4"
}Foutcodes
- 400
validationamountCzkontbreekt, is geen positief geheel getal, of de body bevat een onbekend veld. - 400
invalid_amountHet bedrag is niet een van de vaste aangeboden waarden. - 400
billing_address_requiredHet factuuradres van het account ontbreekt of is onvolledig - zonder kan geen geldig belastingdocument worden uitgegeven. Voeg het toe viaPUT /v1/account/billing. - 404
user_not_foundDe gebruiker die eigenaar is van deze API-sleutel ontbreekt in het account - een ongebruikelijke toestand; neem contact op met support. - 503
stripe_not_configuredBetalingen zijn uitgeschakeld voor deze volai-installatie - niet specifiek voor uw verzoek. - 502
stripe_errorStripe weigerde een betaalsessie aan te maken - probeer het zo meteen opnieuw.
GET/v1/credit/auto-topup
Status van automatisch opwaarderen: ingeschakeld/uitgeschakeld, drempel, bedrag, opgeslagen kaart (alleen merk en laatste vier cijfers), uitgaven en plafond voor deze kalendermaand.
Verzoek
curl https://volai.cz/v1/credit/auto-topup \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"autoTopup": {
"enabled": true,
"thresholdHal": 10000,
"amountHal": 50000,
"card": { "brand": "visa", "last4": "4242" },
"spentThisMonthHal": 50000,
"monthlyCapHal": 1000000,
"disabledReason": null
}
}PATCH/v1/credit/auto-topup
UITSLUITEND uitschakelen ({"enabled": false}) en/of de drempel wijzigen ({"thresholdHal": ...}) - inschakelen of het bedrag wijzigen kan alleen via POST .../setup (Stripe Checkout), nooit via een directe schrijfactie, zodat een kale enabled: true nooit kan leiden tot afschrijving van een opgeslagen kaart zonder dat er een mens bij is.
Verzoek
curl -X PATCH https://volai.cz/v1/credit/auto-topup \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"thresholdHal": 20000}'Antwoord
{
"autoTopup": {
"enabled": true,
"thresholdHal": 20000,
"amountHal": 50000,
"card": { "brand": "visa", "last4": "4242" },
"spentThisMonthHal": 50000,
"monthlyCapHal": 1000000,
"disabledReason": null
}
}Foutcodes
- 400
validationDe body bevatenabled: trueofamountHal(beide horen alleen inPOST .../setup),thresholdHalvalt buiten het aangeboden bereik, of de body bevat nochenablednochthresholdHal. - 400
autotopup_not_configuredAutomatisch opwaarderen is nooit viaPOST .../setupingesteld - er valt niets uit te schakelen, te wijzigen of een kaart voor te vervangen. - 400
invalid_amountHet bedrag is niet een van de vaste aangeboden waarden.
POST/v1/credit/auto-topup/setup
Maakt een Stripe Checkout-sessie aan waarvan de voltooiing METEEN amountHal afschrijft als eerste opwaardering, en alleen daarmee wordt de kaart opgeslagen voor toekomstig automatisch opwaarderen - werkt ook met een kaart die al eerder was opgeslagen. De enige manier om automatisch opwaarderen in te schakelen of het bedrag te wijzigen; een wijziging van alleen de drempel zonder afschrijving is de PATCH hierboven. Het factuuradres is verplicht. Wordt geweigerd met subscription_cap_active zolang een maandabonnement met het bestedingsplafond loopt. Accepteert Idempotency-Key.
Verzoek
curl -X POST https://volai.cz/v1/credit/auto-topup/setup \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: autotopup-setup-2026-09-08-01" \
-d '{"thresholdHal": 10000, "amountHal": 50000}'Antwoord
{
"checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3d4"
}Foutcodes
- 400
validationthresholdHalofamountHalontbreekt, is geen niet-negatief/positief geheel getal, of de body bevat een onbekend veld. - 400
invalid_amountHet bedrag is niet een van de vaste aangeboden waarden. - 400
billing_address_requiredHet factuuradres van het account ontbreekt of is onvolledig - zonder kan geen geldig belastingdocument worden uitgegeven. Voeg het toe viaPUT /v1/account/billing. - 409
subscription_cap_activeAutomatisch opwaarderen kan niet worden ingeschakeld zolang het abonnement met het bestedingsplafond loopt. Schakel het abonnement over op opwaarderen met de kaart (PATCH /v1/subscriptiongeeft een bevestigingslink) of houd het plafond. - 404
user_not_foundDe gebruiker die eigenaar is van deze API-sleutel ontbreekt in het account - een ongebruikelijke toestand; neem contact op met support. - 503
stripe_not_configuredBetalingen zijn uitgeschakeld voor deze volai-installatie - niet specifiek voor uw verzoek. - 502
stripe_errorStripe weigerde een betaalsessie aan te maken - probeer het zo meteen opnieuw.
POST/v1/credit/auto-topup/card
Maakt een Stripe Checkout-sessie aan in de modus kaart instellen voor automatisch opwaarderen - er wordt niets afgeschreven, alleen een nieuwe kaart wordt opgeslagen.
Verzoek
curl -X POST https://volai.cz/v1/credit/auto-topup/card \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3d4"
}Foutcodes
- 400
autotopup_not_configuredAutomatisch opwaarderen is nooit viaPOST .../setupingesteld - er valt niets uit te schakelen, te wijzigen of een kaart voor te vervangen. - 404
user_not_foundDe gebruiker die eigenaar is van deze API-sleutel ontbreekt in het account - een ongebruikelijke toestand; neem contact op met support. - 503
stripe_not_configuredBetalingen zijn uitgeschakeld voor deze volai-installatie - niet specifiek voor uw verzoek. - 502
stripe_errorStripe weigerde een betaalsessie aan te maken - probeer het zo meteen opnieuw.
DELETE/v1/credit/auto-topup/card
Ontkoppelt de opgeslagen kaart METEEN - de HELE configuratie van automatisch opwaarderen wordt verwijderd (inclusief drempel en bedrag), niet alleen de kaart. Opnieuw inschakelen werkt alleen via POST .../setup, dat meteen een eerste opwaardering afschrijft. Zolang een lopend maandabonnement met dezelfde kaart betaalt, wordt de aanroep geweigerd met invalid_state: vervang in dat geval de kaart met POST /v1/subscription/card.
Verzoek
curl -X DELETE https://volai.cz/v1/credit/auto-topup/card \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"removed": true
}Foutcodes
- 400
invalid_stateDe kaart is ook de kaart van het lopende maandabonnement, dus alleen de kaart ontkoppelen zou de volgende verlenging laten mislukken. Vervang haar metPOST /v1/subscription/card; ze kan los worden ontkoppeld zodra het abonnement is beëindigd.
GET/v1/account/spend-cap
Maandelijks bestedingsplafond: of er een is ingesteld, capHal, warnShare, inboundAboveCap, en de uitgaven van deze kalendermaand aan gesprekken, sms en de spraakagent (monthSpendHal, maand YYYY-MM in Praagse tijd), het restant en of het plafond reached is. Uitgaven worden op elk account geteld, ook zonder plafond; nummerkosten en het maandabonnement tellen niet mee.
Verzoek
curl https://volai.cz/v1/account/spend-cap \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"spendCap": {
"enabled": true,
"capHal": 500000,
"warnShare": 0.8,
"inboundAboveCap": false,
"month": "2026-11",
"monthSpendHal": 183400,
"remainingHal": 316600,
"reached": false,
"updatedAt": 1793520000000
}
}Foutcodes
- 503
ledger_unavailableHet saldo of de boekingsgeschiedenis kon nu niet worden gelezen - probeer het zo meteen opnieuw.
PUT/v1/account/spend-cap
Stelt het maandelijkse bestedingsplafond in of wijzigt het: capHal in hele hellers (50 tot 1.000.000 CZK), optioneel warnShare (0,5 tot 0,99, standaard 0,8) voor de waarschuwingsmail en optioneel inboundAboveCap (standaard false). Zodra de uitgaven van de maand het plafond bereiken, mislukken nieuwe uitgaande gesprekken, beltonen, proefgesprekken en sms met spend_cap_reached (HTTP 402), en inkomende gesprekken naar uw agents eindigen na één zin met de mededeling dat de lijn tijdelijk niet beschikbaar is (endReason: spend_cap, prijs 0), tenzij inboundAboveCap true is. Gesprekken die al bezig zijn worden niet afgebroken, dus de uitgaven kunnen het plafond met hun kosten overschrijden. Doorschakelen zonder agent wordt niet door het plafond gestopt, en SIP-lijnen van een account boven het plafond verlaten de SIP-proxy (inkomende gesprekken naar uw eigen centrale stoppen dan ook), tenzij inboundAboveCap true is; met inboundAboveCap: true blijft de lijn in de proxy, dus het plafond stopt ook uitgaande gesprekken vanuit uw centrale niet en die worden nog steeds in rekening gebracht. Automatisch opwaarderen blijft ongewijzigd.
Verzoek
curl -X PUT https://volai.cz/v1/account/spend-cap \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"capHal": 500000, "warnShare": 0.8, "inboundAboveCap": false}'Antwoord
{
"spendCap": {
"enabled": true,
"capHal": 500000,
"warnShare": 0.8,
"inboundAboveCap": false,
"month": "2026-11",
"monthSpendHal": 183400,
"remainingHal": 316600,
"reached": false,
"updatedAt": 1793520000000
}
}Foutcodes
- 400
validationcapHalontbreekt, is geen geheel aantal hellers tussen 5.000 en 100.000.000,warnSharevalt buiten 0,5 tot 0,99,inboundAboveCapis geen boolean, of de body bevat een onbekend veld. - 502
ledger_failedHet plafond kon nu niet worden weggeschreven - er is niets gewijzigd. Probeer het zo meteen opnieuw. - 503
ledger_unavailableHet saldo of de boekingsgeschiedenis kon nu niet worden gelezen - probeer het zo meteen opnieuw.
DELETE/v1/account/spend-cap
Verwijdert het maandelijkse bestedingsplafond; het account gedraagt zich weer als voordat het werd ingesteld. Een plafond verwijderen dat niet is ingesteld lukt ook.
Verzoek
curl -X DELETE https://volai.cz/v1/account/spend-cap \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"spendCap": {
"enabled": false,
"capHal": null,
"warnShare": null,
"inboundAboveCap": null,
"month": "2026-11",
"monthSpendHal": 183400,
"remainingHal": null,
"reached": false,
"updatedAt": null
}
}Foutcodes
- 502
ledger_failedHet plafond kon nu niet worden weggeschreven - er is niets gewijzigd. Probeer het zo meteen opnieuw. - 503
ledger_unavailableHet saldo of de boekingsgeschiedenis kon nu niet worden gelezen - probeer het zo meteen opnieuw.
GET/v1/billing/documents
Lijst van uitgegeven belastingdocumenten (opwaarderingen van het tegoed, betalingen van maandabonnementen, done-for-you-bestellingen) en creditnota's, meest recente eerst. before is een cursor (Unix-ms, exclusief) - stuur de nextBefore van de vorige pagina. De onderliggende opslag geeft nooit meer dan 100 records per aanroep terug, ook niet als limit meer vraagt.
Verzoek
curl "https://volai.cz/v1/billing/documents?limit=2" \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"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
}Foutcodes
- 400
validationlimitofbeforevalt buiten het toegestane bereik, of het verzoek bevat een extra onbekende parameter.
GET/v1/billing/documents/{id}
De gegevens van één belastingdocument - bedragen, btw, klant, betalingsreferentie en een link naar de pdf.
Verzoek
curl https://volai.cz/v1/billing/documents/inv_4kX9mQ2pRtLz \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"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"
}
}Foutcodes
- 404
invoice_not_foundEr bestaat geen document met ditidop het account (een vreemd of ontbrekendidgeeft dezelfde fout).
GET/v1/billing/documents/{id}/pdf
Downloadt hetzelfde document als application/pdf - het wordt bij elke download vers gegenereerd, niet opgeslagen.
Verzoek
curl https://volai.cz/v1/billing/documents/inv_4kX9mQ2pRtLz/pdf \
-H "Authorization: Bearer vk_YOUR_KEY" \
-o invoice.pdfFoutcodes
- 404
invoice_not_foundEr bestaat geen document met ditidop het account (een vreemd of ontbrekendidgeeft dezelfde fout).
Maandabonnement
Een maandabonnement schrijft eenmaal per maand af van de opgeslagen kaart en voegt maandelijks tegoed toe, dat vóór opgewaardeerd tegoed wordt besteed en aan het einde van de periode vervalt. Elk niveau heeft een prijs en een groter maandtegoed, beide exclusief btw (zie tiers in GET /v1/subscription). Geld verplaatst zich alleen met een mens bij de kaart: deze API start of verhoogt nooit zelf terugkerende afschrijvingen. Een abonnement starten, een mislukte verlenging betalen en de kaart vervangen geven een Stripe Checkout-link terug, en het niveau verhogen, een abonnement hervatten dat aan het einde staat of overstappen op opwaarderen van de kaart geeft een confirmUrl terug die een mens in het portaal moet bevestigen. Een volledige uitleg staat op /en/docs/credit.
GET/v1/subscription
Het maandabonnement van het account en alles wat nodig is om er een aan te bieden. subscription is null als het account nooit een abonnement had; een beëindigd abonnement wordt nog steeds teruggegeven, met status: "canceled". tiers toont de aangeboden niveaus met hun priceHal en creditHal (hellers, exclusief btw). topupBalanceHal is het opgewaardeerde tegoed; GET /v1/balance bevat dezelfde twee blokken naast het totaal. available geeft aan of dit account NU een NIEUW abonnement kan starten: het aanbod staat aan voor het account en het account heeft geen lopend abonnement (active, past_due), omdat een tweede niet kan worden gestart en de checkout subscription_exists zou geven; zodra het abonnement is beëindigd (canceled) is het weer true. Een bestaand abonnement wordt altijd teruggegeven en blijft werken als dit false is. recommendation is de goedkoopste manier om te betalen voor wat het account de afgelopen 30 dagen daadwerkelijk heeft uitgegeven (dezelfde berekening als de calculator op de website): spendHal, cheapestPath (per_second of plan), het goedkoopste niveau met opwaarderingen tier en de kosten ervan tierTotalHal, de besparing savingsHal, worthIt, of de besparing groot genoeg is, steady, of de uitgaven aanhoudend zijn en geen eenmalige piek (het gebruik van de laatste 14 dagen omgerekend naar 30 dagen, plus maandelijkse nummerkosten, bereikt minstens de prijs van het aanbevolen niveau; het portaal biedt het abonnement alleen aan als worthIt en steady beide true zijn), en options, alle manieren om voor dezelfde uitgaven te betalen (per seconde en elk niveau met opwaarderingen; dezelfde cijfers die het portaal toont bij het wijzigen van het abonnementsniveau). Het is null als het abonnement niet aan het account wordt aangeboden of de tegoedgeschiedenis geen 30 dagen terugreikt (een nieuw account). previousPeriod vat de betaalde periode vóór de huidige samen (subscription.periodStart), met dezelfde cijfers als de verlengingsmail: het tier, periodStart en periodEnd (ISO 8601), het maandtegoed creditHal, hoeveel ervan is gebruikt usedHal en usedPercent (naar beneden afgerond), uitgaven boven het maandtegoed overHal (bij opwaarderingen zonder schuld die het volgende tegoed heeft afgelost, bij het plafond inclusief schuld; null als de tegoedgeschiedenis het niet kan bewijzen), wat is vervallen expiredHal, exhaustionMode en exhaustedAt, wanneer het maandtegoed op raakte (null als dat niet gebeurde, of als de tegoedgeschiedenis niet kan tonen wanneer). Het wordt alleen teruggegeven zolang het abonnement actief is, wat available ook zegt; het is null zonder abonnement, bij status past_due of canceled, in de eerste periode, wanneer de tegoedgeschiedenis het gebruik niet kon bewijzen, en 45 dagen na de verlenging die het opsloeg.
Verzoek
curl https://volai.cz/v1/subscription \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"subscription": {
"tier": "990",
"status": "active",
"periodStart": "2026-10-17T08:00:00.000Z",
"periodEnd": "2026-11-17T09:00:00.000Z",
"monthlyCreditHal": 130000,
"monthlyRemainingHal": 98500,
"exhaustionMode": "topup",
"cancelAtPeriodEnd": false,
"nextTier": null,
"card": {
"brand": "visa",
"last4": "4242"
}
},
"tiers": [
{
"id": "490",
"priceHal": 49000,
"creditHal": 60000
},
{
"id": "990",
"priceHal": 99000,
"creditHal": 130000
},
{
"id": "1990",
"priceHal": 199000,
"creditHal": 280000
}
],
"topupBalanceHal": 8500,
"available": false,
"recommendation": {
"spendHal": 105500,
"days": 30,
"cheapestPath": "plan",
"tier": "490",
"tierTotalHal": 94500,
"savingsHal": 11000,
"worthIt": true,
"steady": true,
"options": [
{
"path": "per_second",
"tier": null,
"totalHal": 105500
},
{
"path": "plan",
"tier": "490",
"totalHal": 94500
},
{
"path": "plan",
"tier": "990",
"totalHal": 99000
},
{
"path": "plan",
"tier": "1990",
"totalHal": 199000
}
]
},
"previousPeriod": {
"tier": "990",
"periodStart": "2026-09-17T08:00:00.000Z",
"periodEnd": "2026-10-17T08:00:00.000Z",
"creditHal": 130000,
"usedHal": 105500,
"usedPercent": 81,
"overHal": 0,
"expiredHal": 24500,
"exhaustionMode": "topup",
"exhaustedAt": null
}
}Foutcodes
- 503
ledger_unavailableHet saldo of de boekingsgeschiedenis kon nu niet worden gelezen - probeer het zo meteen opnieuw.
POST/v1/subscription/checkout
Maakt een Stripe Checkout-link aan voor de eerste periode van een maandabonnement. Hier wordt niets afgeschreven: een MENS betaalt op de Stripe-pagina, de kaart wordt opgeslagen voor de maandelijkse verlengingen en pas daarna wordt het maandtegoed toegevoegd. exhaustionMode heeft geen standaardwaarde en moet worden gekozen: topup koopt tegoed met de opgeslagen kaart zodra het maandtegoed op is (zoals automatisch opwaarderen), cap koopt niets en begrenst de maand op de prijs van het abonnement. Een consumentenaccount (zonder bedrijfsnummer, btw-nummer of bedrijfsnaam) krijgt geen Checkout-link: de aanroep geeft 409 subscription_confirmation_required met een confirmUrl naar het formulier in het portaal, waar de persoon uitdrukkelijk vraagt de dienst te starten voordat de herroepingstermijn van 14 dagen afloopt, het gedrag na uitputting kiest en betaalt; consumerConsent wordt niet meer verstuurd. Het factuuradres is verplicht. Accepteert Idempotency-Key.
Verzoek
curl -X POST https://volai.cz/v1/subscription/checkout \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: plan-2026-10-17-01" \
-d '{"tier": "990", "exhaustionMode": "cap", "consumerConsent": true}'Antwoord
{
"checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3d4"
}Foutcodes
- 400
validationtierofexhaustionModeontbreekt of is niet een van de aangeboden waarden,consumerConsentis geen boolean, of de body bevat een onbekend veld (een retour-URL wordt nooit geaccepteerd). - 404
pausal_unavailableHet maandabonnement wordt nog niet aan dit account aangeboden (het wordt geleidelijk uitgerold). Opnieuw proberen helpt niet;availableinGET /v1/subscriptionzegt wanneer het zover is. - 404
user_not_foundDe gebruiker die eigenaar is van deze API-sleutel ontbreekt in het account - een ongebruikelijke toestand; neem contact op met support. - 400
billing_address_requiredHet factuuradres van het account ontbreekt of is onvolledig - zonder kan geen geldig belastingdocument worden uitgegeven. Voeg het toe viaPUT /v1/account/billing. - 409
subscription_existsHet account heeft al een lopend abonnement. Wijzig het in plaats daarvan metPATCH /v1/subscription; een beëindigd abonnement staat een nieuw niet in de weg. - 409
subscription_confirmation_requiredEen consumentenaccount (zonder bedrijfsnummer, btw-nummer of bedrijfsnaam): via de API wordt geen Checkout aangemaakt, de foutbody bevat eenconfirmUrlnaar het portaalformulier, waar de persoon vraagt de dienst te starten en betaalt. Er is niets afgeschreven. - 400
consumer_consent_requiredAlleen in het portaal: een consument verstuurde het formulier zonder het verzoek om de dienst te starten aan te vinken. Via de API krijgt een consument in plaats daarvan 409subscription_confirmation_required. Er is niets afgeschreven. - 503
stripe_not_configuredBetalingen zijn uitgeschakeld voor deze volai-installatie - niet specifiek voor uw verzoek. - 502
stripe_errorStripe weigerde een betaalsessie aan te maken - probeer het zo meteen opnieuw. - 503
ledger_unavailableHet saldo of de boekingsgeschiedenis kon nu niet worden gelezen - probeer het zo meteen opnieuw.
PATCH/v1/subscription
Wijzigt het abonnement zonder de kaart te belasten. Deze wijzigingen gelden direct, omdat ze nooit verhogen wat kan worden afgeschreven: het tier verlagen (gaat in vanaf de volgende periode, nextTier toont het), cancelAtPeriodEnd: true (geen verdere afschrijving, het tegoed blijft bruikbaar tot periodEnd) en exhaustionMode: "cap" (schakelt ook automatisch opwaarderen uit). Het tier verhogen, cancelAtPeriodEnd: false bij een abonnement dat is ingesteld om te eindigen (hervatten) en exhaustionMode: "topup" gelden NIET: de aanroep geeft 409 subscription_confirmation_required en niets in het verzoek wordt gewijzigd. De foutbody bevat confirmUrl, een link naar de portaalpagina waar een bij het account ingelogd persoon de wijziging bevestigt; de link is 24 uur geldig en werkt één keer. Verstuur wijzigingen die bevestiging nodig hebben één per verzoek. Een verzoek dat niets wijzigt (een abonnement hervatten dat niet eindigt, topup terwijl opwaarderingen al lopen, een niveau waar het abonnement al naartoe overgaat) slaagt zonder bevestiging en zonder fout. Dezelfde body kan dus na een storing veilig opnieuw worden verstuurd: wat al geldt wordt overgeslagen en alleen wat nog openstaat wordt uitgevoerd. De ene uitzondering is teruggaan naar het huidige niveau (het annuleert de geplande wijziging): een herhaling heeft niets meer te annuleren en eindigt met invalid_tier, verstuur de overige velden dus apart. Bij een abonnement dat eindigt geeft een tier-wijziging alleen invalid_state (maak eerst de opzegging ongedaan); samen met cancelAtPeriodEnd: true verstuurd is het een herhaling en telt het als gedaan.
Verzoek
curl -X PATCH https://volai.cz/v1/subscription \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"tier": "490"}'Antwoord
{
"subscription": {
"tier": "990",
"status": "active",
"periodStart": "2026-10-17T08:00:00.000Z",
"periodEnd": "2026-11-17T09:00:00.000Z",
"monthlyCreditHal": 130000,
"monthlyRemainingHal": 98500,
"exhaustionMode": "topup",
"cancelAtPeriodEnd": false,
"nextTier": "490",
"card": {
"brand": "visa",
"last4": "4242"
}
}
}Foutcodes
- 400
validationDe body is leeg, bevat een onbekend veld of een waarde buiten de aangeboden waarden, of vraagt in één verzoek om meer dan één wijziging die bevestiging nodig heeft. Er is niets gewijzigd. - 400
invalid_tierHet account zit al op dit niveau en er is geen ander niveau gepland, dus er valt niets te wijzigen. Een niveau waar het al naartoe overgaat vanaf de volgende periode is geen fout: het telt als gedaan. - 400
invalid_stateEentier-wijziging bij een abonnement dat aan het einde van de periode eindigt: er begint geen volgende periode, dus het niveau zou nooit ingaan. Maak eerst de opzegging ongedaan (cancelAtPeriodEnd: false, wat een persoon in het portaal bevestigt). Een body die ookcancelAtPeriodEnd: truebevat is een herhaling en gaat door. - 404
subscription_missingHet account heeft geen maandabonnement - start er een metPOST /v1/subscription/checkout. - 409
subscription_canceledHet abonnement is al beëindigd, dus het kan niet meer worden gewijzigd, hervat of betaald. Start een nieuw metPOST /v1/subscription/checkout. - 409
subscription_confirmation_requiredDe wijziging verhoogt wat van de kaart mag worden afgeschreven, dus alleen een persoon kan haar bevestigen. Er is niets gewijzigd; de fout bevatconfirmUrl. - 503
ledger_unavailableHet saldo of de boekingsgeschiedenis kon nu niet worden gelezen - probeer het zo meteen opnieuw.
POST/v1/subscription/pay
Voor een abonnement waarvan de laatste verlenging mislukte (status: "past_due"): maakt een Stripe Checkout-link aan die het verlengingsbedrag eenmaal afschrijft en een nieuwe kaart opslaat; de nieuwe periode begint bij de betaling. Hier wordt niets afgeschreven, een MENS betaalt op de Stripe-pagina. Een abonnement dat niet achterstallig is, heeft niets te betalen en geeft invalid_state. Accepteert Idempotency-Key.
Verzoek
curl -X POST https://volai.cz/v1/subscription/pay \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Idempotency-Key: plan-pay-2026-11-18-01"Antwoord
{
"checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3d4"
}Foutcodes
- 404
subscription_missingHet account heeft geen maandabonnement - start er een metPOST /v1/subscription/checkout. - 409
subscription_canceledHet abonnement is al beëindigd, dus het kan niet meer worden gewijzigd, hervat of betaald. Start een nieuw metPOST /v1/subscription/checkout. - 400
invalid_stateDe laatste verlenging is niet mislukt (statusis nietpast_due), dus er valt niets handmatig te betalen. - 404
user_not_foundDe gebruiker die eigenaar is van deze API-sleutel ontbreekt in het account - een ongebruikelijke toestand; neem contact op met support. - 503
stripe_not_configuredBetalingen zijn uitgeschakeld voor deze volai-installatie - niet specifiek voor uw verzoek. - 502
stripe_errorStripe weigerde een betaalsessie aan te maken - probeer het zo meteen opnieuw. - 503
ledger_unavailableHet saldo of de boekingsgeschiedenis kon nu niet worden gelezen - probeer het zo meteen opnieuw.
POST/v1/subscription/card
Maakt een Stripe Checkout-link aan in de modus kaart instellen om de kaart te vervangen waarmee het abonnement wordt verlengd. Hier wordt niets afgeschreven, een MENS voert de nieuwe kaart in op de Stripe-pagina; die wordt daarna gebruikt voor het abonnement en voor opwaarderingen met de kaart, en de oude wordt ontkoppeld. Werkt bij elke abonnementsstatus behalve canceled. Accepteert Idempotency-Key.
Verzoek
curl -X POST https://volai.cz/v1/subscription/card \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Idempotency-Key: plan-card-2026-11-18-01"Antwoord
{
"checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3d4"
}Foutcodes
- 404
subscription_missingHet account heeft geen maandabonnement - start er een metPOST /v1/subscription/checkout. - 409
subscription_canceledHet abonnement is al beëindigd, dus het kan niet meer worden gewijzigd, hervat of betaald. Start een nieuw metPOST /v1/subscription/checkout. - 404
user_not_foundDe gebruiker die eigenaar is van deze API-sleutel ontbreekt in het account - een ongebruikelijke toestand; neem contact op met support. - 503
stripe_not_configuredBetalingen zijn uitgeschakeld voor deze volai-installatie - niet specifiek voor uw verzoek. - 502
stripe_errorStripe weigerde een betaalsessie aan te maken - probeer het zo meteen opnieuw. - 503
ledger_unavailableHet saldo of de boekingsgeschiedenis kon nu niet worden gelezen - probeer het zo meteen opnieuw.
Taken
Een taak belt met één agent een lijst ontvangers volgens vooraf ingestelde regels (budget, belvenster, aantal pogingen) - er wordt niets gebeld totdat u zelf start (POST .../start). Een volledige gids met de CSV-import, statussen en de globale doorvoer staat op /en/docs/tasks. Een agent met een nummer voor bellen en sms belt in een campagne alleen Tsjechische nummers (+420) en vanaf het nummer loopt slechts één gesprek tegelijk: een contact met een ander nummer gaat naar controle terwijl de taak de anderen blijft bellen, en een bezet nummer laat de taak kort wachten.
GET/v1/tasks
Zonder ids toont het tot de 100 meest recente taken. Met ids=id1,id2 leest het één tot vijf unieke taken van het account en geeft requestedIdsComplete: true, requestedIds en hasMore: false terug; onbekende of vreemde ID's geven allemaal dezelfde 404. Het geselecteerde antwoord is begrensd op 4 MiB; splits de ID's na task_inventory_payload_limit in kleinere groepen. Als één taak alleen al de limiet overschrijdt, bekijk hem dan met GET /v1/tasks/{id} of neem contact op met support; wijzig de onveranderlijke ontvangerslijst niet om de antwoordlimiet te omzeilen.
Verzoek
curl https://volai.cz/v1/tasks \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"tasks": [
{
"id": "task_9f3a2c1d",
"name": "Payment reminder - September",
"agentId": "ag_4e91a2f0",
"taskType": "custom",
"purpose": "standard",
"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": [] }
]
}
]
}Foutcodes
- 400
validationDe parameteridsmoet één tot vijf unieke geldige ID's van uitgaande taken bevatten en mag maar één keer voorkomen. - 404
task_not_foundEr bestaat geen taak met ditidop het account. - 413
task_inventory_payload_limitDe geselecteerde taakrecords overschrijden 4 MiB. Splits de ID's in kleinere groepen en probeer opnieuw; als één taak alleen al de limiet overschrijdt, bekijk hem dan met GET /v1/tasks/{id} of neem contact op met support. Wijzig de onveranderlijke ontvangerslijst niet om deze limiet te omzeilen.
GET/v1/outbound/dispatch-scopes/petcenter/registrations
Alleen voor de eigenaar: operationele terugleesactie voor de uitgaande dispatchscope van PetCenter. Zonder operationId geeft het de samenvatting van het huidige geregistreerde manifest; met een operationId geeft het het duurzame resultaat van die bewerking na een onzekere POST. Dit endpoint kan het lidmaatschap van taken niet wijzigen.
Verzoek
curl https://volai.cz/v1/outbound/dispatch-scopes/petcenter/registrations \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{"registration":null,"operation":null}Foutcodes
- 400
validationHet operatie-ID, de manifestdigest of de takenlijst is onjuist opgebouwd of botst met het geregistreerde manifest waaraan alleen kan worden toegevoegd.
POST/v1/outbound/dispatch-scopes/petcenter/registrations
Alleen voor de eigenaar: operationele registratie voor de dispatcher van PetCenter die elke minuut draait. Stuur een stabiele operationId, de SHA-256 van het bronmanifest en geordende taskIds. De server controleert het eigendom van account en agent; het eerste manifest moet overeenkomen met de vastgepinde lijst van 19 taken, latere manifesten mogen alleen taken toevoegen. Om uitsluitend de digest uitdrukkelijk te vernieuwen, stuurt u ook expectedManifestSha uit de huidige GET mee en behoudt u de volledige geordende takenlijst. Compare-and-swap weigert een verouderde digest; dit veld kan geen taken toevoegen, verwijderen of herordenen. De voorwaarde maakt deel uit van de onveranderlijke bewerkingsbody. Een operatie-ID met dezelfde body hergebruiken is veilig; de body wijzigen geeft een conflict. Dit wijzigt alleen het lidmaatschap van de planner en start of hervat nooit een taak.
Verzoek
curl https://volai.cz/v1/outbound/dispatch-scopes/petcenter/registrations \
-H "Authorization: Bearer vk_YOUR_KEY" -H "Content-Type: application/json" \
--data-binary @petcenter-manifest-operation.jsonAntwoord
{"registration":{"manifestSha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","taskCount":19,"updatedAt":1791802800000},"operation":{"operationId":"registration-2026-10-12","manifestSha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","taskCount":19,"acceptedAt":1791802800000,"replayed":false}}Foutcodes
- 400
validationHet operatie-ID, de manifestdigest of de takenlijst is onjuist opgebouwd of botst met het geregistreerde manifest waaraan alleen kan worden toegevoegd. - 409
revision_conflictHet operatie-ID is al aan een andere body gebonden, het manifest is gewijzigd, of een taakrevisie is verouderd. Lees de bewerking en de huidige taken voordat u een nieuwe bewerking start. Alleen een registratieconflict meterror.details.reason: task_changed_during_registrationbevat ookproviderActionStarted: falseen de oorspronkelijkeoperationId: de controles stopten voordat het register veranderde. Lees vóór een nieuwe poging de registratie met GET en dezelfdeoperationId, bevestigoperation: nullen vernieuw de taken. Een timeout of een ander conflict stelt deze veilige herhaalvoorwaarde niet vast.
GET/v1/outbound/dispatch-scopes/petcenter/pauses
Alleen voor de eigenaar: duurzame terugleesactie voor een batchpauze van PetCenter. Stuur de operationId ervan; het antwoord verpakt de bewerking onder operation en bevat de exacte manifestSha, de canonieke requestHash, de geordende taak-ID's en revisies, en de vastgelegde uitkomst per taak. requestHash is de SHA-256 van compacte UTF-8-JSON met lexicografisch gesorteerde objectsleutels voor {manifestSha,tasks} (de volgorde van de takenarray blijft behouden); operationId is uitgesloten. Gebruik dit na elke onzekere POST voordat u een nieuwe bewerking aanmaakt.
Verzoek
curl 'https://volai.cz/v1/outbound/dispatch-scopes/petcenter/pauses?operationId=pause-batch-2026-10-12-01' \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"operation": {
"operationId": "pause-batch-2026-10-12-01",
"manifestSha": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"requestHash": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"tasks": [{ "taskId": "task_abcdefgh", "revision": 12 }],
"acceptedAt": 1791802800000,
"complete": true,
"outcomes": [{ "taskId": "task_abcdefgh", "outcome": "paused", "revision": 13 }]
}
}Foutcodes
- 400
validationHet operatie-ID, de manifestdigest of de invoer van één tot vijf unieke taak-ID's/revisies is onjuist opgebouwd.
POST/v1/outbound/dispatch-scopes/petcenter/pauses
Alleen voor de eigenaar: pauzebewerking voor één tot vijf momenteel geregistreerde PetCenter-taken. Stuur een stabiele operationId, de huidige manifest-SHA en de vers gelezen revisie van elke taak. Het antwoord verpakt de bewerking onder operation, inclusief de canonieke requesthash en de geordende taakinvoer. De revisiewijziging van de taak en de duurzame uitkomst per taak zijn atomair; dezelfde bewerking opnieuw afspelen is veilig, de body wijzigen geeft een conflict. Een revisieconflict wordt per taak gemeld en pauzeert die taak niet.
Verzoek
curl https://volai.cz/v1/outbound/dispatch-scopes/petcenter/pauses \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"operationId":"pause-batch-2026-10-12-01","manifestSha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","tasks":[{"taskId":"task_abcdefgh","revision":12}]}'Antwoord
{
"operation": {
"operationId": "pause-batch-2026-10-12-01",
"manifestSha": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"requestHash": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"tasks": [{ "taskId": "task_abcdefgh", "revision": 12 }],
"acceptedAt": 1791802800000,
"complete": true,
"outcomes": [{ "taskId": "task_abcdefgh", "outcome": "paused", "revision": 13 }]
}
}Foutcodes
- 400
validationHet operatie-ID, de manifestdigest of de invoer van één tot vijf unieke taak-ID's/revisies is onjuist opgebouwd. - 409
revision_conflictHet operatie-ID is al aan een andere body gebonden, het manifest is gewijzigd, of een taakrevisie is verouderd. Lees de bewerking en de huidige taken voordat u een nieuwe bewerking start. Alleen een registratieconflict meterror.details.reason: task_changed_during_registrationbevat ookproviderActionStarted: falseen de oorspronkelijkeoperationId: de controles stopten voordat het register veranderde. Lees vóór een nieuwe poging de registratie met GET en dezelfdeoperationId, bevestigoperation: nullen vernieuw de taken. Een timeout of een ander conflict stelt deze veilige herhaalvoorwaarde niet vast.
POST/v1/tasks
Maakt een taak aan met status draft - er wordt niets gebeld. agentId mag elke actieve agent zijn, ook een op ElevenLabs - de engine-eis (provider: "engine", bij het aanmaken gekozen via useForOutboundTasks: true) wordt pas gecontroleerd bij POST .../start, dat dan mislukt met engine_required. Maximaal 1000 recipients. Zonder retryPolicy is maxAttempts 1-3 en standaard 1; met de optionele retryPolicy: "lorela_enterprise" is het 1-4 en standaard 4. Optionele expiresAt (positieve veilige gehele Unix-milliseconden) voorkomt nieuwe gesprekken op of na de campagnedeadline, ook het eerste. Optionele retryMaxAgeDays (een geheel getal 1-30, standaard 5; alleen met de policy, in hetzelfde verzoek) stelt de herhaalhorizon in: N × 24 uur vanaf de eerste getelde poging van elke ontvanger. Policy, horizon en deadline staan in het taakdetail. Accepteert Idempotency-Key. Het optionele onveranderlijke purpose is standaard standard. coupon_reminder vereist source: manual, taskType: custom, expliciete maxAttempts: 1 en expiresAt. Vóór het bellen controleert een vertrouwde servergate de exacte herinnering; gebruikersvariabelen kunnen de begroeting of de coupon niet overschrijven. Zonder gate-configuratie wordt de herinnering niet gebeld.
Verzoek
curl -X POST https://volai.cz/v1/tasks \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: task-2026-09-08-01" \
-d '{
"name": "Payment reminder - September",
"agentId": "ag_4e91a2f0",
"taskType": "custom",
"purpose": "standard",
"source": "manual",
"maxAttempts": 2,
"budgetHal": 50000,
"recipients": [
{ "phone": "+420777123456", "variables": { "jmeno": "Jana Novakova" } }
]
}'Antwoord
{
"task": {
"id": "task_9f3a2c1d",
"name": "Payment reminder - September",
"agentId": "ag_4e91a2f0",
"taskType": "custom",
"purpose": "standard",
"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": [] }
]
}
}Foutcodes
- 400
validationDe body komt niet overeen met het schema (een ontbrekend veld, verkeerd type, meer dan 1000 ontvangers of een taak groter dan 2 MB, een ongeldig telefoonnummer bij een van de ontvangers,retryMaxAgeDaysbuiten 1-30 of verstuurd zonderretryPolicy: "lorela_enterprise"...). - 404
agent_not_foundEr bestaat geen agent met ditagentIdop het account, of hij is niet actief. - 400
budget_too_lowbudgetHaldekt niet eens één poging bij de maximale gespreksduur (maxDurationSecs).
GET/v1/tasks/{id}
Taakdetail, INCLUSIEF issues - een droogloop van wat de start van de taak momenteel blokkeert (vers berekend, er wordt niets opgeslagen). Herinneringsintegraties kunnen ook GET /v1/tasks/{id}/items/{itemId}/reminder-call?callId=c_... gebruiken om de exacte, aan de eigenaar gebonden feitelijke poging en de gesaneerde kwaliteit in reminderCall te lezen. Ontbrekende metrics zijn observed: false met null, nooit nul. Elke aangetoonde dismissReminder.invoked: true onderdrukt sms.
Verzoek
curl https://volai.cz/v1/tasks/task_9f3a2c1d \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"task": {
"id": "task_9f3a2c1d",
"name": "Payment reminder - September",
"agentId": "ag_4e91a2f0",
"taskType": "custom",
"purpose": "standard",
"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": []
}
}Foutcodes
- 404
task_not_foundEr bestaat geen taak met ditidop het account.
PATCH/v1/tasks/{id}
Werkt de regels van een bewerkbare taak bij (name, recipients, callingWindow, maxAttempts, retryPolicy, retryMaxAgeDays, expiresAt, maxDurationSecs, budgetHal) - agentId, taskType en source kunnen na het aanmaken niet worden gewijzigd. revision is VERPLICHT (optimistische gelijktijdigheid). De policy kan alleen vóór de eerste poging worden ingeschakeld; dezelfde policy kan later opnieuw worden verstuurd. Weggelaten maxAttempts, retryPolicy, retryMaxAgeDays en expiresAt blijven ongewijzigd. maxAttempts: 4 vereist de policy in dit verzoek of al opgeslagen bij de taak. retryMaxAgeDays (1-30, standaard 5; de horizon eindigt N × 24 uur na de eerste getelde poging van elke ontvanger) vereist ook de policy en mag, anders dan de policy, wijzigen nadat pogingen zijn gestart: verhogen zet ontvangers die alleen vanwege de herhaalhorizon waren overgeslagen (reviewReason: "retry_age_expired") terug naar failed (wachtend op hun volgende poging) met een nieuwe nextAttemptAt, wanneer ze nog een poging over hebben en het volgende slot van het schema binnen de nieuwe horizon en vóór expiresAt valt; verlagen heropent niets. Alleen een PATCH die de horizon verhoogt heropent ontvangers, dus stuur retryMaxAgeDays samen met een verlengde expiresAt in één verzoek. Een taak die running of completed is, kan niet worden bewerkt, pauzeer een lopende taak dus eerst.
Verzoek
curl -X PATCH https://volai.cz/v1/tasks/task_9f3a2c1d \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"revision": 0, "budgetHal": 80000}'Antwoord
{
"task": {
"id": "task_9f3a2c1d",
"name": "Payment reminder - September",
"agentId": "ag_4e91a2f0",
"taskType": "custom",
"purpose": "standard",
"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": [] }
]
}
}Foutcodes
- 400
validationDe body komt niet overeen met het schema (bijvoorbeeldretryMaxAgeDaysbuiten 1-30 of zonder de policylorela_enterprise), ofrevisionontbreekt. - 404
task_not_foundEr bestaat geen taak met ditidop het account. - 409
revision_conflictDe taak is intussen gewijzigd - lees hem opnieuw (GET) en herhaal met de huidigerevision. - 409
task_not_editableDe taak isrunning/completed, ofneeds_attentionzonder herstelmogelijkheid - zijn regels kunnen nu niet worden gewijzigd. - 409
items_immutableDe ontvangerslijst (recipients) kan niet worden vervangen zodra er minstens één belpoging heeft plaatsgevonden of een item een gecontroleerde overslaanresolutie heeft (na een aankoop of een uitschrijving).
POST/v1/tasks/{id}/start
Start (of hervat na een pauze) het bellen van ontvangers - ELK beantwoord gesprek wordt direct in rekening gebracht, het budget wordt vóór elke poging gereserveerd. Accepteert Idempotency-Key.
Verzoek
curl -X POST https://volai.cz/v1/tasks/task_9f3a2c1d/start \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: task-start-2026-09-08-01" \
-d '{"revision": 1}'Antwoord
{
"task": {
"id": "task_9f3a2c1d",
"name": "Payment reminder - September",
"agentId": "ag_4e91a2f0",
"taskType": "custom",
"purpose": "standard",
"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": [] }
]
}
}Foutcodes
- 400
validationDe body komt niet overeen met het schema,revisionontbreekt, ofexpiresAtis al verstreken. - 404
task_not_foundEr bestaat geen taak met ditidop het account. - 409
revision_conflictDe taak is intussen gewijzigd - lees hem opnieuw (GET) en herhaal met de huidigerevision. - 409
task_not_startableDe taak heeft niet de statusdraft/ready/paused, of heeft geen ontvanger meer om te bellen. - 409
task_needs_attentionVóór de volgende start is eenPOST .../reconcilenodig - er is iets veranderd terwijl de taak liep. - 404
agent_not_foundEr bestaat geen agent met ditagentIdop het account, of hij is niet actief. - 403
agent_suspendedDe volai-beheerder heeft de agent van de taak handmatig opgeschort - de taak kan niet worden gestart, of na een pauze opnieuw gestart, totdat de opschorting is opgeheven. Een opgeschort account geeft in plaats daarvanaccount_suspended. - 409
engine_requiredDe agent van de taak draait niet op de volai-engine - uitgaande taken vereisen momenteel de engine, niet ElevenLabs. - 409
agent_not_readyHet concept van de agent is niet gepubliceerd of de gepubliceerde versie is niet getest - publiceer het (POST /v1/agents/{id}/draft/publish) en simuleer daarna de gepubliceerde versie (POST /v1/agents/{id}/simulate) voordat u start. - 503
engine_contract_unavailableDe engine meldt momenteel geen ondersteuning voor variabelen en limieten van uitgaande taken - probeer later opnieuw te starten. - 409
window_closedBuiten het ingestelde belvenster (callingWindow) - wacht tot het weer opent, of wijzig het viaPATCH.
POST/v1/tasks/{id}/pause
Stopt verder bellen VÓÓR de volgende poging; een gesprek dat al bezig is wordt niet onderbroken. Geen Idempotency-Key - herhalen met dezelfde revisie is op zichzelf veilig.
Verzoek
curl -X POST https://volai.cz/v1/tasks/task_9f3a2c1d/pause \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"revision": 2}'Antwoord
{
"task": {
"id": "task_9f3a2c1d",
"status": "paused",
"revision": 3,
"budgetHal": 80000,
"reservedBudgetHal": 0,
"spentHal": 684,
"name": "Payment reminder - September",
"agentId": "ag_4e91a2f0",
"taskType": "custom",
"purpose": "standard",
"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 }
]
}
}Foutcodes
- 400
validationDe body komt niet overeen met het schema, ofrevisionontbreekt. - 404
task_not_foundEr bestaat geen taak met ditidop het account. - 409
task_not_runningPauzeren werkt alleen bij een lopende (running) taak. - 409
revision_conflictDe taak is intussen gewijzigd - lees hem opnieuw (GET) en herhaal met de huidigerevision.
GET/v1/tasks/{id}/items/{itemId}/reminder-call
Leest het bewijs van een feitelijk getelde poging van een taak met purpose: coupon_reminder. Geef de verplichte queryparameter callId=c_... precies één keer mee. De server controleert de exacte binding van account, taak, item, gesprek en herinneringsautorisatie. reminderCall bevat de telefoonhash, status, prijs en gesaneerde kwaliteit; het geeft geen telefoonnummers, transcripties of toolargumenten terug. Ontbrekende metrics hebben observed: false en count: null, nooit nul. dismissReminder.invoked: true of optOutRequested: true onderdrukt sms, zelfs als de tool mislukt of een timeout geeft. Alleen een bewezen gesloten poging zonder antwoord met duur en prijs nul, zonder tegenstrijdig gespreksbewijs, kan vaststellen dat afwijzing en opt-out false zijn; ontbrekende metrics over beurten/tools blijven onbekend. Controleer de gespreksstatus voordat u een vervolgactie onderneemt. Dit endpoint leest alleen bewijs en verstuurt geen sms.
Verzoek
curl "https://volai.cz/v1/tasks/task_reminder_9f3a/items/item_1a2b3c/reminder-call?callId=c_reminder_7e2d" \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"reminderCall": {
"callId": "c_reminder_7e2d",
"tenantId": "u_4a8d2c",
"agentId": "ag_4e91a2f0",
"taskId": "task_reminder_9f3a",
"taskItemId": "item_1a2b3c",
"dispatchIntentId": "dispatch_8d2a4f",
"purpose": "coupon_reminder",
"phoneSha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"direction": "out",
"status": "no_answer",
"startedAt": 1791882000000,
"durationSecs": 0,
"priceHal": 0,
"endReason": "no_answer",
"optOutRequested": false,
"hasConversation": false,
"quality": {
"turnFailures": { "observed": false, "count": null },
"toolFailures": { "observed": false, "count": null },
"dismissReminder": { "observed": true, "invoked": false }
}
}
}Foutcodes
- 400
validationGeef precies één geldige queryparametercallIdin de vormc_...mee, zonder andere parameters. - 404
task_not_foundHet exacte, aan de eigenaar gebonden getelde herinneringsgesprek is niet gevonden; vreemde ID's, een ander doel en ongeldige bindingen geven hetzelfde antwoord.
POST/v1/tasks/{id}/items/{itemId}/resolve
Een aparte variant na een geverifieerde aankoop of nadat de klant zich heeft uitgeschreven accepteert action: skip, reason (customer_purchased = de klant heeft gekocht, unsubscribed = de klant heeft zich uitgeschreven), revision, operationId en evidenceRef (ondoorzichtige identifiers van elk maximaal 128 tekens; evidenceRef verwijst naar het aankoop- of uitschrijvingsrecord en bevat geen telefoonnummer, e-mailadres of naam), bijvoorbeeld {"action":"skip","revision":12,"reason":"unsubscribed","operationId":"unsub-001","evidenceRef":"unsub-record-001"}. Het slaat alleen een inactief pending-item in een draft-taak over, of pending/failed in een paused-taak zonder onafgehandelde gesprekken of reserveringen. Geschiedenis, kosten en DNC blijven behouden: het nummer wordt niet aan de bel-mij-niet-lijst toegevoegd, ook niet bij unsubscribed. GET geeft het onveranderlijke bewijs items[].resolution terug waarvan reason customer_purchased of unsubscribed is. Speel dezelfde bewerking na een onzeker antwoord opnieuw af zonder nogmaals te schrijven; een andere resolutie op een al afgehandeld item (ook een andere reason met dezelfde operationId) geeft 409 revision_conflict. Oorspronkelijk gedrag zonder reason: beslist over een contact dat op controle wacht (items[].status: "review"): normaal zet retry het terug in de wachtrij zonder een nieuwe poging te verbruiken en sluit skip het af als overgeslagen. Met retryPolicy: "lorela_enterprise" geeft handmatig retry validation; alleen geplande automatische pogingen zijn toegestaan. Een contact in review kan verschijnen terwijl de taak nog loopt, maar resolve werkt alleen zolang de taak niet loopt - bij een lopende taak geeft het 409 task_not_editable, wacht dus op needs_attention of pauzeer de taak (pause). Heft bij reviewReason: "dial:destination_auto_blocked" eerst de blokkade op (POST /v1/dnc/{e164}/unblock) en pas daarna retry; bij dial:cannot_call_own_number, dial:cannot_call_volai_number, dial:invalid_number, dial:unsupported_country en dial:blocked_destination helpt alleen skip. reviewReason: "billing_unknown" is anders: de providerCallId en het gereserveerde budget blijven gekoppeld tot POST .../reconcile; na retry wacht de vervangende poging tot het oorspronkelijke gesprek is afgerekend, en na skip wordt geen vervangend gesprek gevoerd maar kan het oorspronkelijke gesprek later nog worden afgeschreven (de reservering ervan blijft meetellen voor het budget). Na retry blijft de taak dus in needs_attention totdat de prijs is afgerekend; na skip gaat ze naar paused en kan worden gestart wanneer er werk overblijft, anders wacht ze in needs_attention en wordt voltooid zodra de prijs is afgerekend. Een gesprek dat nooit is gebeld wordt direct op 0 afgerekend. Bij andere controleredenen verplaatst het afhandelen van het laatste contact de taak van needs_attention naar paused (start hervat) of completed.
Verzoek
curl -X POST https://volai.cz/v1/tasks/task_9f3a2c1d/items/item_1a2b3c/resolve \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"action": "retry", "revision": 5}'Antwoord
{
"task": {
"id": "task_9f3a2c1d",
"name": "Payment reminder - September",
"agentId": "ag_4e91a2f0",
"taskType": "custom",
"purpose": "standard",
"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
}
]
}
}Foutcodes
- 400
validationDe body komt niet overeen met het schema (actionisretryofskip,revisioneen niet-negatief geheel getal, een optionelereasoniscustomer_purchasedofunsubscribeden vereistoperationIdenevidenceRef), ofitemIdbestaat niet in de taak, of de policylorela_enterpriseweigert handmatigretry. - 404
task_not_foundEr bestaat geen taak met ditidop het account. - 409
task_not_editableDe taak heeft geen toegestane status. Overslaan na een aankoop of uitschrijving vereist draft of paused; pauzeer een lopende taak en reken actieve gesprekken eerst af. - 409
item_not_resolvableHet item komt niet in aanmerking voor deze afhandeling. De oorspronkelijke variant vereist controle; overslaan metreason(customer_purchasedofunsubscribed) vereist een inactief pending conceptitem of een pending/failed item in een gepauzeerde taak zonder onafgehandelde gesprekken. - 409
revision_conflictDe taak is intussen gewijzigd - lees hem opnieuw (GET) en herhaal met de huidigerevision.
POST/v1/tasks/{id}/reconcile
Stemt de toestand van de taak af op de feitelijke gesprekken na een onderbreking - op elk moment veilig aan te roepen, ook herhaaldelijk. ANDERS dan start/pause is revision OPTIONEEL. Het handelt een taak af die door een van vier oorzaken stopte: een pauze midden in een belpoging (dispatch_in_flight), een verlopen claim (dispatch_claim_expired), een gesprek waarvan de prijs nog niet bekend was (billing_unknown) of een onzekere belpoging (dial:*) waarvan het gesprek werd gevonden. Zodra er geen gesprek meer op afhandeling wacht, wordt zo'n taak paused zolang een contact nog op een poging wacht (ook als anderen in review staan), needs_attention wanneer alleen contacten in review overblijven, en completed wanneer er niets overblijft. Daarnaast geeft het een belintentie vrij waarvan de worker stierf voordat het gesprek werd geplaatst (er bestaat geen gespreksrecord en geen vastgelegde poging) zodra zowel de werkclaim als een marge van 5 minuten zijn verlopen, dus op zijn vroegst 15 minuten nadat de belpoging begon: het contact gaat terug naar wachten (of wordt completed na een geverifieerde terugbelactie) en de budgetreservering wordt teruggegeven. Verder verandert er niets: een running taak blijft lopen, en een taak die om een andere reden stopte (variables_limit, een mislukte factuur- of agendacontrole, een onzekere belpoging waarvan het gesprek niet te vinden is, of contacten in review die haar eerder stopten) blijft in needs_attention totdat u over die contacten beslist met resolve.
Verzoek
curl -X POST https://volai.cz/v1/tasks/task_9f3a2c1d/reconcile \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{}'Antwoord
{
"task": {
"id": "task_9f3a2c1d",
"status": "completed",
"revision": 3,
"budgetHal": 80000,
"reservedBudgetHal": 0,
"spentHal": 684,
"name": "Payment reminder - September",
"agentId": "ag_4e91a2f0",
"taskType": "custom",
"purpose": "standard",
"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 }
]
}
}Foutcodes
- 400
validationDe body komt niet overeen met het schema (revisionmoet, indien opgegeven, een niet-negatief geheel getal zijn). - 404
task_not_foundEr bestaat geen taak met ditidop het account. - 409
revision_conflictDe taak is intussen gewijzigd - lees hem opnieuw (GET) en herhaal met de huidigerevision.
POST/v1/tasks/{id}/process
Stemt de feitelijke gesprekken af en probeert hoogstens één verschuldigde ontvanger van een al lopende taak te verwerken. Vereist de huidige revision; het start of hervat nooit een taak. Het gebruikt hetzelfde belvenster, dezelfde onderdrukking, budgetreservering, tegoed, capaciteit en atomaire claim als cron. De opgeslagen opt-in dispatchOrder: "first_attempts_first" geeft voorrang aan verschuldigde ontvangers zonder getelde poging; afwijzingen vóór het bellen (countsAsAttempt: false) behouden die voorrang. De bestaande itemvolgorde blijft de standaard, en toekomstige nextAttemptAt-waarden worden gerespecteerd. Lees na een onzeker resultaat de huidige toestand in plaats van een verzoek blindelings te herhalen. Elke nieuwe logische tik heeft een nieuwe Idempotency-Key nodig, ook als de revisie niet is veranderd.
Verzoek
curl -X POST https://volai.cz/v1/tasks/task_9f3a2c1d/process \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: task-process-2026-10-08-tick-001" \
-d '{"revision": 2}'Antwoord
{
"status": "idle",
"task": {
"id": "task_9f3a2c1d",
"name": "Payment reminder - September",
"agentId": "ag_4e91a2f0",
"taskType": "custom",
"purpose": "standard",
"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 }
]
}
}Foutcodes
- 400
validationDe body komt niet overeen met het schema, ofrevisionontbreekt. - 404
task_not_foundEr bestaat geen taak met ditidop het account. - 409
revision_conflictDe taak is intussen gewijzigd - lees hem opnieuw (GET) en herhaal met de huidigerevision. - 409
task_not_runningDe taak loopt niet of werd gepauzeerd voor het bellen. Lees de huidige toestand; process hervat haar nooit. - 409
task_pausedDe taak loopt niet of werd gepauzeerd voor het bellen. Lees de huidige toestand; process hervat haar nooit. - 409
claim_lostHet resultaat van de dispatch kan niet veilig worden bevestigd. Lees of stem de taak af en bekijk het gesprek in plaats van het verzoek blindelings te herhalen. - 500
dispatch_result_unpersistedHet resultaat van de dispatch kan niet veilig worden bevestigd. Lees of stem de taak af en bekijk het gesprek in plaats van het verzoek blindelings te herhalen. - 409
agent_version_changedDe gepubliceerde agentversie is gewijzigd. Bekijk en test hem opnieuw voordat u de taak herstart. - 403
agent_suspendedDe volai-beheerder heeft de agent van de taak handmatig opgeschort - de taak kan niet worden gestart, of na een pauze opnieuw gestart, totdat de opschorting is opgeheven. Een opgeschort account geeft in plaats daarvanaccount_suspended. - 503
engine_contract_unavailableDe engine meldt momenteel geen ondersteuning voor variabelen en limieten van uitgaande taken - probeer later opnieuw te starten. - 409
window_closedBuiten het ingestelde belvenster (callingWindow) - wacht tot het weer opent, of wijzig het viaPATCH.
DELETE/v1/tasks/{id}
Verwijdert de taak en al haar items - alleen bij status draft/ready/paused/completed/needs_attention, ZONDER item in_progress. revision wordt gelezen uit ?revision= OF uit een JSON-body {"revision": ...} (de queryparameter wint).
Verzoek
curl -X DELETE "https://volai.cz/v1/tasks/task_9f3a2c1d?revision=3" \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"deleted": true
}Foutcodes
- 400
validationrevisionontbreekt, of is geen niet-negatief geheel getal (in de query of in de body). - 404
task_not_foundEr bestaat geen taak met ditidop het account. - 409
revision_conflictDe taak is intussen gewijzigd - lees hem opnieuw (GET) en herhaal met de huidigerevision. - 409
task_not_deletableDe taak heeft een itemin_progress, of isrunning- wacht tot het klaar is, of pauzeer haar eerst.
POST/v1/tasks/csv-preview
Parseert en valideert een CSV met ontvangers VOORDAT een taak wordt aangemaakt of bijgewerkt - er wordt niets opgeslagen, fouten per rij komen in de array errors van een 200-antwoord, niet als HTTP-fout.
Verzoek
curl -X POST https://volai.cz/v1/tasks/csv-preview \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"csv": "phone,jmeno\n+420777123456,Jana Novakova\nneplatne,Petr Svoboda\n"}'Antwoord
{
"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 }]
}Foutcodes
- 400
validationHet veldcsvvan de body ontbreekt of is geen string, ofcsvoverschrijdt 1.000.000 tekens.
Nummers
GET/v1/numbers/{e164}/callback-routing
Leest het opt-in terugbelbeleid van een eigen nummer. De standaardroutering van het nummer blijft ongewijzigd.
Verzoek
curl "https://volai.cz/v1/numbers/+420601234567/callback-routing" \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{"callbackRouting":{"agentIds":["ag_campaign"],"lookbackSecs":604800}}Foutcodes
- 400
validatione164 in de URL is geen geldig telefoonnummer. - 404
not_foundHet nummer bestaat niet of hoort niet bij uw account.
PUT/v1/numbers/{e164}/callback-routing
Slaat een expliciete toegestane lijst op van eigen actieve engine-agents voor terugbellende bellers met een uniek feitelijk uitgaand gesprek vanaf dit nummer binnen lookbackSecs (60-604800 seconden). Onbekende, dubbelzinnige of niet-geverifieerde bellers houden de standaardagent. Lege agentIds schakelt het beleid uit. Optionele firstMessage geldt alleen voor geverifieerde terugbelacties. Optionele missedFirstMessage (tot 500 tekens) vervangt firstMessage voor een beller wiens voorafgaande uitgaande gesprek geen conversatie had (niet beantwoord, voicemail, spraakmenu, niemand sprak) - die beller heeft het aanbod nog niet gehoord; zonder deze geldt firstMessage voor iedereen. De engine ontvangt bij een geverifieerde terugbelactie ook volai_callback_kind (missed of contacted). engineSyncRequired: true betekent dat de configuratie van de standaardagent in de engine apart webhooks.inbound_route_url met terugleesactie moet activeren; dit verzoek herschrijft de live-engineconfiguratie niet. Geverifieerde inkomende gesprekken tonen callbackOf.callId en optioneel door de server beheerde taskId en taskItemId in de gespreksdetails.
Verzoek
curl -X PUT "https://volai.cz/v1/numbers/+420601234567/callback-routing" \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"agentIds":["ag_campaign"],"lookbackSecs":604800}'Antwoord
{"callbackRouting":{"agentIds":["ag_campaign"],"lookbackSecs":604800},"engineSyncRequired":true}Foutcodes
- 400
validatione164 in de URL is geen geldig telefoonnummer. - 404
not_foundHet nummer bestaat niet of hoort niet bij uw account.
GET/v1/numbers
De spraaknummers van het account, elk met zijn huidige routering. Mobiele nummers staan apart in GET /v1/sms-numbers (MCP list_sms_numbers) en nummer voor bellen en smss in GET /v1/voice-sms-numbers (MCP list_voice_sms_numbers).
Verzoek
curl https://volai.cz/v1/numbers \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"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}
Het detail van één eigen nummer - dezelfde vorm als een item van de lijst GET /v1/numbers. Handig wanneer u alleen het E.164 kent (meestal een agent via MCP) en niet door de hele lijst wilt bladeren.
Verzoek
curl "https://volai.cz/v1/numbers/+420601234567" \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"number": {
"e164": "+420601234567",
"routing": { "mode": "agent", "agentId": "ag_kx91fa2b", "hasSipPassword": false, "sipTarget": "engine" },
"monthlyFeeHal": 2500,
"boughtAt": 1756111640000,
"region": "objednavka",
"regionLabel": "Czech number",
"billingMonths": 1
}
}Foutcodes
- 400
validatione164 in de URL is geen geldig telefoonnummer. - 404
not_foundHet nummer bestaat niet of hoort niet bij uw account.
POST/v1/numbers
Koopt een nummer. Een lege body {} wijst een willekeurig vrij nummer uit de huidige lijst toe, of stuur een specifiek { "e164": "..." } uit GET /v1/numbers/available. Kies een regio met { "region": "brno" } - toegestane waarden zijn praha, brno, internet, bratislava en de regionale aanbiedingen die momenteel in de verkoop zijn (vermeld in regions van GET /v1/numbers/available); een nummer uit een regio die niet in de verkoop is, wordt besteld via POST /v1/numbers/orders. Dit brengt maandelijks 25,00 kr (≈ 1,04 EUR) in rekening (voor een Slowaaks nummer, aanbod bratislava, is het 75,00 kr (≈ 3,13 EUR) vooraf - billingMonths maanden vooruit betaald, en hetzelfde bedrag wordt elke billingMonths maanden opnieuw afgeschreven), en de routering begint op none - stel die meteen in met PATCH.
Verzoek
curl -X POST https://volai.cz/v1/numbers \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{}'Antwoord
{
"number": {
"e164": "+420266266647",
"routing": { "mode": "none", "hasSipPassword": false },
"monthlyFeeHal": 2500,
"boughtAt": 1756111640000,
"region": "praha",
"regionLabel": "Prague",
"billingMonths": 1
}
}Foutcodes
- 400
validationDe request body komt niet overeen met het schema (e164/region langer dan 32 tekens, of het verkeerde type), ofregionis geen herkend aanbod-id. - 402
insufficient_creditUw tegoed dekt de maandelijkse kosten van 25,00 kr (≈ 1,04 EUR) niet. Voor een Slowaaks nummer (aanbodbratislava) moet het tegoedmonthlyFeeHal * billingMonthsvooraf dekken. Een account dat nooit heeft opgewaardeerd, mag met het welkomsttegoed één nummer houden (een openstaande bestelling telt mee); een ander nummer kan pas na de eerste opwaardering worden gekocht. Er wordt niets afgeschreven. - 402
subscription_cap_reachedHet account heeft een maandabonnement met het bestedingsplafond, het maandtegoed is op en het opgewaardeerde tegoed dekt de actie niet. Het vervangtinsufficient_creditvoor zo'n account (dezelfde HTTP-status). Er is niets afgeschreven. - 404
number_unavailableIemand anders heeft dit specifieke nummer al gekocht - kies een ander uit de huidige lijst (GET /v1/numbers/available). - 503
pool_emptyWe hebben geen nummers meer in de huidige lijst - bestel er een uit een andere regio (POST /v1/numbers/orders), of meld u aan voor de wachtlijst in het portaal.
GET/v1/numbers/waitlist
Status van de wachtlijst voor elk poolaanbod (praha/brno/internet/bratislava en de regionale aanbiedingen in de verkoop, dezelfde lijst als regions in GET /v1/numbers/available) - joined zegt of het account erop wacht, available of het nu op voorraad is.
Verzoek
curl https://volai.cz/v1/numbers/waitlist \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"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 }
]
}Foutcodes
- 404
user_not_foundDe gebruiker die eigenaar is van deze API-sleutel ontbreekt in het account - een ongebruikelijke toestand; neem contact op met support.
POST/v1/numbers/waitlist
Meldt u aan voor de wachtlijst van een uitverkocht aanbod (POST /v1/numbers mislukte met pool_empty) - het koopt geen nummer, het registreert alleen interesse. U ontvangt een e-mail zodra het aanbod weer op voorraad is. Een lege body {} gebruikt het standaardaanbod (praha).
Verzoek
curl -X POST https://volai.cz/v1/numbers/waitlist \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"offerId": "brno"}'Antwoord
{
"joined": true
}Foutcodes
- 400
validationofferIdis niet een van de aanbiedingen in de verkoop (praha/brno/internet/bratislavaen de regionale aanbiedingen inregions). - 404
user_not_foundDe gebruiker die eigenaar is van deze API-sleutel ontbreekt in het account - een ongebruikelijke toestand; neem contact op met support.
DELETE/v1/numbers/waitlist/{offerId}
Verlaat de wachtlijst voor ÉÉN aanbod (offerId in het pad) - wijzigt de wachtlijst voor andere aanbiedingen niet.
Verzoek
curl -X DELETE https://volai.cz/v1/numbers/waitlist/brno \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"left": true
}Foutcodes
- 400
validationofferIdin het pad is niet een van de aanbodwaarden. - 404
user_not_foundDe gebruiker die eigenaar is van deze API-sleutel ontbreekt in het account - een ongebruikelijke toestand; neem contact op met support.
GET/v1/numbers/available
De lijst met nummers die te koop zijn (maximaal 5 per regio). numbers bevat de nummers uit de gekozen regio (Praag als u de parameter weglaat), regions geeft altijd de hele lijst in één keer terug - inclusief bratislava (een Slowaaks nummer met andere kosten, zie POST /v1/numbers hierboven). Regionale aanbiedingen (jihocesky, karlovarsky, kralovehradecky, liberecky, moravskoslezsky, olomoucky, pardubicky, plzensky, stredocesky, ustecky, vysocina, zlinsky) verschijnen pas in regions zodra de regio in de verkoop is (volai heeft daar een adres voor alarmdiensten); tot die tijd wordt zo'n region als onbekend geweigerd en wordt een nummer uit die regio besteld via POST /v1/numbers/orders.
Verzoek
curl "https://volai.cz/v1/numbers/available?region=brno" \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"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"]
}
]
}Foutcodes
- 400
validationregionmoet een van de id's in het aanbod zijn (zie/en/pricing).
GET/v1/numbers/address-options
Adressen voor het bestellen van een nummer uit een regio buiten de lijst. Dit doorloopt de trapsgewijze adreslijst van de carrier: stuur psc en krijg gemeenten, voeg obec toe en krijg wijken, cobce geeft straten en ulice geeft huisnummers. Zodra cp is gekozen, komt er een leesbaar adres in het veld recap. Verzin geen codes - alleen de hier teruggegeven zijn geldig. Een snellere weg die de trap overslaat is query, zie het kader hieronder.
Verzoek
curl "https://volai.cz/v1/numbers/address-options?psc=70200&obec=554821&cobce=413950" \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"obce": [{ "value": "554821", "label": "Ostrava" }],
"casti": [{ "value": "413950", "label": "Moravská Ostrava" }],
"ulice": [{ "value": "353710", "label": "28. října" }],
"cp": [],
"recap": null
}Foutcodes
- 400
validationquery is langer dan 200 tekens, bevat geen postcode, of de carrier heeft geen passende optie voor het niveau waartoe het is opgelost. - 502
provisioning_failedAdressen konden helemaal niet bij de carrier worden geladen. - 503
orders_disabledBestellingen zijn tijdelijk gepauzeerd.
Een snellere weg: het hele adres in één query
Stuur in plaats van de trapsgewijze keuze query met het hele adres (bijv. "Nadrazni 100, 702 00 Ostrava") - de server doorloopt de trap voor u. Alleen de postcode wordt rechtstreeks uit de tekst gelezen; al het andere wordt alleen vergeleken met de opties die de carrier voor dat niveau teruggaf - de server verzint nooit een code.
curl -G https://volai.cz/v1/numbers/address-options \
-H "Authorization: Bearer vk_YOUR_KEY" \
--data-urlencode "query=Nádražní 100, 702 00 Ostrava"{
"obce": [],
"casti": [],
"ulice": [],
"cp": [],
"recap": "28. října 102/1, Ostrava, 70200",
"selection": {
"psc": "70200",
"obec": "554821",
"cobce": "413950",
"ulice": "353710",
"cp": "3180026"
}
}Als het adres dubbelzinnig is, wordt alleen het niveau teruggegeven waar dat duidelijk wordt (ambiguousLevel: obec/cobce/ulice/cp) met opties om uit te kiezen in het bijbehorende veld, bijvoorbeeld:
{
"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 bevat altijd wat al is opgelost - ga verder met dezelfde stapparameter (psc/obec/cobce/ulice/cp) die u zonder query zou gebruiken. Als het adres helemaal niet kan worden opgelost, of het tijdsbudget midden in het opzoeken opraakt, komt er in plaats van recap een leesbaar bericht in het veld message, samen met de tot dan toe opgeloste selection - ga vandaar verder met de stapparameters.
query en de stapparameters MOGEN NIET in één verzoek worden GEMENGD - stuurt u beide samen, dan geeft het endpoint validation. Herhaal dus na een dubbelzinnig antwoord niet dezelfde query met een toegevoegde obec (of ander niveau) uit de opties - stuur het volgende verzoek ZONDER query, met alleen stapparameters. De exacte vorm (inclusief al opgeloste waarden) staat uitgeschreven in het veld message van dat dubbelzinnige antwoord.
POST/v1/numbers/orders
Bestelt een nummer op het adres van de klant - wanneer die het daar geregistreerd wil hebben, of de regio is uitverkocht of valt buiten de lijst. Adrescodes moeten uit GET /v1/numbers/address-options komen. Het endpoint wacht terwijl we bij de carrier een adres voor alarmdiensten instellen en het nummer kopen - meestal minder dan een minuut; het klare nummer komt dan terug in het veld e164 met status done. Lukt het niet in één keer, dan geeft het de bestelling terug met status pending en maakt een cronjob haar af. De kosten van 25,00 kr (≈ 1,04 EUR) worden pas afgeschreven zodra het nummer klaar is.
Verzoek
curl -X POST https://volai.cz/v1/numbers/orders \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"psc":"70200","obec":"554821","cobce":"413950","ulice":"353710","cp":"3180026"}'Antwoord
{
"order": {
"id": "Xa3Kd9pQmR2v",
"status": "pending",
"address": "70200",
"createdAt": 1756111640000,
"updatedAt": 1756111640000
}
}Foutcodes
- 400
validationDe request body komt niet overeen met het schema - psc/obec/cobce/cp ontbreekt of is leeg, of een veld overschrijdt zijn lengtelimiet (ulice is optioneel). - 400
incomplete_addresspsc, obec, cobce of cp ontbreekt (ulice is optioneel - niet elk adres heeft er een). - 402
insufficient_creditUw tegoed dekt de maandelijkse kosten van 25,00 kr (≈ 1,04 EUR) niet. Een account dat nooit heeft opgewaardeerd, mag met het welkomsttegoed één nummer houden (een openstaande bestelling telt mee); een ander kan pas na de eerste opwaardering worden besteld. Er wordt niets afgeschreven. - 402
subscription_cap_reachedHet account heeft een maandabonnement met het bestedingsplafond, het maandtegoed is op en het opgewaardeerde tegoed dekt de actie niet. Het vervangtinsufficient_creditvoor zo'n account (dezelfde HTTP-status). Er is niets afgeschreven. - 503
orders_disabledBestellingen zijn tijdelijk gepauzeerd. - 409
idempotency_conflictDeze Idempotency-Key is al gebruikt voor een ander bestelnadres.
GET/v1/numbers/orders
Status van de bestelling: pending (in de wachtrij), provisioning (wordt nu ingesteld), done (het nummer staat in het veld e164 en hoort bij het account), failed (de reden staat in het veld error, er is niets afgeschreven), cancelled (door de klant geannuleerd terwijl het nog in de wachtrij stond - er is niets afgeschreven).
Verzoek
curl https://volai.cz/v1/numbers/orders \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"orders": [
{
"id": "Xa3Kd9pQmR2v",
"status": "done",
"address": "28. října 102/1, Ostrava, 70200",
"e164": "+420596123456",
"createdAt": 1756111640000,
"updatedAt": 1756118840000
}
]
}POST/v1/numbers/orders/{id}/cancel
Annuleert een bestelling die nog in de wachtrij staat (pending) - geen body, geeft de bestelling terug met status cancelled. Er wordt niets afgeschreven (de kosten worden pas ingehouden zodra het nummer klaar is) en een account dat nooit heeft opgewaardeerd kan opnieuw bestellen. Een bestelling die nu wordt ingesteld (provisioning) kan niet worden geannuleerd - ze geeft 409 in_progress, probeer het over een paar minuten opnieuw; een done of failed bestelling ook niet (409 invalid_transition) - geef een klaar nummer in plaats daarvan vrij met DELETE /v1/numbers/{e164}. Een al geannuleerde bestelling annuleren is onschadelijk (ze wordt ongewijzigd teruggegeven).
Verzoek
curl -X POST https://volai.cz/v1/numbers/orders/Xa3Kd9pQmR2v/cancel \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"order": {
"id": "Xa3Kd9pQmR2v",
"status": "cancelled",
"address": "28. října 102/1, Ostrava, 70200",
"createdAt": 1756111640000,
"updatedAt": 1756112240000
}
}Foutcodes
- 404
not_foundDe bestelling bestaat niet of hoort niet bij uw account. - 409
in_progressDe bestelling wordt nu ingesteld (provisioning, of de cronjob pakt haar net op) - ze kan later alleen als klaar nummer worden vrijgegeven. Probeer het over een paar minuten opnieuw. - 409
invalid_transitionDe bestelling is voltooid (done- geef het nummer vrij metDELETE /v1/numbers/{e164}) of al mislukt (failed- er valt niets te annuleren).
DELETE/v1/numbers/{e164}
Geeft het nummer terug aan de pool - verbreekt de routering en elke agentregistratie. De al betaalde maand wordt niet terugbetaald.
Verzoek
curl -X DELETE "https://volai.cz/v1/numbers/+420601234567" \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"released": true
}Foutcodes
- 400
validatione164 in de URL is geen geldig telefoonnummer. - 404
not_foundHet nummer bestaat niet of hoort niet bij uw account. - 502
release_failedHet verbreken van de routering op het netwerk kon niet veilig worden bevestigd, dus het nummer is niet teruggegeven aan de pool. Probeer het zo meteen opnieuw, of neem contact op met support.
PATCH/v1/numbers/{e164}
Wijzigt de routering van het nummer.
mode: "agent"+agentId- de spraakagent neemt gesprekken aan.mode: "forward"+forwardTo(E.164) - doorschakelen - u betaalt voor beide gespreksbenen.mode: "sip"+sipUri- routeert naar uw eigen SIP-server (zie SIP) - optioneel met authenticatie via sipUsername en sipPassword, als de doelcentrale dat vereist. Een veld dat helemaal uit de PATCH-body is weggelaten, behoudt zijn opgeslagen waarde - voor beide velden hetzelfde, dus alleen sipUri wijzigen vereist niet dat u de opgeslagen gebruikersnaam of het wachtwoord opnieuw verstuurt. Uitzondering: als de wijziging van sipUri ook de server wijzigt (het deel na het @-teken), moet u het wachtwoord opnieuw versturen - we sturen nooit een opgeslagen wachtwoord naar een andere server dan die waarvoor u het hebt ingevoerd. Authenticatie wissen kan alleen expliciet - stuur sipUsername en sipPassword allebei als lege strings. Het antwoord bevat ook altijd hasSipPassword (optioneel, alleen-lezen) - true betekent dat het nummer een opgeslagen SIP-wachtwoord heeft; sipPassword zelf wordt nooit door de API teruggegeven.mode: "none"- gesprekken naar het nummer worden nergens naartoe gerouteerd en het netwerk weigert ze.
sipTarget in routing is een optioneel, informatief veld dat door volai wordt ingesteld - u verstuurt het nooit zelf. De waarde "engine" betekent dat de agent van het nummer op de eigen engine van volai draait. Elke andere waarde of een ontbrekend veld betekent dat dat niet zo is (meestal een agent op ElevenLabs) - beschouw een ontbrekend veld alleen niet als zeker bewijs van ElevenLabs; "engine" is het enige betrouwbare signaal.
Verzoek
curl -X PATCH "https://volai.cz/v1/numbers/+420601234567" \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"routing": {"mode": "agent", "agentId": "ag_kx91fa2b"}}'Antwoord
{
"number": {
"e164": "+420601234567",
"routing": { "mode": "agent", "agentId": "ag_kx91fa2b", "hasSipPassword": false, "sipTarget": "engine" },
"monthlyFeeHal": 2500,
"boughtAt": 1756111640000,
"region": "objednavka",
"regionLabel": "Czech number",
"billingMonths": 1
}
}Foutcodes
- 400
validationrouting.mode vereist het bijbehorende veld (agentId / forwardTo / sipUri). Bij routing.mode: "sip" gelden ook sipUsername en sipPassword: vul beide in of geen van beide, zonder dubbele punt, @-teken, witruimte, aanhalingsteken of backslash, elk maximaal 64 tekens. Als beide inloggegevens zijn ingevuld, mag het gebelde nummer in sipUri (vóór het @-teken) geen dubbele punt bevatten en mag de server in sipUri (na het @-teken) geen poort hebben - dat wordt samen met inloggegevens nog niet ondersteund. U krijgt deze code ook wanneer u de server in sipUri wijzigt bij een nummer met een opgeslagen wachtwoord zonder sipPassword opnieuw te versturen, of wanneer sipUri naar een Vapi BYO SIP-trunk wijst (<credentialId>.sip.vapi.ai, of .sip.eu.vapi.ai) terwijl inloggegevens worden verstuurd of opgeslagen - beide velden moeten daar leeg blijven, omdat Vapi onze gesprekken herkent aan het IP in de gateways. - 400
invalid_sip_urisipUri heeft niet de vorm sip:user@server (optioneel met een poort) - geldt alleen voor routing.mode: "sip". U krijgt dezelfde code wanneer de operator het opslaan van een GEAUTHENTICEERDE route (met zowel sipUsername als sipPassword ingevuld) definitief weigert, of een ander doel terugleest dan het doel dat we verstuurden - opnieuw proberen zonder wijzigingen helpt niet. Voor routering zonder inloggegevens wordt dezelfde situatie gemeld als routing_failed. - 402
insufficient_creditHet account heeft nooit opgewaardeerd en het nummer wordt nog niet doorgeschakeld - doorschakelen (routing.mode: "forward") kan pas na de eerste opwaardering worden ingeschakeld, omdat het achteraf wordt afgerekend op basis van de gegevens van de carrier zonder rem tijdens het gesprek. De agent, SIP en inkomende gesprekken blijven werken. Een nummer dat al wordt doorgeschakeld kan zonder opwaardering op een ander doel worden gericht. Er wordt niets afgeschreven. - 404
not_foundHet nummer bestaat niet of hoort niet bij uw account. - 502
routing_failedDe gevraagde routering werd zojuist niet bevestigd (doorschakelen, SIP, het verbreken ervan, of het bevestigen van een route naar de engine) - een tijdelijke netwerkstoring, probeer het zo meteen opnieuw. - 502
engine_unavailableBij routing.mode: "agent" voor een agent op de eigen engine van volai: de engine reageert nu niet, het toewijzen van het nummer is mislukt. Probeer het zo meteen opnieuw, of neem contact op met support. Dezelfde code wordt teruggegeven bij een schrijfactie die door het standby-knooppunt van de engine werd geweigerd (de engine draait momenteel op een standby-knooppunt dat geen configuratie accepteert): de melding zegt dat, de wijziging is niet opgeslagen en hetzelfde verzoek kan over een paar minuten worden herhaald. - 502
engine_readback_mismatchBij routing.mode: "agent" voor een agent op de eigen engine van volai: de engine antwoordde, maar het terugleggen toonde een andere koppeling dan de koppeling die we verstuurden. Probeer het opnieuw.
GET/v1/numbers/{e164}/sip
De SIP-gegevens van het nummer - voor uw eigen softphone/centrale en voor de uitgaande trunk van een spraakplatform (ElevenLabs, Vapi...). Een stapsgewijze installatie staat op de pagina SIP.
| Veld | Betekenis |
|---|---|
server | Registratiedomein voor uw eigen softphone of centrale (SIP REGISTER). Niet voor de uitgaande trunk van een platform - zie outboundTrunkAddress. |
outboundTrunkAddress | Adres voor de uitgaande SIP-trunk van uw platform (ElevenLabs outbound_trunk_config.address). Gebruik precies deze waarde, ongeacht of ze overeenkomt met server - een ander adres betekent dat het gesprek niet op het netwerk wordt gerouteerd en het platform een timeout meldt. |
username / password | Inloggegevens voor de lijn - dezelfde voor REGISTER en voor een trunk met digest-authenticatie. |
port / transport | De poort en het aanbevolen transport voor SIP-signalering - TCP (een grote INVITE met SDP past mogelijk niet in de UDP-MTU en het platform weigert hem dan helemaal te versturen); UDP werkt ook. |
inboundSignallingCidrs | Waar onze SIP-signalering naar uw centrale of platform vandaan komt - de allowlist voor uw inkomende trunk (bijv. ElevenLabs allowed_addresses, Vapi-gateways). |
server en outboundTrunkAddress zijn niet uitwisselbaar: server is voor REGISTER vanaf een softphone of centrale, outboundTrunkAddress is voor de uitgaande trunk van een platform - de trunk neemt altijd de waarde uit outboundTrunkAddress, of ze nu overeenkomt met server of niet; een ander adres betekent dat het gesprek niet op het netwerk wordt gerouteerd en het platform een timeout meldt (details op de SIP-pagina en in de gids voor eigen agents).
Verzoek
curl "https://volai.cz/v1/numbers/+420601234567/sip" \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"server": "sip.volai.cz",
"username": "123456",
"password": "a1b2c3d4e5f6",
"outboundTrunkAddress": "sip.volai.cz",
"port": 5060,
"transport": "tcp",
"inboundSignallingCidrs": ["81.31.45.0/24"]
}Foutcodes
- 400
validatione164 in de URL is geen geldig telefoonnummer. - 404
not_foundHet nummer bestaat niet of hoort niet bij uw account. - 502
sip_credentials_unavailableHet wachtwoord van de SIP-lijn kon niet worden gevonden. Probeer het zo meteen opnieuw, of neem contact op met support.
GET/v1/numbers/{e164}/sip/status
De ware SIP-registratiestatus van een nummer dat is gerouteerd naar uw eigen centrale en het laatste inkomende gesprek ervan - anders dan het veld routing.mode is dit een WERKELIJK geverifieerde status, niet een uit instellingen afgeleide. registration is null wanneer het nummer naar een VREEMDE centrale wijst (een sipa:-doel zonder challenge) - daar is alleen lastInbound zinvol.
Verzoek
curl "https://volai.cz/v1/numbers/+420601234567/sip/status" \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"target": { "kind": "own_line", "host": null },
"registration": { "registered": true },
"lastInbound": {
"at": 1756118900000,
"status": "completed",
"from": "+420777123456",
"recent": true
},
"serviceStopped": null
}Foutcodes
- 400
validatione164 in de URL is geen geldig telefoonnummer. - 404
not_foundHet nummer bestaat niet of hoort niet bij uw account. - 400
not_sip_modeHet nummer is niet gerouteerd naar "uw eigen centrale".
Berichten
Twee soorten nummers ontvangen sms
Spraaknummers van volai (vast en VoIP) kunnen geen sms ontvangen. Sms ontvangen en vanaf uw eigen nummer verzenden doet de add-on mobiel nummer (zie het onderdeel mobiele nummers hieronder): een Tsjechisch mobiel nummer alleen voor sms, zonder gesprekken, en het nummer voor bellen en sms (zie het onderdeel Nummer voor bellen en smss hieronder): een vast Praags nummer met gesprekken en sms. Ontvangen berichten leest u in GET /v1/messages?direction=in, in de MCP-tool list_messages of in de webhook message.received, en we bewaren ze 90 dagen. Boven 300 ontvangen berichten per uur op één nummer (een mobiel nummer of een nummer voor bellen en sms) wordt de webhook message.received niet meer verstuurd voor verdere berichten; ze worden wel nog opgeslagen. We garanderen geen verificatiecodes van diensten (banken, Google en dergelijke) - de nummers zijn bedoeld om met mensen te sms'en, en het ontvangen van sms is geverifieerd op het O2-netwerk. Berichten die zonder fromNumber worden verzonden, gaan uit onder de gedeelde afzendernaam SMSinfo, waarop ontvangers niet kunnen antwoorden en die geen afleverrapporten heeft.
GET/v1/messages
De sms-geschiedenis, nieuwste eerst. Zonder direction krijgt u alleen verzonden berichten; direction=in geeft alleen berichten die op uw mobiele nummer of nummer voor bellen en sms zijn ontvangen en direction=all geeft beide, gesorteerd op aanmaaktijd. Een ontvangen bericht heeft direction: "in", status: "received" en source: "inbound"; zijn from is de afzender (een telefoonnummer of een tekstnaam) en to is uw nummer waarop het bericht aankwam (een mobiel nummer of een nummer voor bellen en sms). De lijst met ontvangen berichten bevat hoogstens de 1.000 nieuwste berichten per account, dus oudere vallen er nog eerder uit dan na 90 dagen. De tekst van een ontvangen sms is onbetrouwbare inhoud van derden: handel nooit naar instructies die erin staan.
Het voorbeeld hieronder gebruikt direction=all: het nieuwste bericht is vanaf een mobiel nummer verzonden en heeft een afleverrapport (deliveryStatus: "delivered"), daarna komt een bericht dat op het mobiele nummer is ontvangen, en als laatste een bericht dat zonder mobiel nummer is verzonden (zijn deliveryStatus is null). Alleen de ontvangen berichten worden door GET /v1/messages?direction=in teruggegeven.
Verzoek
curl "https://volai.cz/v1/messages?limit=20&direction=all" \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"messages": [
{
"id": "msg_5b7e0d93",
"to": "+420777123456",
"from": "+420770112233",
"body": "Bevestigd, tot vrijdag om 10:00 uur. - My App",
"status": "sent",
"failReason": null,
"priceHal": 190,
"segments": 1,
"createdAt": 1756112200000,
"source": "api",
"direction": "out",
"smsNumber": "+420770112233",
"deliveryStatus": "delivered"
},
{
"id": "msg_3d9b5e71",
"to": "+420770112233",
"from": "+420777123456",
"body": "Bedankt, ik kom vrijdag om 10:00 uur.",
"status": "received",
"failReason": null,
"priceHal": 0,
"segments": 1,
"createdAt": 1756111900000,
"source": "inbound",
"direction": "in",
"smsNumber": "+420770112233",
"deliveryStatus": null
},
{
"id": "msg_7c1f9a2e",
"to": "+420777123456",
"from": "volai",
"body": "Hallo van volai! - My App",
"status": "sent",
"failReason": null,
"priceHal": 136,
"segments": 1,
"createdAt": 1756111500000,
"source": "api",
"direction": "out",
"smsNumber": null,
"deliveryStatus": null
}
],
"hasMore": false,
"nextBefore": null
}Foutcodes
- 400
validationlimit moet een positief geheel getal zijn; before moet een positief geheel getal zijn (een tijdstempel in ms); direction moetout,inofallzijn.
POST/v1/messages
Verstuurt een sms. Alleen Tsjechische en Slowaakse nummers (+420 / +421). De prijs is 1,36 kr (≈ 0,06 EUR) per segment - langere berichten (of berichten met tekens buiten het GSM-7-alfabet, meestal Tsjechische diakritische tekens) worden in meer segmenten gesplitst, elk apart in rekening gebracht - zie segments in het antwoord. Vereist een eerste opwaardering van het tegoed - het welkomsttegoed ontgrendelt sms niet.
Zonder fromNumber gaan berichten uit onder de gedeelde afzendernaam SMSinfo - carriers staan op Tsjechische netwerken alleen goedgekeurde afzendernamen toe, dus een eigen afzendernaam is met de gedeelde afzender niet mogelijk, en ontvangers kunnen niet op SMSinfo antwoorden. Het veld from in het antwoord is dan alleen het interne label volai; ontvangers zien het nooit. Bij een bericht dat vanaf een mobiel nummer is verzonden, is from dat nummer. Zet uw eigen identiteit in plaats daarvan rechtstreeks in de berichttekst, zoals in het voorbeeld hieronder.
Om vanaf uw eigen mobiele nummer te verzenden, stelt u fromNumber in (een nummer uit GET /v1/sms-numbers). De ontvanger ziet dan uw nummer in plaats van SMSinfo. Verzenden vanaf het nummer werkt alleen naar Tsjechische nummers (+420), kost 1,90 kr (≈ 0,08 EUR) per segment (exclusief btw) en heeft een dagplafond per account: 20 berichten in de eerste 30 dagen nadat u het nummer hebt gekocht, daarna 50. Het veld from in het verzoek wordt nog steeds genegeerd.
fromNumber accepteert ook een nummer voor bellen en sms (een nummer uit GET /v1/voice-sms-numbers). Een sms ervandaan kost 2,50 kr (≈ 0,10 EUR) per segment (exclusief btw), gaat alleen naar Tsjechische nummers (+420), heeft geen afleverrapporten (deliveryStatus blijft null) en het dagelijkse verzendplafond wordt gedeeld met mobiele nummers: 20 berichten in de eerste 30 dagen nadat u het nummer bestelt, daarna 50. Een antwoord STOP blokkeert niets op dit nummer (recipient_opted_out wordt alleen voor mobiele nummers teruggegeven), houd uitschrijvingen van uw ontvangers dus bij in uw eigen applicatie.
- Zonder diakritische tekens (het GSM-7-alfabet) bevat één segment 160 tekens; langere tekst wordt in stukken van 153 gesplitst.
- Met diakritische tekens of een teken buiten GSM-7 (UCS-2) is de limiet 70 tekens per segment; langere tekst wordt in stukken van 67 gesplitst.
- Tip: schrijf zonder diakritische tekens om in één segment te passen -
Prilis zlutoucky kunin plaats vanPříliš žluťoučký kůň.
Verzoek
curl -X POST "https://volai.cz/v1/messages" \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: REPLACE_WITH_UNIQUE_POST_V1_MESSAGES_ID" \
-d '{"to":"+420777123456","body":"Hallo van volai! - My App"}'Vervang vóór het uitvoeren REPLACE_WITH_UNIQUE… door een unieke ID voor deze actie, bijvoorbeeld een UUID, en bewaar die. Gebruik dezelfde ID opnieuw als u hetzelfde verzoek na een time-out herhaalt. Gebruik een nieuwe ID voor een andere actie of een gewijzigde body, ook bij een ander endpoint.
Antwoord
{
"id": "msg_7c1f9a2e",
"status": "sent",
"priceHal": 136,
"segments": 1
}Foutcodes
- 400
validationDe request body komt niet overeen met het schema (toofbodyontbreekt,bodyis langer dan 2000 tekens,fromNumberis geen string van maximaal 32 tekens), oftois hetzelfde nummer alsfromNumber(een nummer kan zichzelf niet sms'en). Hiervoor rekenen we niets af. - 400
invalid_numberDat is geen geldig Tsjechisch of Slowaaks telefoonnummer. - 400
invalid_bodyDe berichttekst moet 1 tot 765 tekens lang zijn. - 400
unsupported_countryNummer buiten CZ/SK - daar versturen we geen sms. - 400
blocked_destinationDe bestemming is een premium-tariefnummer (voorvoegsels +420 900, +420 906, +420 908, +420 909, +420 976 en +421 900). Daarheen versturen we geen sms, zodat u geen onbedoelde rekening oploopt. Hiervoor rekenen we niets af. - 400
recipient_cannot_receive_smsVast nummer - de sms zou niet aankomen. Hiervoor rekenen we niets af. - 400
unsupported_charactersDe tekst bevat een emoji of een ander teken dat het telefoonnetwerk niet kan afleveren. Hiervoor rekenen we niets af. - 400
recipient_is_virtual_numberDe ontvanger is een volai-nummer. Een sms bereikt nooit een spraaknummer van volai (ook uw eigen niet) - die verwerken alleen gesprekken. Een sms vanaf de gedeelde naam SMSinfo bereikt geen mobiel nummer van volai en geen nummer voor bellen en sms: verstuur hem vanaf uw eigen mobiele nummer of nummer voor bellen en sms met het veldfromNumber. Hiervoor rekenen we niets af. - 400
recipient_rejectedHet telefoonnetwerk accepteert dit nummer niet - meestal is het een virtueel of niet-bestaand nummer. Hiervoor rekenen we niets af. - 400
invalid_from_numberfromNumberis noch een actief mobiel nummer noch een actief nummer voor bellen en sms van uw account (een onbekend nummer of een nummer van een ander account geeft dezelfde fout). Controleer het inGET /v1/sms-numbersen inGET /v1/voice-sms-numbers; een nummer waarvan de status nietactiveis (bijvoorbeeldreleasingofawaiting_registration) kan niet worden gebruikt. Er wordt niets afgeschreven. - 400
from_number_unsupported_destinationVerzenden vanaf een mobiel nummer werkt alleen naar Tsjechische nummers (+420). Verstuur naar een Slowaaks nummer zonderfromNumber, onder de gedeelde naam SMSinfo. Er wordt niets afgeschreven. - 400
destination_not_allowedVerzenden vanaf een nummer voor bellen en sms werkt alleen naar Tsjechische nummers (+420). Verstuur naar een Slowaaks nummer zonderfromNumber, onder de gedeelde naam SMSinfo. Er wordt niets afgeschreven. - 402
insufficient_creditHet tegoed dekt niet alle segmenten van het bericht, of het account heeft nooit opgewaardeerd - sms vanaf de gedeelde afzender SMSinfo vereist een eerste opwaardering, en het welkomsttegoed dekt ze niet, zelfs niet met een eigen nummer. Hiervoor rekenen we niets af. - 402
subscription_cap_reachedHet account heeft een maandabonnement met het bestedingsplafond, het maandtegoed is op en het opgewaardeerde tegoed dekt de actie niet. Het vervangtinsufficient_creditvoor zo'n account (dezelfde HTTP-status). Er is niets afgeschreven. - 402
spend_cap_reachedHet door u ingestelde maandelijkse bestedingsplafond is bereikt. Er is niets geplaatst of verzonden en er is niets in rekening gebracht. Verhoog of verwijder het plafond metPUTofDELETE /v1/account/spend-cap, of wacht tot de volgende kalendermaand. - 403
sms_numbers_unavailableVerzenden vanaf een mobiel nummer is voor dit account nu niet beschikbaar. Er wordt niets afgeschreven; zonderfromNumberkan het bericht nog steeds onder SMSinfo uitgaan. - 403
voice_sms_numbers_unavailableVerzenden vanaf een nummer voor bellen en sms is voor dit account nu niet beschikbaar. Er wordt niets afgeschreven; zonderfromNumberkan het bericht nog steeds onder SMSinfo uitgaan. - 409
recipient_opted_outDe ontvanger antwoordde STOP op uw mobiele nummer en accepteert geen verdere berichten ervan totdat hij START stuurt. Opnieuw proberen helpt niet en er wordt niets afgeschreven. - 429
rate_limitedMeer dan 1 sms per 2 seconden, of meer dan 100 sms op het account binnen 24 uur na de eerste (de melding zegt hoe lang het duurt tot de limiet wordt gereset). Verzenden vanaf een mobiel nummer of een nummer voor bellen en sms (fromNumber) heeft een verder dagplafond per account, gedeeld door beide soorten: 20 berichten in de eerste 30 dagen nadat u het nummer hebt gekocht, daarna 50. - 502
send_failedHet telefoonnetwerk heeft het verzoek geweigerd. We hebben uw tegoed niet belast - probeer het opnieuw. - 502
send_failed_operatorEen fout aan onze kant (tegoed van afzender of operator). We hebben uw tegoed niet belast - probeer het zo meteen opnieuw. - 502
sms_gateway_failedDe gateway van de operator is tijdelijk uitgevallen. We hebben uw tegoed niet belast - probeer het over een paar minuten opnieuw. - 502
send_unknownWe konden het verzendresultaat niet betrouwbaar bevestigen; het bericht kan nog steeds zijn verzonden - het wordt toch in rekening gebracht, verstuur het dus niet blindelings opnieuw. - 503
ledger_unavailableHet saldo kon nu niet worden gelezen, probeer het zo meteen opnieuw. Er wordt niets afgeschreven.
De status van een bericht: pending (opgeslagen, wordt nog verzonden), sent (de carrier heeft het bericht geaccepteerd voor verzending, ook berichten in de wachtrij; dit is geen ontvangstbevestiging), failed (de carrier heeft het geweigerd, bij een bericht vanaf een mobiel nummer mogelijk pas achteraf - niet in rekening gebracht, een reeds afgeschreven tegoed wordt terugbetaald, priceHal 0), received (een bericht ontvangen op uw mobiele nummer of nummer voor bellen en sms, altijd met direction: "in" en priceHal 0) en unknown (de carrier bevestigde de acceptatie niet, meestal omdat zijn antwoord een timeout gaf) - het wordt toch tegen de volle prijs in rekening gebracht, omdat het bericht mogelijk is doorgekomen. Bij een bericht vanaf een mobiel nummer kan unknown nog vanzelf veranderen: in sent met een deliveryStatus zodra de carrier het bericht bevestigt, of in failed met terugbetaling van het tegoed als hij het weigert. Krijgt u unknown, verstuur het dan niet blindelings opnieuw - neem contact op met support en we betalen een dubbele afschrijving terug.
Bij status: "failed" krijgt u ook failReason: unsupported_characters (een emoji of teken dat het netwerk niet kan afleveren), unsupported_recipient (het netwerk accepteert het nummer niet), forbidden_sender en low_balance (een fout aan onze kant), gateway_failed (de gateway van de operator), network_rejected (een andere weigering), opted_out (de ontvanger antwoordde STOP op uw mobiele nummer). Bij andere statussen is het null.
deliveryStatus (delivered of undelivered) is het afleverrapport van de carrier en bestaat alleen voor berichten die vanaf uw mobiele nummer zijn verzonden. Het is null voor berichten onder de gedeelde naam SMSinfo, die geen afleverrapporten heeft, voor berichten vanaf een nummer voor bellen en sms, die er ook geen heeft, en voor ontvangen berichten. sent betekent nog steeds alleen dat de carrier het bericht heeft geaccepteerd en bewijst geen aflevering. Een bericht met undelivered wordt nog steeds in rekening gebracht, omdat de carrier het heeft geaccepteerd. Meldt de carrier later een mislukking, dan verandert de status van het bericht in failed, daalt priceHal naar 0 en wordt het tegoed ervoor terugbetaald. Het afleverrapport komt binnen zodra de carrier het meldt; komt de statusmelding niet aan, dan vult een regelmatige controle elke 15 minuten het in. Meldt de carrier het nooit, dan blijft het null.
Voorbeeld: verzenden vanaf uw mobiele nummer
Om op een ontvangen sms te antwoorden, verzendt u vanaf hetzelfde nummer: stel fromNumber in op uw mobiele nummer uit GET /v1/sms-numbers en de ontvanger ziet uw nummer. Het afleverrapport komt later: de deliveryStatus van het bericht begint als null, en delivered of undelivered leest u uit GET /v1/messages/{id}.
curl -X POST https://volai.cz/v1/messages \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"to": "+420777123456", "fromNumber": "+420770112233", "body": "Bevestigd, tot vrijdag om 10:00 uur. - My App"}'{
"id": "msg_5b7e0d93",
"status": "sent",
"priceHal": 190,
"segments": 1
}Voorbeeld: een bericht met diakritische tekens van meer dan 70 tekens betekent meer segmenten
Dit bericht is 112 tekens lang en bevat diakritische tekens, dus het wordt als UCS-2 geteld (een limiet van 67 tekens per segment bij berichten uit meerdere delen) - het komt uit op 2 segmenten, dus 2,72 kr (≈ 0,11 EUR), niet 1,36 kr (≈ 0,06 EUR):
curl -X POST https://volai.cz/v1/messages \
-H "Authorization: Bearer vk_YOUR_KEY" \
-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."}'{
"id": "msg_9a3f1c7d",
"status": "sent",
"priceHal": 272,
"segments": 2
}GET/v1/messages/{id}
Het detail van één bericht.
Verzoek
curl https://volai.cz/v1/messages/msg_7c1f9a2e \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"message": {
"id": "msg_7c1f9a2e",
"to": "+420777123456",
"from": "volai",
"body": "Hallo van volai! - My App",
"status": "sent",
"failReason": null,
"priceHal": 136,
"segments": 1,
"createdAt": 1756111500000,
"source": "api",
"direction": "out",
"smsNumber": null,
"deliveryStatus": null
}
}Foutcodes
- 404
not_foundHet bericht bestaat niet, hoort niet bij uw account, of is een ontvangen bericht ouder dan 90 dagen.
Mobiele nummers
GET/v1/sms-numbers
De mobiele nummers van het account (de add-on mobiel nummer): Tsjechische mobiele nummers die sms ontvangen en vanaf uw eigen nummer verzenden. Elk heeft status (active, of releasing terwijl een vrijgave loopt of op een nieuwe poging wacht), de kosten monthlyFeeHal per periode van 30 dagen en nextChargeAt, wanneer de volgende verschuldigd is. De verzendlimiet staat in dailySendLimit (20 sms per venster van 24 uur gedurende de eerste 30 dagen van het nummer, daarna 50), dailySendRemaining (hoeveel er over zijn, 0 voor een nummer dat wordt vrijgegeven) en dailySendResetsAt (wanneer het venster eindigt, null als er geen loopt); het venster wordt gedeeld door alle mobiele nummers van het account, begint met de eerste verzonden sms en telt ook pogingen boven de limiet en sms die de carrier weigerde. De lijst werkt ook wanneer nieuwe aankopen niet beschikbaar zijn.
Verzoek
curl https://volai.cz/v1/sms-numbers \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"smsNumbers": [
{
"number": "+420770112233",
"status": "active",
"createdAt": 1756111640000,
"monthlyFeeHal": 29000,
"nextChargeAt": 1758703640000,
"dailySendLimit": 20,
"dailySendRemaining": 17,
"dailySendResetsAt": 1756198040000
}
]
}POST/v1/sms-numbers
Koopt een Tsjechisch mobiel nummer voor het account. Stuur geen body; wij kiezen het nummer. Het nummer ontvangt sms (lees ze met GET /v1/messages?direction=in of de webhookgebeurtenis message.received) en u verzendt ervandaan met fromNumber bij POST /v1/messages. Het voert geen gesprekken en ontvangt er geen. 290,00 kr (≈ 12,08 EUR) exclusief btw wordt meteen van het tegoedsaldo afgeschreven voor de eerste periode van 30 dagen en daarna elke 30 dagen opnieuw; de betaalde periode wordt niet terugbetaald wanneer u het nummer vrijgeeft. Een account kan hoogstens twee mobiele nummers hebben en kopen vereist een eerste opwaardering van het tegoed. Mislukt er na de aankoop iets bij de carrier, dan wordt het nummer weer vrijgegeven en wordt niets afgeschreven. Zolang een andere aankoop voor het account nog loopt, krijgt u 429 rate_limited; wacht een paar seconden en herhaal het. Accepteert Idempotency-Key: stuur na een timeout dezelfde waarde, zodat een nieuwe poging geen tweede nummer kan kopen. Aflevering van berichten van elke afzender, inclusief verificatiecodes, is niet gegarandeerd.
Verzoek
curl -X POST "https://volai.cz/v1/sms-numbers" \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Idempotency-Key: REPLACE_WITH_UNIQUE_POST_V1_SMS_NUMBERS_ID"Vervang vóór het uitvoeren REPLACE_WITH_UNIQUE… door een unieke ID voor deze actie, bijvoorbeeld een UUID, en bewaar die. Gebruik dezelfde ID opnieuw als u hetzelfde verzoek na een time-out herhaalt. Gebruik een nieuwe ID voor een andere actie of een gewijzigde body, ook bij een ander endpoint.
201 Created
{
"smsNumber": {
"number": "+420770112233",
"status": "active",
"createdAt": 1756111640000,
"monthlyFeeHal": 29000,
"nextChargeAt": 1758703640000,
"dailySendLimit": 20,
"dailySendRemaining": 20,
"dailySendResetsAt": null
}
}Foutcodes
- 402
insufficient_creditUw tegoed dekt de kosten van 290,00 kr (≈ 12,08 EUR) voor de eerste periode niet, of het account heeft nooit opgewaardeerd - een mobiel nummer kan pas na de eerste opwaardering worden gekocht, en het welkomsttegoed ontgrendelt het niet. Er wordt niets afgeschreven. - 402
subscription_cap_reachedHet account heeft een maandabonnement met het bestedingsplafond, het maandtegoed is op en het opgewaardeerde tegoed dekt de actie niet. Het vervangtinsufficient_creditvoor zo'n account (dezelfde HTTP-status). Er is niets afgeschreven. - 403
sms_numbers_unavailableDe add-on mobiel nummer is voor dit account nu niet beschikbaar. Er wordt niets afgeschreven; schrijf naar support als u toegang verwachtte. - 409
sms_number_limitHet account heeft al het maximale aantal mobiele nummers (twee). Geef er een vrij metDELETE /v1/sms-numbers/{e164}en koop opnieuw. Er wordt niets afgeschreven. - 429
rate_limitedEen andere nummeraankoop op dit account (spraak of sms) loopt nog. Wacht een paar seconden en herhaal hetzelfde verzoek. Er wordt niets afgeschreven. - 503
sms_number_out_of_stockDe carrier heeft nu geen vrij Tsjechisch mobiel nummer. Er wordt niets afgeschreven; probeer het later opnieuw. - 503
ledger_unavailableHet saldo kon nu niet worden gelezen, probeer het zo meteen opnieuw. Er wordt niets afgeschreven.
DELETE/v1/sms-numbers/{e164}
Geeft een mobiel nummer vrij: het gaat terug naar de carrier en kan niet worden hersteld, en berichten die er daarna naartoe worden gestuurd bereiken u niet meer. Reeds ontvangen berichten blijven in de lijst staan totdat hun 90 dagen voorbij zijn. De kosten voor de lopende periode van 30 dagen worden niet terugbetaald. Mislukt het vrijgeven bij de carrier, dan blijft het nummer in status: "releasing"; verstuur hetzelfde verzoek opnieuw om het vrij te geven.
Verzoek
curl -X DELETE "https://volai.cz/v1/sms-numbers/+420770112233" \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"released": true
}Foutcodes
- 400
validatione164 in de URL is geen geldig telefoonnummer (schrijf het plusteken letterlijk of als %2B). - 404
not_foundHet mobiele nummer bestaat niet of hoort niet bij uw account. - 502
release_failedHet vrijgeven kon bij de carrier zojuist niet worden voltooid. Het nummer blijft van u instatus: "releasing"; verstuur hetzelfde verzoek zo meteen opnieuw. De reeds betaalde periode wordt niet terugbetaald.
Nummer voor bellen en smss
Het nummer voor bellen en sms is een derde soort nummer naast spraaknummers en mobiele nummers: een vast Praags nummer (+420 2xx) dat gesprekken en sms ontvangt en waarmee uw agent belt en sms verstuurt. Een spraaknummer kan geen sms ontvangen en een mobiel nummer heeft geen gesprekken. Het nummer wordt bij de operator geregistreerd op naam van de dienstverlener volai, en u gebruikt het; nadat u het hebt besteld, is het meestal binnen 1 werkdag actief en uiterlijk binnen 48 uur, en de kosten worden pas na activering afgeschreven. Inkomende gesprekken worden beantwoord door een agent die op de eigen spraakengine van volai draait, en het nummer verwerkt hoogstens twee gelijktijdige gesprekken. Het ontvangen van sms is geverifieerd op het O2-netwerk, en we garanderen geen verificatiecodes van diensten. Met country in de bestelbody krijgt u in plaats van het Praagse nummer een nummer in een ander land: een vast nummer in een ander land dat gesprekken ontvangt (een agent beantwoordt ze) maar geen sms heeft (het ontvangt noch verstuurt ze) en dat alleen belt naar het land van het nummer en naar Tsjechië. De prijs hangt af van het land (lees hem uit monthlyFeeHal van het land in GET /v1/voice-sms-numbers/countries) en de overige regels zijn dezelfde als bij het Praagse nummer; het nummer wordt bij de operator geregistreerd op naam van de dienstverlener met een adres in dat land, en is meestal binnen 1 werkdag actief en uiterlijk binnen 3 werkdagen (binnen het uur voor landen zonder registratie). De landen waarin u kunt bestellen veranderen, lees ze dus uit GET /v1/voice-sms-numbers/countries (MCP list_voice_sms_countries) en noem er nooit een uit het hoofd. U bestelt het zelf in het portaal, via REST (POST /v1/voice-sms-numbers) of MCP (order_voice_sms_number), na uw eerste opwaardering van het tegoed. Zijn bestellingen tijdelijk gepauzeerd, dan geeft de bestelling voice_sms_numbers_unavailable en wordt niets afgeschreven; probeer het later opnieuw of schrijf naar support.
GET/v1/voice-sms-numbers
De nummer voor bellen en smss en nummer in een ander lands van het account: vaste Praagse nummers die gesprekken en sms ontvangen en waarmee u belt en sms verstuurt, en vaste nummers in andere landen die gesprekken ontvangen en waarmee de agent belt, zonder sms. Elk heeft country (ISO-code), capabilities (calls, sms en outboundTo: de landen waarnaar de agent vanaf het nummer belt) en status (awaiting_registration, active, rejected of releasing), routing (waar inkomende gesprekken overgaan), de kosten monthlyFeeHal per periode van 30 dagen, de prijs smsFromNumberHal van één sms-segment dat vanaf het nummer wordt verzonden (null voor een nummer zonder sms), nextChargeAt (wanneer de volgende kosten verschuldigd zijn, null totdat het nummer actief is), smsInboundReady (true wanneer het nummer actief is en het ontvangen van sms is ingesteld) en de tijden createdAt en activatedAt. pendingOrders toont bestellingen die zijn geaccepteerd maar waaraan geen nummer is toegewezen (na een tijdelijk probleem bij de operator); elk bevat alleen createdAt. Zo'n bestelling rondt zichzelf af en het nummer verschijnt dan tussen de nummers, en tot die tijd telt ze mee voor de limieten, bestel dus niet opnieuw. Elk item bevat het country van de bestelling. Mislukt een bestelling, dan verdwijnt ze zonder nummer uit pendingOrders, wordt niets afgeschreven en ontvangt het account een e-mail; u kunt dan opnieuw bestellen. De lijst werkt ook wanneer nieuwe bestellingen niet beschikbaar zijn.
Verzoek
curl https://volai.cz/v1/voice-sms-numbers \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"voiceSmsNumbers": [
{
"number": "+420292000001",
"country": "CZ",
"capabilities": { "calls": true, "sms": true, "outboundTo": ["CZ"] },
"status": "active",
"routing": { "mode": "agent", "agentId": "ag_9f3a2b7c4d1e", "sipTarget": "engine", "hasSipPassword": false },
"monthlyFeeHal": 4900,
"smsFromNumberHal": 250,
"nextChargeAt": 1758703640000,
"smsInboundReady": true,
"createdAt": 1756101640000,
"activatedAt": 1756111640000
}
],
"pendingOrders": [{ "createdAt": 1758790000000, "country": "CZ" }]
}GET/v1/voice-sms-numbers/countries
De landen waarin u nu een nummer kunt bestellen, in de volgorde van het aanbod (Tsjechië eerst). Elk item bevat country (de ISO-code die u als country aan POST /v1/voice-sms-numbers meegeeft), name, prefix, numberKind, sms (alleen true voor het Tsjechische nummer, een nummer buiten Tsjechië verwerkt alleen gesprekken), outboundTo (de landen waarnaar de agent vanaf het nummer mag bellen), activation (within_hour of within_business_days) en monthlyFeeHal (de prijs van een nummer in dat land per 30 dagen in hellers, exclusief btw; hij verschilt per land, toon de gebruiker dus dat bedrag vóór het bestellen). Het aanbod hangt af van het account en verandert in de loop van de tijd, dus lees het in plaats van een land aan te nemen. Alleen-lezen, er wordt niets afgeschreven.
Verzoek
curl https://volai.cz/v1/voice-sms-numbers/countries \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"countries": [
{
"country": "CZ",
"name": "Czechia",
"prefix": "+420",
"numberKind": "landline, Prague (+420 2)",
"sms": true,
"outboundTo": ["CZ"],
"activation": "within_business_days",
"monthlyFeeHal": 4900
},
{
"country": "GB",
"name": "United Kingdom",
"prefix": "+44",
"numberKind": "landline, London (+44 20)",
"sms": false,
"outboundTo": ["CZ"],
"activation": "within_hour",
"monthlyFeeHal": 2900
}
],
"waitlisted": ["NL"]
}POST/v1/voice-sms-numbers/countries/{country}/waitlist
Schrijft het account in op de wachtlijst van een land waar nog geen nummers worden verkocht (een land van de kaart in het portaal dat GET /v1/voice-sms-numbers/countries niet teruggeeft). Er wordt niets in rekening gebracht. Opnieuw aanroepen voor hetzelfde land geeft alreadyJoined: true. Een land dat al in het aanbod zit, geeft validation. Klanten worden nog niet automatisch geïnformeerd; de ingeschreven landen staan in het veld waitlisted van de landenlijst.
Verzoek
curl -X POST https://volai.cz/v1/voice-sms-numbers/countries/NL/waitlist \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"country": "NL",
"joined": true,
"alreadyJoined": false
}Foutcodes
- 400
validationcountryis niet de ISO-code van een land op de kaart, of het is Tsjechië, of het land zit nu in het aanbod van het account en kan direct worden besteld. - 404
user_not_foundDe gebruiker die eigenaar is van deze API-sleutel ontbreekt in het account - een ongebruikelijke toestand; neem contact op met support.
POST/v1/voice-sms-numbers
Bestelt een Praags nummer voor bellen en sms voor het account, of met country een nummer in een ander land. De body is optioneel: zonder body (of met {"country": "CZ"}) krijgt u een Praags nummer, {"country": "<ISO-code>"} bestelt een nummer in een ander land. Kies het land altijd uit GET /v1/voice-sms-numbers/countries; een land buiten het aanbod geeft 400 country_not_offered en er wordt niets afgeschreven. Een nummer in een ander land is alleen voor gesprekken, zonder sms: het ontvangt noch verstuurt ze, het belt alleen naar het land van het nummer en naar Tsjechië, en het is uiterlijk 3 werkdagen na de bestelling actief (binnen het uur voor landen zonder registratie). De prijs van een nummer in een ander land hangt af van het land: het is monthlyFeeHal van dat land uit GET /v1/voice-sms-numbers/countries, de kosten worden pas na activering afgeschreven en de overige regels zijn dezelfde als bij het Praagse nummer. Het nummer wordt automatisch toegewezen en kan niet worden gekozen. Een Praags nummer wordt actief na registratie bij de operator, meestal binnen één werkdag (hoogstens 48 uur): tot die tijd is de status ervan awaiting_registration, gaat het niet over en ontvangt het geen sms, en wordt niets afgeschreven. De kosten van 49,00 kr (≈ 2,04 EUR) exclusief btw per 30 dagen (voor het Praagse nummer; een nummer in een ander land kost de prijs van zijn land) worden pas van het tegoedsaldo afgeschreven zodra het nummer actief wordt en daarna elke 30 dagen opnieuw; de betaalde periode wordt niet terugbetaald wanneer u het nummer vrijgeeft. U belt ermee via een agent (POST /v1/calls met agentId) en vanaf een Praags nummer verstuurt u sms (fromNumber bij POST /v1/messages, 2,50 kr (≈ 0,10 EUR) per segment, alleen naar Tsjechische nummers en zonder afleverrapporten); inkomende gesprekken routeert u met PATCH /v1/voice-sms-numbers/{e164}. Een account kan hoogstens twee van dergelijke nummers in één land hebben en hoogstens vier in totaal (een bestelling die nog op registratie wacht, of waaraan nog een nummer moet worden toegewezen, telt ook mee), en bestellen vereist een eerste opwaardering van het tegoed. Het antwoord is 202; voiceSmsNumber is null wanneer een tijdelijk probleem bij de operator de bestelling zonder toegewezen nummer achterliet: de bestelling gaat op de achtergrond door, is zichtbaar in pendingOrders van GET /v1/voice-sms-numbers totdat het nummer is toegewezen en verschijnt daar dan tussen de nummers, dus bestel niet opnieuw. Mislukt een bestelling, dan verdwijnt ze zonder nummer uit pendingOrders, wordt niets afgeschreven en ontvangt het account een e-mail; u kunt dan opnieuw bestellen. Accepteert Idempotency-Key: stuur na een timeout dezelfde waarde, zodat een nieuwe poging geen tweede bestelling kan aanmaken. Bij een Praags nummer is het ontvangen van sms alleen op het O2-netwerk geverifieerd, en aflevering van berichten van elke afzender, inclusief verificatiecodes, is niet gegarandeerd.
# Example: a number in another country (choose the country from GET /v1/voice-sms-numbers/countries)
curl -X POST https://volai.cz/v1/voice-sms-numbers \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"country":"GB"}'{
"voiceSmsNumber": {
"number": "+442000000001",
"country": "GB",
"capabilities": { "calls": true, "sms": false, "outboundTo": ["CZ"] },
"status": "awaiting_registration",
"routing": { "mode": "none", "hasSipPassword": false },
"monthlyFeeHal": 2900,
"smsFromNumberHal": null,
"nextChargeAt": null,
"smsInboundReady": false,
"createdAt": 1756101640000,
"activatedAt": null
}
}Verzoek
curl -X POST "https://volai.cz/v1/voice-sms-numbers" \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Idempotency-Key: REPLACE_WITH_UNIQUE_POST_V1_VOICE_SMS_NUMBERS_ID"Vervang vóór het uitvoeren REPLACE_WITH_UNIQUE… door een unieke ID voor deze actie, bijvoorbeeld een UUID, en bewaar die. Gebruik dezelfde ID opnieuw als u hetzelfde verzoek na een time-out herhaalt. Gebruik een nieuwe ID voor een andere actie of een gewijzigde body, ook bij een ander endpoint.
202 Accepted
{
"voiceSmsNumber": {
"number": "+420292000001",
"country": "CZ",
"capabilities": { "calls": true, "sms": true, "outboundTo": ["CZ"] },
"status": "awaiting_registration",
"routing": { "mode": "none", "hasSipPassword": false },
"monthlyFeeHal": 4900,
"smsFromNumberHal": 250,
"nextChargeAt": null,
"smsInboundReady": false,
"createdAt": 1756101640000,
"activatedAt": null
}
}Foutcodes
- 400
validationDe body is geen geldig JSON-object, ofcountryis geen ISO-code van twee hoofdletters (bijvoorbeeldPL). Er wordt niets afgeschreven. - 400
country_not_offeredDit account kan nu niet bestellen in het gevraagdecountry, omdat het niet in het aanbod staat.GET /v1/voice-sms-numbers/countriesgeeft het actuele aanbod. Er wordt niets afgeschreven. - 402
insufficient_creditUw tegoed dekt de kosten voor de eerste periode (49,00 kr (≈ 2,04 EUR) voor een Praags nummer, vanaf 29,00 kr (≈ 1,21 EUR) per land voor een nummer in een ander land) niet, of het account heeft nooit opgewaardeerd - een nummer voor bellen en sms kan pas na de eerste opwaardering worden besteld, en het welkomsttegoed ontgrendelt het niet. Er wordt niets afgeschreven. - 402
subscription_cap_reachedHet account heeft een maandabonnement met het bestedingsplafond, het maandtegoed is op en het opgewaardeerde tegoed dekt de actie niet. Het vervangtinsufficient_creditvoor zo'n account (dezelfde HTTP-status). Er is niets afgeschreven. - 403
voice_sms_numbers_unavailableNummer voor bellen en smss zijn voor dit account nu niet beschikbaar: de functie is ervoor uitgeschakeld, of nieuwe bestellingen zijn gepauzeerd na een probleem met een registratie. Opnieuw proberen helpt niet; er wordt niets afgeschreven, schrijf naar support. - 409
voice_sms_number_limitHet account heeft al het maximale aantal nummer voor bellen en smss (twee in één land, vier in totaal op het account); een bestelling die nog op registratie wacht, of waaraan geen nummer is toegewezen (pendingOrdersin de lijst), telt ook mee. Geef er een vrij (DELETE /v1/voice-sms-numbers/{e164}) of annuleer een bestelling die op registratie wacht (POST /v1/voice-sms-numbers/{e164}/cancel) en bestel opnieuw; een bestelling zonder toegewezen nummer rondt zichzelf af en kan niet worden geannuleerd. Er wordt niets afgeschreven. - 409
idempotency_conflictDezeIdempotency-Keyis al gebruikt voor een andere bestelling. - 429
rate_limitedOp dit account loopt al een andere nummerbestelling (spraak, sms, of gesprekken en sms). Wacht een paar seconden en herhaal hetzelfde verzoek. Er wordt niets afgeschreven. - 429
voice_sms_order_limitIn de afgelopen 30 dagen heeft het account twee bestellingen van een nummer voor bellen en sms geannuleerd, dus nu kan geen nieuwe worden geplaatst; de limiet stopt bestel-en-annuleerlussen. Wacht tot het venster van 30 dagen voorbij is, of schrijf naar support. Er wordt niets afgeschreven. - 503
voice_sms_number_out_of_stockDe operator heeft nu geen vrij nummer in het gevraagde land (een Praags nummer wanneercountryontbreekt). Er wordt niets afgeschreven; probeer het later opnieuw. - 503
order_failedHet nummer kon niet bij de operator worden ingesteld (een tijdelijk probleem of een geweigerde registratie), dus de bestelling kon niet worden voltooid. Er wordt niets afgeschreven; probeer het later opnieuw, of schrijf naar support. - 503
ledger_unavailableHet saldo kon nu niet worden gelezen, probeer het zo meteen opnieuw. Er wordt niets afgeschreven.
GET/v1/voice-sms-numbers/{e164}
Detail van één nummer voor bellen en sms, in dezelfde vorm als een lijstitem. Het nummer van een ander account en een niet-bestaand nummer geven dezelfde 404.
Verzoek
curl "https://volai.cz/v1/voice-sms-numbers/+420292000001" \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"voiceSmsNumber": {
"number": "+420292000001",
"country": "CZ",
"capabilities": { "calls": true, "sms": true, "outboundTo": ["CZ"] },
"status": "active",
"routing": { "mode": "none", "hasSipPassword": false },
"monthlyFeeHal": 4900,
"smsFromNumberHal": 250,
"nextChargeAt": 1758703640000,
"smsInboundReady": true,
"createdAt": 1756101640000,
"activatedAt": 1756111640000
}
}Foutcodes
- 400
validatione164 in de URL is geen geldig telefoonnummer (schrijf het plusteken letterlijk of als %2B). - 404
not_foundHet nummer voor bellen en sms bestaat niet of hoort niet bij uw account.
PATCH/v1/voice-sms-numbers/{e164}
Stelt in waar inkomende gesprekken naar een nummer voor bellen en sms overgaan: routing is {"mode": "agent", "agentId": "ag_..."} (een agent op de eigen spraakengine van volai neemt aan) of {"mode": "none"} (gesprekken worden niet beantwoord en de agent wordt losgekoppeld). Doorschakelen naar een ander nummer (forward) en een eigen SIP-server (sip) worden voor dit nummertype niet ondersteund en geven unsupported_for_number_type, evenals een agent die niet op de eigen engine draait. Een agent kan alleen aan een actief nummer worden gekoppeld (invalid_transition); de routering uitschakelen (none) werkt bij elke status. Een agent die aan dit nummer is gekoppeld, mag alleen een Tsjechisch (+420) doorverbindnummer hebben, anders wordt destination_not_allowed teruggegeven. Terwijl een andere bewerking het nummer verwerkt (een activering, afrekening of vrijgave) krijgt u 409 in_progress: er is niets gewijzigd en het verzoek kan over een minuut worden herhaald. Alleen mode en agentId bereiken de service; andere velden van de body worden weggelaten.
Verzoek
curl -X PATCH "https://volai.cz/v1/voice-sms-numbers/+420292000001" \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"routing":{"mode":"agent","agentId":"ag_9f3a2b7c4d1e"}}'Antwoord
{
"voiceSmsNumber": {
"number": "+420292000001",
"country": "CZ",
"capabilities": { "calls": true, "sms": true, "outboundTo": ["CZ"] },
"status": "active",
"routing": { "mode": "agent", "agentId": "ag_9f3a2b7c4d1e", "sipTarget": "engine", "hasSipPassword": false },
"monthlyFeeHal": 4900,
"smsFromNumberHal": 250,
"nextChargeAt": 1758703640000,
"smsInboundReady": true,
"createdAt": 1756101640000,
"activatedAt": 1756111640000
}
}Foutcodes
- 400
validationDe body is geen geldig JSON-object metrouting,modeis nochagentnochnone,agentIdontbreekt bijagentof is langer dan 64 tekens, of e164 in de URL is geen geldig telefoonnummer. - 400
unsupported_for_number_typeDe modusforwardofsip, of een agent die niet op de eigen spraakengine van volai draait. Kies een agent op de engine, ofnone. Er is niets gewijzigd. - 400
destination_not_allowedDe agent heeft een doorverbindnummer buiten+420, waarnaar een nummer voor bellen en sms niet kan doorverbinden. Wijzig het doorverbindnummer van de agent in een Tsjechisch nummer, of verwijder het, en koppel daarna het nummer. Bij een nummer in een ander land moet het doorverbindnummer in het land van het nummer liggen (zodra bellen daarheen is ingeschakeld) of in Tsjechië. Er is niets gewijzigd. - 404
not_foundHet nummer bestaat niet of hoort niet bij uw account, ofagentIdis geen actieve agent van uw account. - 409
invalid_transitionHet nummer is niet actief: het wacht op registratie, de registratie is geweigerd, of het wordt vrijgegeven. Een agent kan alleen aan een actief nummer worden gekoppeld (nonewerkt altijd). De status staat inGET /v1/voice-sms-numbers/{e164}. - 409
in_progressEen andere bewerking (een activering, afrekening of vrijgave) verwerkt het nummer. Er is niets gewijzigd; herhaal hetzelfde verzoek over een minuut. - 502
routing_failedHet loskoppelen van de agent in de eigen spraakengine is mislukt; het nummer blijft gerouteerd zoals het was. Herhaal het verzoek zo meteen. - 502
engine_unavailableDe eigen spraakengine reageert nu niet, de agent is niet aan het nummer gekoppeld. Probeer het zo meteen opnieuw, of neem contact op met support. Dezelfde code wordt teruggegeven bij een schrijfactie die door het standby-knooppunt van de engine werd geweigerd (de engine draait momenteel op een standby-knooppunt dat geen configuratie accepteert): de melding zegt dat, de wijziging is niet opgeslagen en hetzelfde verzoek kan over een paar minuten worden herhaald. - 502
engine_readback_mismatchDe engine antwoordde, maar het terugleggen toonde een andere koppeling dan de koppeling die we verstuurden. Probeer het opnieuw.
POST/v1/voice-sms-numbers/{e164}/cancel
Annuleert een nummer voor bellen en sms dat nog op registratie bij de operator wacht (awaiting_registration): het nummer wordt geannuleerd en u betaalt niets, omdat de kosten pas na activering worden afgeschreven. Een actief nummer kan niet worden geannuleerd (409 invalid_transition); geef het vrij met DELETE /v1/voice-sms-numbers/{e164}. Een annulering telt mee voor een limiet van twee geannuleerde bestellingen in 30 dagen; zodra die is bereikt, geeft POST /v1/voice-sms-numbers voice_sms_order_limit. Geen body.
Verzoek
curl -X POST "https://volai.cz/v1/voice-sms-numbers/+420292000001/cancel" \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"cancelled": true
}Foutcodes
- 400
validatione164 in de URL is geen geldig telefoonnummer (schrijf het plusteken letterlijk of als %2B). - 404
not_foundHet nummer voor bellen en sms bestaat niet of hoort niet bij uw account. - 409
invalid_transitionHet nummer is al actief (betaald; geef het vrij metDELETE), geweigerd (niets te annuleren), of wordt vrijgegeven. - 409
in_progressHet nummer wordt nu geactiveerd of een andere bewerking verwerkt de bestelling ervan. Probeer het over een paar minuten opnieuw; zodra het actief is, kunt u het vrijgeven. - 500
internal_errorDe annulering kon bij de operator zojuist niet worden bevestigd. De bestelling blijft wachten en er wordt niets afgeschreven; herhaal het verzoek zo meteen.
DELETE/v1/voice-sms-numbers/{e164}
Geeft een actief nummer voor bellen en sms vrij: het koppelt de agent los, gaat terug naar de operator en kan niet worden hersteld. De kosten voor de lopende periode van 30 dagen worden niet terugbetaald. Een nummer dat op registratie wacht, wordt niet vrijgegeven maar geannuleerd (POST /v1/voice-sms-numbers/{e164}/cancel). Mislukt het vrijgeven (het loskoppelen van de agent in de engine, of het annuleren bij de operator), dan wordt 502 release_failed teruggegeven: het nummer blijft active, of gaat naar releasing, waarin het niet meer overgaat en niet kan bellen of verzenden, en hetzelfde verzoek opnieuw versturen rondt het vrijgeven af.
Verzoek
curl -X DELETE "https://volai.cz/v1/voice-sms-numbers/+420292000001" \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"released": true
}Foutcodes
- 400
validatione164 in de URL is geen geldig telefoonnummer (schrijf het plusteken letterlijk of als %2B). - 404
not_foundHet nummer voor bellen en sms bestaat niet of hoort niet bij uw account. - 409
invalid_transitionHet nummer wacht op registratie (annuleer het metPOST /v1/voice-sms-numbers/{e164}/cancel, u betaalt niets), of de operator heeft de registratie geweigerd en het nummer is al geannuleerd. - 409
in_progressEen andere bewerking (een activering, een annulering of een andere vrijgave) verwerkt het nummer. Probeer het over een paar minuten opnieuw. - 502
release_failedHet vrijgeven kon nu niet worden voltooid. Het nummer blijftactive, ofreleasing(het kan niet meer worden gebruikt); verstuur hetzelfde verzoek zo meteen opnieuw. De reeds betaalde periode wordt niet terugbetaald.
Gesprekken
GET/v1/calls
De gespreksgeschiedenis, nieuwste eerst. direction (in of out) beperkt de lijst tot één richting. Filters from/to (Unix-ms, startedAt binnen het bereik, from <= to), agentId, flagged (true/false), goal (success/failure/unclear/no_conversation/not_evaluated, of het achterwaarts compatibele unknown - records zonder duidelijk resultaat: goal.result ontbreekt of is unknown (altijd waar voor unclear en not_evaluated, en voor no_conversation alleen als het resultaat unknown is) - zie het veld goal van het gesprek), outcome (agent/transferred/no_answer/other, zie de kolom Facturering van de tabel kind hieronder; een poortbel, kind: "ring", is altijd other, ook als de status no_answer is). Onbekende parameters worden genegeerd. Gesprekken versus conversaties: GET /v1/calls geeft afzonderlijke gespreks-RECORDS terug - elk telefoonbeen is een eigen rij. De tabel /en/calls van het portaal groepeert samenhangende benen (een inkomend agentbeen plus een uitgaand doorverbindbeen) tot één CONVERSATIE - een succesvol doorverbonden gesprek verschijnt in het portaal dus als één rij, maar via de API/MCP als maximaal twee records (het been dat de doorverbinding uitvoerde heeft outcome: "transferred"; het ontvangende been behoudt zijn eigen outcome). Tel gespreksaantallen uit de API met dat in gedachten op.
Verzoek
curl "https://volai.cz/v1/calls?limit=20" \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"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": {
"name": "Jan de Vries",
"coffee_count": 2,
"urgency": "normal"
}
}
],
"hasMore": false,
"nextBefore": null
}Foutcodes
- 400
validationlimit/before moet een positief geheel getal zijn, direction moet "in" of "out" zijn; from moet kleiner dan of gelijk aan to zijn wanneer beide worden verstuurd; agentId mag niet langer zijn dan 64 tekens; flagged/goal/outcome vallen buiten hun toegestane waarden.
POST/v1/calls
Start een uitgaand gesprek naar to. Verstuur precies ÉÉN van: agentId (uw spraakagent voert het uit volgens zijn systemPrompt), from (een directe verbinding tussen twee nummers, zonder agent - een bridge, zie hieronder), of systemPrompt (een proefgesprek zonder eigen nummer - geen aankoop of agentinstelling nodig, zie hieronder). Nooit meer dan één, nooit geen. Bij de variant met agentId kunt u ook variables (eigen waarden voor de prompt) en ringingTimeoutSecs (hoe lang het mag overgaan, zie het kader hieronder) versturen.
Verzoek
curl -X POST "https://volai.cz/v1/calls" \
-H "Authorization: Bearer vk_YOUR_KEY" \
-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}'Vervang vóór het uitvoeren REPLACE_WITH_UNIQUE… door een unieke ID voor deze actie, bijvoorbeeld een UUID, en bewaar die. Gebruik dezelfde ID opnieuw als u hetzelfde verzoek na een time-out herhaalt. Gebruik een nieuwe ID voor een andere actie of een gewijzigde body, ook bij een ander endpoint.
Antwoord
{
"id": "c_9d4e2b7f",
"status": "initiated"
}Foutcodes
- 400
invalid_numberto is geen geldig telefoonnummer. - 400
validationEén van agentId/from/systemPrompt ontbreekt, of er zijn er meer dan één; bij agentId valt variables buiten de limieten (max. 20 sleutels, sleutels van 64 tekens, waarden van 512 tekens) of ringingTimeoutSecs valt buiten het bereik 5-60; bij systemPrompt ontbreekt hij/is hij langer dan 1200 tekens, firstMessage is langer dan 300 tekens, of ringingTimeoutSecs valt buiten het bereik 5-30. - 400
on_dncDat nummer staat op uw bel-mij-niet-lijst (/v1/dnc). - 400
destination_auto_blockedDe bestemming is na herhaalde mislukkingen 30 dagen automatisch geblokkeerd - hef de blokkade op in het portaal (Instellingen) of viaPOST /v1/dnc/{e164}/unblock. - 400
cannot_call_own_numberLusbeveiliging - u kunt uw eigen volai-nummer niet bellen. - 400
cannot_call_volai_numberLusbeveiliging - de bestemming is het volai-nummer van een ander account. - 400
destination_not_allowedEen nummer voor bellen en sms kan alleen Tsjechische nummers (+420) bellen; een nummer in een ander land kan alleen het land van het nummer bellen (zodra bellen daarheen is ingeschakeld, ziecapabilities.outboundTovan het nummer) en Tsjechië.tois een ander nummer. Geef een toegestanetomee, of bel vanaf een agent die een spraaknummer gebruikt. Er wordt niets afgeschreven. - 400
unsupported_for_number_typeDe variantfrom(een gewoon gesprek tussen twee nummers zonder agent) is niet beschikbaar vanaf een nummer voor bellen en sms:fromis uw eigen nummer voor bellen en sms, en alleen een agent belt ervandaan. Geef uw eigen telefoon alsfrommee (een Tsjechisch of Slowaaks nummer), of verstuuragentId. Er wordt niets afgeschreven. - 404
bridge_needs_numberEen bridge (from) heeft minstens één van uw eigen spraaknummers op het account nodig. Een nummer voor bellen en sms telt niet mee: alleen een agent belt ervandaan. Koop een spraaknummer metPOST /v1/numbers. - 409
destination_busyU hebt al een ander gesprek lopen naar hetzelfde nummer. - 404
agent_not_foundagentId bestaat niet of hoort niet bij uw account. - 404
agent_no_numberAan de agent is geen telefoonnummer toegewezen. - 403
agent_suspendedDe volai-beheerder heeft deze agent handmatig opgeschort - er kan nu geen gesprek via worden gevoerd. Een opgeschort account geeft in plaats daarvanaccount_suspended. - 402
insufficient_creditUw tegoed dekt het minimum om een gesprek te starten niet. - 402
subscription_cap_reachedHet account heeft een maandabonnement met het bestedingsplafond, het maandtegoed is op en het opgewaardeerde tegoed dekt de actie niet. Het vervangtinsufficient_creditvoor zo'n account (dezelfde HTTP-status). Er is niets afgeschreven. - 402
spend_cap_reachedHet door u ingestelde maandelijkse bestedingsplafond is bereikt. Er is niets geplaatst of verzonden en er is niets in rekening gebracht. Verhoog of verwijder het plafond metPUTofDELETE /v1/account/spend-cap, of wacht tot de volgende kalendermaand. - 503
capacity_busyAlle uitgaande lijnen zijn nu bezet, probeer het over een minuut opnieuw. Een nummer voor bellen en sms staat slechts één uitgaand gesprek tegelijk toe: loopt er al een vanaf dat nummer, dan bevat het antwoord een headerRetry-Aftermet de seconden tot u het opnieuw kunt proberen. - 503
trial_calls_disabledMet systemPrompt: proefgesprekken zijn tijdelijk uitgeschakeld via een operationele schakelaar - bel in plaats daarvan via uw eigen agent (agentId). - 403
trial_email_unverifiedMet systemPrompt: alleen voor accounts met een geverifieerd e-mailadres. - 500
trial_not_configuredMet systemPrompt: de gedeelde demoagent van volai is niet juist geconfigureerd - een tijdelijke fout aan onze kant. - 502
call_rejectedMet systemPrompt: het spraakplatform weigerde de verbinding rechtstreeks, er is niets afgeschreven. - 429
rate_limitedEen gesprekslimiet, er wordt niets afgeschreven. Met agentId: hoogstens 10 gesprekken per minuut en 1000 per dag per account, of de limiet voor uitgaande minuten van de eigen spraakengine is opgebruikt - het antwoord bevat danRetry-After(seconden tot het zin heeft het opnieuw te proberen). Met systemPrompt: het dagplafond van 3 proefgesprekken per account is opgebruikt, of het gedeelde plafond over alle accounts is tijdelijk verzadigd. - 502
callback_failedMet bridge (from): de telefoonoperator weigerde het verbindingsverzoek rechtstreeks, het gesprek kwam nooit tot stand. Er is geen tegoed afgeschreven. - 502
engine_unavailableMet agentId bij een agent op de eigen spraakengine van volai: de engine reageert nu niet (een storing, geen limiet) - probeer het zo meteen opnieuw en neem bij aanhoudende problemen contact op met support met de requestId uit de fout. Als de engine het verzoek accepteerde maar zijn antwoord niet op tijd aankwam, kan het gesprek toch hebben plaatsgevonden: het blijft in de geschiedenis staan alsinitiated, we vullen het werkelijke resultaat en de prijs in, en hetzelfde nummer blijft ongeveer 15 minuten vergrendeld (destination_busy), zodat u het niet twee keer belt. Een opgebruikte limiet voor uitgaande gesprekken is in plaats daarvan 429rate_limited.
Eigen variabelen en beltijd
variables is een object van string naar string dat de agent als dynamische waarden ontvangt - verwijs ernaar in de prompt als {{jmeno_zakaznika}}. Limieten: maximaal 20 sleutels, sleutels tot 64 tekens, waarden tot 512 tekens. Een sleutel mag alleen letters, cijfers en onderstrepingstekens bevatten, mag niet met een cijfer beginnen en mag niet beginnen met het voorvoegsel system__ of volai_ (beide zijn gereserveerd en een verzoek met zo'n sleutel wordt geweigerd). De namen attempt_id, caller_number, called_number, trial_prompt en trial_first_message zijn gereserveerd - die vullen we zelf in, en alle waarden die u onder die namen verstuurt worden weggegooid.
ringingTimeoutSecs is 5 tot 60 seconden, standaard 25. Zodra die verstreken is, eindigt een onbeantwoord gesprek en krijgt het detail ervan endReason: "no_answer". Langer laten overgaan betekent een betere kans dat de klant opneemt, maar ook dat de uitgaande slot langer wordt bezet gehouden.
Zonder agent: twee nummers rechtstreeks verbinden
Verstuur from in plaats van agentId - uw eigen nummer, of een ander nummer van de klant. volai belt eerst from, en zodra iemand opneemt, belt het to. Beide benen worden apart in rekening gebracht tegen 0,92 kr/min (≈ 0,04 EUR). Beantwoordt de operator de opdracht niet (een netwerkstoring), dan geven we het gesprek terug als gestart (initiated): het kan zijn doorgekomen, en het werkelijke resultaat en de prijs worden ingevuld door de synchronisatie met de operator. Probeer het niet meteen opnieuw - hetzelfde nummer blijft ongeveer 10 minuten vergrendeld.
curl -X POST https://volai.cz/v1/calls \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"to": "+420777123456", "from": "+420601234567"}'Een proefgesprek zonder eigen nummer
Verstuur in plaats van agentId/from een systemPrompt (instructies voor de agent, alleen voor dit ene gesprek, tot 1200 tekens) en optioneel firstMessage (de openingszin, tot 300 tekens). Het gesprek gaat uit vanaf het gedeelde demonummer van volai, niet vanaf uw eigen nummer - het antwoord bevat dus ook trial: true en from (het demonummer). U kunt voor deze variant geen voiceId versturen, het draait op de standaardstem. ringingTimeoutSecs heeft een lagere bovengrens van 5 tot 30 (standaard 25) - het houdt een slot uit de gedeelde pool vast.
curl -X POST https://volai.cz/v1/calls \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+420777123456",
"systemPrompt": "U bent de receptie van café Nula. U neemt bestellingen om af te halen aan en houdt het kort en vriendelijk.",
"firstMessage": "Goedendag, u spreekt met de proefagent van volai, waarmee kan ik u helpen?"
}'{
"id": "c_2f7a91mn",
"status": "initiated",
"trial": true,
"from": "+420266266641"
}Alleen voor accounts met een geverifieerd e-mailadres, hoogstens 3 gesprekken per account per 24 uur (plus een gedeeld plafond over alle accounts, zodat een piek het demonummer niet overspoelt). Wordt precies als een gewoon gesprek in rekening gebracht, inclusief de toeslag voor de agent - geen korting. Wie meer nodig heeft, koopt een eigen nummer (POST /v1/numbers) en maakt een agent aan (POST /v1/agents) - daar geldt dat plafond niet.
Zijn proefgesprekken tijdelijk uitgeschakeld (een operationele schakelaar), dan geeft systemPrompt een leesbare fout (trial_calls_disabled) in plaats van het gesprek te voeren; gebruik ondertussen uw eigen agent (agentId).
POST/v1/voice-messages
Belt to, leest text woordelijk voor (1 tot 500 tekens, repeat 1 tot 3 keer, standaard 1) en hangt op. Geen AI-agent en geen eigen nummer: de gebelde ziet een gedeeld volai-nummer, en terugbellen bereikt de demolijn van volai. Alleen de uitgaande minuut wordt per seconde in rekening gebracht (0,92 kr/min exclusief btw); tekst-naar-spraak is inbegrepen en er geldt geen toeslag voor een agent. Een gesprek dat niemand beantwoordt kost 0. Het antwoord komt direct nadat het gesprek is geplaatst; lees het resultaat in GET /v1/calls/{id} (kind: "voice_message", blok voiceMessage) of uit de webhook.
Verzoek
curl -X POST "https://volai.cz/v1/voice-messages" \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: REPLACE_WITH_UNIQUE_POST_V1_VOICE_MESSAGES_ID" \
-d '{"to":"+420777123456","text":"Let op: server api-1 reageert al vijf minuten niet. Controleer de status ervan.","repeat":2}'Vervang vóór het uitvoeren REPLACE_WITH_UNIQUE… door een unieke ID voor deze actie, bijvoorbeeld een UUID, en bewaar die. Gebruik dezelfde ID opnieuw als u hetzelfde verzoek na een time-out herhaalt. Gebruik een nieuwe ID voor een andere actie of een gewijzigde body, ook bij een ander endpoint.
Antwoord
{
"id": "c_k4m8p2q7r1s5",
"status": "ringing",
"from": "+420266266641"
}Foutcodes
- 400
validationtooftextontbreekt,textis leeg of langer dan 500 tekens,repeatis geen geheel getal van 1 tot 3, de body bevat een onbekend veld (bijvoorbeeldringingTimeoutSecs, dat een spraakbericht niet heeft), of de body is geen geldige JSON. - 400
invalid_numbertois geen geldig telefoonnummer. - 400
unsupported_countrytois geen Tsjechisch (+420) of Slowaaks (+421) nummer. - 400
blocked_destinationtois een premium-tariefnummer, dus gesprekken ernaartoe worden geblokkeerd en u krijgt geen onbedoeld hoge rekening. - 400
on_dncDat nummer staat op uw bel-mij-niet-lijst (/v1/dnc) of onder de automatische blokkade van 30 dagen na herhaalde onbeantwoorde gesprekken (destination_auto_blocked). - 400
destination_auto_blockedDat nummer staat op uw bel-mij-niet-lijst (/v1/dnc) of onder de automatische blokkade van 30 dagen na herhaalde onbeantwoorde gesprekken (destination_auto_blocked). - 400
cannot_call_own_numberLusbeveiliging -tois een van uw eigen volai-nummers. - 400
cannot_call_volai_numberLusbeveiliging -tois het volai-nummer van een ander account of de demolijn van volai. - 402
insufficient_creditUw tegoed dekt het minimum om een gesprek te starten niet, of het account heeft zijn eerste opwaardering van het tegoed nog niet gedaan (het welkomsttegoed dekt spraakberichten niet). Waardeer op en probeer het opnieuw. Er wordt niets afgeschreven. - 402
subscription_cap_reachedHet account heeft een maandabonnement met het bestedingsplafond, het maandtegoed is op en het opgewaardeerde tegoed dekt de actie niet. Het vervangtinsufficient_creditvoor zo'n account (dezelfde HTTP-status). Er is niets afgeschreven. - 402
spend_cap_reachedHet door u ingestelde maandelijkse bestedingsplafond is bereikt. Er is niets geplaatst of verzonden en er is niets in rekening gebracht. Verhoog of verwijder het plafond metPUTofDELETE /v1/account/spend-cap, of wacht tot de volgende kalendermaand. - 403
email_unverifiedAlleen accounts met een geverifieerd e-mailadres kunnen een spraakbericht versturen. Verifieer uw e-mailadres en probeer het opnieuw. Er wordt niets afgeschreven. - 409
destination_busyEen ander gesprek of spraakbericht naar hetzelfde nummer loopt nog. Het nummer blijft vergrendeld zolang het gesprek duurt, probeer het dus zo meteen opnieuw. - 429
rate_limitedDe gesprekslimiet of het dagplafond van 200 spraakberichten per account is opgebruikt, er wordt niets afgeschreven. Probeer het later opnieuw, of schrijf naar support en we verhogen het plafond. - 502
engine_unavailableDe spraakengine van volai accepteerde het gesprek nu niet. Er wordt niets afgeschreven, probeer het zo meteen opnieuw. Probeer niet blindelings opnieuw zonder eenIdempotency-Key. - 503
capacity_busyAlle uitgaande lijnen zijn nu bezet. Er wordt niets afgeschreven, probeer het over een minuut opnieuw. - 503
ledger_unavailableHet saldo kon nu niet worden gelezen, probeer het zo meteen opnieuw. Er wordt niets afgeschreven. - 503
voice_messages_disabledSpraakberichten zijn nog niet ingeschakeld. Er wordt niets afgeschreven, probeer het later opnieuw. - 503
voice_message_unavailableDe spraakengine kan nu geen spraakberichten accepteren. Het gesprek is niet geplaatst en er wordt niets afgeschreven, probeer het zo meteen opnieuw.
Spraakbericht: bellen, een tekst voorlezen, ophangen
Voor waarschuwingen en berichten waarop niemand antwoordt: een incident voor een dienstdoende engineer, een herinnering aan een deadline, een bezorgbericht. POST /v1/voice-messages belt, leest text woordelijk voor (in het Tsjechisch, zonder dat {{...}}-variabelen worden ingevuld), laat een korte pauze tussen de voorlezingen en hangt op. U hebt geen agent en geen eigen nummer nodig. Gebruik voor een gesprek waarin de gebelde moet antwoorden POST /v1/calls met een agent.
De prijs is alleen de uitgaande minuut, per seconde in rekening gebracht (0,92 kr/min exclusief btw); tekst-naar-spraak is inbegrepen en er geldt geen toeslag voor een agent, dus een kort bericht kost tientallen hellers. Een gesprek dat niemand beantwoordt kost 0. Neemt de voicemail van de gebelde op, dan wordt het bericht daar opgenomen en wordt het gesprek als elk verbonden gesprek in rekening gebracht.
De gebelde ziet een gedeeld volai-nummer, niet het uwe. Wie het terugbelt, bereikt de demolijn van volai. Het account heeft een geverifieerd e-mailadres en minstens één opwaardering van het tegoed nodig, en er geldt een dagplafond van 200 spraakberichten per account.
Lees het resultaat uit GET /v1/calls/{id}: kind is voice_message en het blok voiceMessage bevat repeatPlayed (hoe vaak het bericht is gehoord) en completed (true wanneer het tot het einde is voorgelezen en volai ophing, false wanneer de gebelde eerder ophing). Dezelfde velden komen binnen in de webhook call.completed (call.no_answer wanneer niemand opnam). De gespreksamenvatting per e-mail wordt voor een spraakbericht niet verstuurd. In de statistieken valt een afgeleverd bericht onder categorie other, een onbeantwoord bericht blijft no_answer.
{
"call": {
"id": "c_k4m8p2q7r1s5",
"direction": "out",
"from": "+420266266641",
"to": "+420777123456",
"status": "completed",
"kind": "voice_message",
"durationSecs": 17.4,
"voiceMessage": {
"text": "...",
"repeat": 2,
"repeatPlayed": 2,
"completed": true
}
}
}Gebruik dit niet voor ongevraagde marketing. Geautomatiseerde gesprekken zonder toestemming van de gebelde zijn illegaal. Een spraakbericht is bedoeld voor operationele berichten aan mensen die ze willen ontvangen.
POST/v1/rings
Belt to vanaf een van uw eigen spraaknummers (from) en verbindt het gesprek nooit: het overgaan stopt na ongeveer ringSecs seconden (een geheel getal van 3 tot 30, standaard 10), of eerder wanneer het doel het weigert; de carrier beëindigt het overgaan met een vertraging tot 3 seconden, dus de beltijd in het record (ring.rangSecs) is iets langer. Bedoeld voor GSM-poorten, sloten en kasten die openen wanneer ze het bellende nummer herkennen. Het doel ziet precies from als beller (zonder from wordt uw eerste actieve nummer gebruikt), dus de poort moet het op zijn lijst met toegestane nummers hebben. Het antwoord komt METEEN nadat de carrier de opdracht accepteerde, binnen ongeveer een seconde en voordat het overgaan is afgelopen: status is initiated en er is nog geen resultaat. Het resultaat komt binnen via webhook (call.no_answer voor rang en ended_early, call.completed voor answered) en staat in GET /v1/calls/{id} als ring.outcome (met ?waitSecs= wacht de server tot de poging is afgelopen). De waarde van ring.outcome is rang, ended_early, answered of unknown (de laatste terwijl het resultaat nog wordt afgerekend op basis van de gegevens van de carrier). Eén poging kost 0,25 kr exclusief btw, afgeschreven zodra de carrier de opdracht accepteerde; neemt het doel op, dan hangt volai direct op en worden de verbonden seconden bovendien als uitgaand gesprek in rekening gebracht. Beltonen tellen niet mee voor de automatische bestemmingsblokkade, maar uw bel-mij-niet-lijst geldt wel. Limieten per account, los van de gesprekslimieten: 30 beltonen per minuut, 500 per dag en 120 per uur naar één nummer. Ondersteunt de header Idempotency-Key.
Verzoek
curl -X POST "https://volai.cz/v1/rings" \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: REPLACE_WITH_UNIQUE_POST_V1_RINGS_ID" \
-d '{"to":"+420777123456","from":"+420601234567","ringSecs":10}'Vervang vóór het uitvoeren REPLACE_WITH_UNIQUE… door een unieke ID voor deze actie, bijvoorbeeld een UUID, en bewaar die. Gebruik dezelfde ID opnieuw als u hetzelfde verzoek na een time-out herhaalt. Gebruik een nieuwe ID voor een andere actie of een gewijzigde body, ook bij een ander endpoint.
Antwoord
{
"id": "c_x7f2k9a1b3c4",
"status": "initiated",
"kind": "ring",
"from": "+420601234567",
"to": "+420777123456",
"requestedSecs": 10,
"priceHal": 25
}Foutcodes
- 400
validationtoontbreekt,ringSecsis geen geheel getal van 3 tot 30 (een waarde buiten het bereik wordt geweigerd, niet begrensd), of de body is geen geldige JSON. - 400
invalid_numbertois geen geldig telefoonnummer. - 400
unsupported_countrytois geen Tsjechisch (+420) of Slowaaks (+421) nummer. - 400
blocked_destinationtois een premium-tariefnummer, dus beltonen ernaartoe worden geblokkeerd en u krijgt geen onbedoeld hoge rekening. - 400
on_dncDat nummer staat op uw bel-mij-niet-lijst (/v1/dnc). De automatische blokkade van 30 dagen geldt niet voor beltonen, de handmatige lijst wel. - 400
cannot_call_own_numberLusbeveiliging -tois een van uw eigen volai-nummers. - 400
cannot_call_volai_numberLusbeveiliging -tois het volai-nummer van een ander account. - 400
unsupported_for_number_typefromis uw eigen nummer voor bellen en sms, en alleen een agent belt ervandaan, dus het kan niet overgaan. Laatfromweg (uw eerste actieve spraaknummer wordt gebruikt), of gebruik een spraaknummer. Er wordt niets afgeschreven. - 402
insufficient_creditUw tegoed dekt het minimum om een gesprek te starten niet. Er wordt niets afgeschreven. - 402
subscription_cap_reachedHet account heeft een maandabonnement met het bestedingsplafond, het maandtegoed is op en het opgewaardeerde tegoed dekt de actie niet. Het vervangtinsufficient_creditvoor zo'n account (dezelfde HTTP-status). Er is niets afgeschreven. - 402
spend_cap_reachedHet door u ingestelde maandelijkse bestedingsplafond is bereikt. Er is niets geplaatst of verzonden en er is niets in rekening gebracht. Verhoog of verwijder het plafond metPUTofDELETE /v1/account/spend-cap, of wacht tot de volgende kalendermaand. - 404
from_number_not_ownedfromis geen actief spraaknummer van uw account. De lijst staat inGET /v1/numbers. - 404
ring_needs_numberHet account heeft geen actief spraaknummer om vanaf te bellen. Een nummer voor bellen en sms telt niet mee: alleen een agent belt ervandaan. Koop een spraaknummer metPOST /v1/numbers. - 409
destination_busyEen andere beltoon of een gesprek naar hetzelfde nummer loopt nog. Het nummer blijft vergrendeld gedurende de beltijd plus ongeveer 30 seconden, probeer het daarna dus opnieuw. - 429
rate_limitedBeltoonlimiet bereikt, er wordt niets afgeschreven: hoogstens 30 per minuut en 500 per dag per account, en 120 per uur naar één nummer. Los van de gesprekslimieten. - 502
callback_failedDe carrier weigerde de beltoonopdracht meteen, dus er is niets gebeld. Uw tegoed is niet belast. - 503
ledger_unavailableHet saldo kon nu niet worden gelezen, probeer het zo meteen opnieuw. Er wordt niets afgeschreven.
Een poort laten overgaan: open hem met één verzoek
Een GSM-poort, slot of kast heeft een eigen telefoonnummer: wanneer een nummer van zijn lijst met toegestane nummers belt, weigert hij het gesprek en opent. POST /v1/rings doet dat met één verzoek - het belt de poort vanaf uw spraaknummer from en beëindigt het overgaan na ongeveer ringSecs seconden, of eerder wanneer de poort het weigert. Voeg from toe aan de lijst met toegestane nummers van de poort: dat is het nummer dat hij zal zien.
Het antwoord (status: "initiated") komt direct na de opdracht. Hoe de poging eindigde, staat in ring.outcome, in de webhookgebeurtenis en in GET /v1/calls/{id}: rang (het ging over totdat de gevraagde tijd om was), ended_early (het gesprek eindigde eerder, bijvoorbeeld omdat de poort het weigerde), answered (het doel nam op, volai hangt direct op en de verbonden seconden worden bovendien als uitgaand gesprek in rekening gebracht) of unknown (het resultaat kon niet direct worden vastgesteld; het wordt binnen enkele minuten ingevuld op basis van de gegevens van de carrier).
{
"call": {
"id": "c_x7f2k9a1b3c4",
"direction": "out",
"from": "+420601234567",
"to": "+420777123456",
"status": "no_answer",
"startedAt": 1756111640000,
"durationSecs": 0,
"priceHal": 25,
"source": "api",
"kind": "ring",
"ring": { "requestedSecs": 10, "outcome": "ended_early", "rangSecs": 2 },
"note": null,
"flag": null,
"handledAt": null,
"rating": null,
"goal": null,
"hasConversation": false,
"endReason": "no_answer"
}
}Een onbeantwoorde beltoon (rang, ended_early) bereikt uw integratie als gebeurtenis call.no_answer, net als elk ander onbeantwoord uitgaand gesprek; answered komt aan als call.completed. Eén uitzondering: wanneer de carrier het opnemen pas in zijn gespreksrecord (CDR) bevestigt, nadat volai de beltoon al als onbeantwoord had afgesloten, komt alleen call.no_answer aan, en het record in GET /v1/calls/{id} verandert later in completed met ring.outcome: "answered" en de beantwoorde seconden in rekening gebracht; er wordt geen tweede gebeurtenis verstuurd. De body van de gebeurtenis bevat kind: "ring" en een blok ring (requestedSecs, outcome, rangSecs), dus kind vertelt u dat de poort het gesprek weigerde in plaats van dat niemand opnam. Elke gespreksgebeurtenis bevat kind; het blok ring staat alleen bij beltonen. Een resultaat unknown blijft initiated zonder gebeurtenis totdat de gegevens van de carrier het invullen; meldt de carrier de poging nooit, dan wordt ze na 24 uur als failed afgesloten.
Beltonen tellen niet mee voor de automatische bestemmingsblokkade (alleen uw handmatige bel-mij-niet-lijst geldt) en hebben eigen limieten, los van de gesprekslimieten: 30 per minuut en 500 per dag per account, en 120 per uur naar één nummer. Het nummer blijft vergrendeld terwijl het overgaat plus ongeveer 30 seconden: een andere beltoon of een gesprek ernaartoe geeft in die tijd destination_busy. Verstuur bij een nieuwe poging na een netwerkfout de header Idempotency-Key, zodat een tweede poging niet wordt besteld en niet wordt afgerekend.
GET/v1/calls/active
Leest elk geconfigureerd native engineknooppunt voor één eigen agentId. Dit is een live momentopname, anders dan de gespreksgeschiedenis. complete betekent dat alle engineantwoorden geldig waren en de eigendomsmetadata volledig was. outboundCount omvat onopgeloste gesprekken; inboundCount telt terugbelacties apart. Elke uitgaande rij heeft een ondoorzichtige nativeRef, startedAt (Unix-ms) en resolved. Alleen geverifieerde taakgesprekken bevatten callId, taskId, itemId en phoneSha256. Een ontbrekende koppeling blijft zichtbaar met resolved: false. checkedAt is het ISO-tijdstip waarop de momentopname klaar was en enginesChecked het aantal knooppunten. Bewijs alleen geen uitgaande gesprekken wanneer complete: true, outboundCount: 0 en unresolvedOutboundCount: 0; fouten of een onvolledige momentopname bewijzen nooit nul. Dit endpoint dekt alleen native telefoongesprekken, geen websessies of andere providers. Gebruikt de bestaande gedeelde snelheidslimiet per API-sleutel.
Verzoek
curl 'https://volai.cz/v1/calls/active?agentId=ag_example' -H 'Authorization: Bearer vk_YOUR_KEY'Antwoord
{
"data": {
"agentId": "ag_example",
"complete": true,
"checkedAt": "2026-10-09T17:59:00.000Z",
"enginesChecked": 2,
"outboundCount": 0,
"inboundCount": 0,
"unresolvedOutboundCount": 0,
"outbound": []
}
}Foutcodes
- 400
validationDe verwachte bindingen of de agentquery ontbreken, zijn ongeldig of komen niet meer overeen met het gesprek. - 404
agent_not_foundDe agent bestaat niet of hoort bij een ander account. - 502
engine_unavailableDe native toestand kon niet worden geverifieerd; ga er niet van uit dat gesprekken zijn beëindigd. - 503
engine_not_configuredDe verbinding met de native engine is niet geconfigureerd.
POST/v1/calls/{id}/hangup
Beëindigt één eigen native uitgaand taakgesprek. Stuur alle vier de verwachte bindingen mee: expectedAgentId, expectedTaskId, expectedItemId en expectedPhoneSha256 (SHA-256 in kleine letters van de exacte E.164-bestemming). De server controleert taakgeschiedenis, dispatchcontext, native room, tenant, agent, richting en starttijd voordat hij één native ophangsignaal verstuurt. Een permanente claim per gesprek voorkomt dubbele providerverzoeken, ook bij gelijktijdige verzoeken en timeouts; een Idempotency-Key is niet nodig. Het antwoord bevat callId, outcome en endConfirmed: hung_up betekent dat de native engine het exacte gesprek heeft bevestigd, already_ended vereist een opgeslagen eindstatus plus afwezigheid op alle knooppunten, en unknown betekent nooit succes. Bekijk na een onbekend resultaat de live-activiteit en de opgeslagen gespreksstatus; dit endpoint herhalen leest alleen terug en verstuurt nooit nog een ophangsignaal. Controleer na het beëindigen opnieuw de actieve gesprekken. Inkomende gesprekken, andere tenants, losse gesprekken en andere providers worden geweigerd. Campagnebereik, belvensters en deadlines horen bij de belcontroller; dit endpoint wijzigt de facturering of de taakstatus niet.
Verzoek
curl -X POST https://volai.cz/v1/calls/c_example/hangup -H 'Authorization: Bearer vk_YOUR_KEY' -H 'Content-Type: application/json' -d '{"expectedAgentId":"ag_example","expectedTaskId":"task_example","expectedItemId":"item_example","expectedPhoneSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}'Antwoord
{
"data": {
"callId": "c_example",
"outcome": "hung_up",
"endConfirmed": true
}
}Foutcodes
- 400
validationDe verwachte bindingen of de agentquery ontbreken, zijn ongeldig of komen niet meer overeen met het gesprek. - 404
call_not_foundHet gesprek bestaat niet of hoort niet bij uw account. - 502
engine_unavailableDe native toestand kon niet worden geverifieerd; ga er niet van uit dat gesprekken zijn beëindigd. - 503
engine_not_configuredDe verbinding met de native engine is niet geconfigureerd.
GET/v1/calls/{id}
Het detail van een gesprek. Optioneel laat waitSecs (zie het kader hieronder) de server op het resultaat wachten in plaats van dat u pollt. Bij gesprekken met een agent bevat het ook transcript (een transcriptie per beurt, elke beurt met een optioneel veld timeInCallSecs - seconden sinds het begin van het gesprek, afwezig als de provider die beurt niet van een tijdstempel voorzag; een agentbeurt die de beller onderbrak heeft bovendien interrupted: true en zijn message is nog steeds de volledige geplande tekst, alleen bij de eigen engine van volai) en summary (een korte samenvatting) - zie Webhooks voor dezelfde vorm die automatisch wordt afgeleverd zodra het gesprek eindigt. Zowel de lijst als het detail bevatten ook answeredBy (alleen UITGAANDE gesprekken: human, voicemail, ivr (een geautomatiseerd spraakmenu nam op; de engine hangt direct op), of unknown wanneer de engine geen besluit nam of de transcriptie ontbreekt; inkomende gesprekken laten het veld weg, omdat een kort antwoord van de beller geen voicemail is). De engine beslist met human, voicemail en ivr; unknown betekent dat de engine geen besluit nam voordat zijn detectievenster sloot, en sinds 1.33.0 blijft het unknown (eerder verving de schatting op basis van de transcriptie dat, en die schatting beschouwde een kort antwoord als "Ja." na de inleiding als een voicemail). De schatting op basis van de transcriptie (human of voicemail, unknown voor een lege transcriptie; ze detecteert nooit een spraakmenu) geldt alleen voor gesprekken zonder waarde van de engine. Een uitgaand gesprek dat door de eigen spraakengine van volai wordt afgehandeld, bevat ook voicemailReason (phrase, long_monologue, beep, human_reply, ivr_phrase of digits - human_reply betekent dat een persoon opnam met een antwoord zo kort dat de detector het noteerde (niet dat het een voicemail was), ivr_phrase is een zin van een spraakmenu, digits een begroeting van de carrier die het gebelde nummer cijfer voor cijfer voorleest), voicemailMessageLeft (de engine las het ingestelde bericht in de voicemail voor) en voicemailDetectedAtSecs (seconden vanaf het opnemen tot de detector van de engine tot een besluit kwam - ook bij een gesprek dat een persoon beantwoordde) - alle drie de velden komen alleen van de engine; de classificatie van ElevenLabs op basis van de transcriptie (answeredBy zonder expliciete waarde) kent de reden en het tijdstip niet. Een inkomend of uitgaand gesprek op de engine bevat ook optOutRequested: true wanneer de deelnemer aan de andere kant uitdrukkelijk vroeg niet meer te worden gebeld (bijvoorbeeld "bel mij niet meer", zelfs in de laatste seconden na het afscheid) - het nummer staat dan automatisch op de bel-mij-niet-lijst van het account (GET /v1/dnc) en het herhaalbeleid van de taak plant geen verdere poging in; een gesprek zonder zo'n antwoord heeft zo'n sleutel niet. Het bevat ook endReason (completed, no_answer, busy, rejected, capacity, blocked, loop_guard, credit_blocked, suspended, engine_error, caller_loop, failed), en kind (agent, bridge, relay, inbound, transfer, sip, ring, voice_message) - zie de tabel hieronder. Een spraakbericht (kind: "voice_message") bevat ook een blok voiceMessage (text, repeat, en zodra het gesprek is geëindigd repeatPlayed en completed) - zie POST /v1/voice-messages. Een beltoon (kind: "ring") bevat ook een blok ring (requestedSecs, outcome, rangSecs) - zie POST /v1/rings. De golf met tools en opnames voegde nog twee velden toe: data (wat de agent uit het gesprek vastlegde volgens zijn dataFields; een waarde null betekent dat het niet werd genoemd) en hasRecording (het gesprek heeft een opname die kan worden gedownload, zie het endpoint hieronder). Oudere gesprekken hebben deze velden niet en ze ontbreken in het antwoord. durationSecs is in seconden; bij gesprekken die door de eigen engine van volai worden afgehandeld kan het een decimaal getal zijn (afgerond op twee decimalen). Het antwoord bevat de eigen aantekeningen van de accounteigenaar, altijd aanwezig, null wanneer niet ingesteld: note (string), flag ({reason, flaggedAt} | null), handledAt (Unix-ms of null) en goal ({result: "success"|"failure"|"unknown", rationale, reason} | null, pas ingevuld zodra het doel van de agent is beoordeeld; reason is "no_caller_speech"|"evaluation_error"|null en zegt waarom het resultaat "unknown" werd - null voor een werkelijk onduidelijk resultaat) - zie PATCH /v1/calls/{id} hieronder. Het antwoord bevat ook altijd hasConversation (boolean | null) - of het gesprek werkelijk een conversatie met de beller had; false voor een gesprek dat niet tot stand kwam, voicemail of een spraakmenu, null voor een ouder record zonder genoeg signaal om te beslissen. Een gesprek van een agent met fasen (plan van workflowknooppunten) dat door de eigen spraakengine van volai wordt afgehandeld (provider: "engine") bevat ook workflowPath - zie het onderdeel Gespreksfasen (workflow) onder agents hierboven; een gesprek van een agent die op ElevenLabs draait bevat het veld nooit, ook al doorliep het fasen, omdat alleen de post-call van de engine het schrijft. Een gesprek dat door een enginebuild vanaf 15 okt 2026 wordt afgehandeld bevat in het detail (niet in de lijst) ook een blok engine met protectedUtterances en protectedUtterancesCount: uitingen van de persoon die opnam en die de engine tijdens het begroetingsvenster liet vallen - wat die zei terwijl de agent zijn eerste zin uitsprak ("Hallo?", zijn naam, of alles binnen firstMessageProtectSecs) en de engine bewust niet overnam, dus het staat NIET in de transcriptie en de agent reageerde er niet op; een transcriptie met alleen de agent en een niet-lege lijst betekent dus dat de persoon wel opnam en sprak. Elk item bevat timeInCallSecs (begin van de spraak in de opname, afwezig wanneer de engine het niet weet), atSecs (wanneer de engine klaar was met horen, seconden vanaf het begin van het gesprek, altijd), message (tot 200 tekens), words en reason (first_message_protect, greeting_courtesy of greeting_identification; open lijst). Hoogstens 10 items, protectedUtterancesCount telt ze allemaal; een gesprek waarbij niets is gevallen bevat [] en 0; oudere gesprekken, agents die op ElevenLabs draaien en gesprekken die door een herstart van de engine werden afgekapt (endReason: engine_error, webhook reason: unfinished; de engine heeft hun begroetingsvenster niet gemeten) hebben helemaal geen sleutel engine. Hetzelfde blok staat in de webhook call.*.
Verzoek
curl https://volai.cz/v1/calls/c_8f2ac1d4 \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"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": {
"name": "Jan de Vries",
"coffee_count": 2,
"urgency": "normal"
},
"transcript": [
{ "role": "agent", "message": "Goedendag, café Nula, waarmee kan ik u helpen?", "timeInCallSecs": 0 },
{ "role": "caller", "message": "Ik wil graag twee latte's bestellen om mee te nemen.", "timeInCallSecs": 4 },
{ "role": "agent", "message": "Prima, twee latte's om af te halen, ze staan over vijftien minuten klaar.", "timeInCallSecs": 9 }
],
"summary": "De beller bestelde twee latte's om mee te nemen, af te halen over 15 minuten."
}
}Foutcodes
- 404
call_not_foundHet gesprek bestaat niet of hoort niet bij uw account. - 400
validationwaitSecs is geen geheel getal van 0 tot 45.
Wacht op het resultaat in plaats van te pollen (waitSecs)
Roep direct na POST /v1/calls GET /v1/calls/{id} aan met waitSecs (0 tot 45, standaard 0) - de server antwoordt zodra het gesprek een eindstatus bereikt (completed/failed/missed/no_answer), waarbij transcript en summary direct worden teruggegeven, of zodra waitSecs verstrijkt, wat het eerst gebeurt.
curl -X GET "https://volai.cz/v1/calls/c_9d4e2b7f?waitSecs=30" \
-H "Authorization: Bearer vk_YOUR_KEY"{
"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",
"engine": {
"protectedUtterances": [
{ "timeInCallSecs": 1.2, "atSecs": 2.4, "message": "Haló?", "words": 1, "reason": "greeting_courtesy" }
],
"protectedUtterancesCount": 1
}
}
}Als de tijd om is voordat het gesprek eindigt, is stillRunning true en bevatten de velden de huidige (niet-definitieve) toestand - het geeft nooit een fout. Probeer GET (of de MCP get_call) opnieuw, desgewenst met een hogere waitSecs.
{
"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
De opname van het gesprek. Anders dan de rest van de API is dit antwoord geen JSON - het formaat van de body volgt het content-type van het feitelijke antwoord (de extensie in content-disposition komt ermee overeen): audio/mpeg (MP3, 128 kbps, 16 kHz mono) voor een agent op ElevenLabs, audio/ogg momenteel voor een agent op de eigen engine van volai. Het formaat wordt nooit omgezet - ga uit van die header, niet van een aangenomen type. We bewaren zelf geen kopie van de opname: de stream loopt vanaf het spraakplatform rechtstreeks door ons heen, en we controleren alleen dat het gesprek bij uw account hoort.
?stahnout=1 zet content-disposition om naar attachment (de browser slaat het bestand op in plaats van het af te spelen) - de bestandsextensie erin (.mp3 / .ogg, anders .bin) komt altijd overeen met het feitelijke content-type hierboven. Deze route biedt geen bereiken (Range) - ze is bedoeld voor machine-integraties die het hele bestand downloaden. Alleen gesprekken die worden afgehandeld door een agent met recordCalls ingeschakeld worden opgenomen, en we bewaren ze 90 dagen na het gesprek.
Verzoek
curl https://volai.cz/v1/calls/c_8f2ac1d4/recording \
-H "Authorization: Bearer vk_YOUR_KEY" \
-OJFoutcodes
- 404
call_not_foundHet gesprek bestaat niet of hoort niet bij uw account. - 404
no_recordingHet gesprek heeft geen opname - het werd niet door een agent afgehandeld, of opnemen was uitgeschakeld. - 410
recording_expiredEr was een opname, maar er zijn 90 dagen verstreken en ze is verwijderd. De transcriptie blijft bestaan.
PATCH/v1/calls/{id}
De eigen aantekening van de accounteigenaar bij een gesprek - een notitie, de agent markeren met een reden, en handmatig "afgehandeld". Minstens één van de drie velden moet aanwezig zijn. note (string of null, max. 2000 tekens) - null wist de notitie. flag ({reason} max. 500 tekens, of null) - een markering instellen WIST handledAt (een gemarkeerd gesprek is niet langer "afgehandeld"); flag: null wist de markering zonder handled te wijzigen. handled (boolean) - true zet handledAt op nu, false zet het terug op null. flag (niet-null) en handled: true kunnen niet allebei in HETZELFDE verzoek worden ingesteld (400 validation) - markeren en bevestigde afhandeling sluiten elkaar uit; verstuur ze in twee aanroepen.
Verzoek
curl -X PATCH https://volai.cz/v1/calls/c_8f2ac1d4 \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"note": "Volal kvůli reklamaci, přeposlat na podporu.", "flag": {"reason": "Agent nerozuměl dotazu na fakturu."}}'Antwoord
{
"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": { "name": "Jan de Vries" },
"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": "De beller bestelde twee latte's om mee te nemen, af te halen over 15 minuten.", "reason": null },
"hasConversation": true
}
}Foutcodes
- 400
validationGeen vannote/flag/handledstaat in de body,noteis langer dan 2000 tekens,flag.reasonontbreekt of is langer dan 500 tekens, of de body steltflag(niet-null) samen methandled: truein. - 404
call_not_foundHet gesprek bestaat niet of hoort niet bij uw account.
Gespreksstatus: het veld status
status is het enige veld van een gesprek dat in de loop van de tijd verandert. Het kan nog veranderen tijdens de eerste vijf minuten nadat het gesprek begint; daarna is het definitief - vier van deze toestanden zijn eindtoestanden.
| status | Betekenis |
|---|---|
initiated | Het gesprek is geplaatst maar gaat nog niet over. |
ringing | Het gaat over bij de gebelde. |
in_progress | Het gesprek loopt. |
completed | Eindtoestand. Een verbonden gesprek dat normaal eindigde - verwacht bij de overgrote meerderheid durationSecs > 0 (uitzondering: een gesprek met endReason: credit_blocked, suspended of spend_cap hieronder heeft zowel een duur als een prijs van 0). Een gesprek met endReason: caller_loop (een geautomatiseerde beller aan de andere kant, tot vijf minuten) heeft ook een prijs van 0, maar wel een duur. |
no_answer | Eindtoestand. Een uitgaand gesprek ging over en de gebelde nam niet op. Bij een beltoon (kind: "ring") is het het normale resultaat: de poort weigerde het gesprek, of het overgaan liep tot het einde van de gevraagde tijd (ring.outcome maakt het onderscheid). |
missed | Eindtoestand. Een inkomend gesprek bleef onbeantwoord (hieronder valt ook een gesprek dat werd afgewezen omdat alle lijnen bezet waren - endReason zegt welk). |
failed | Eindtoestand. Meestal kwam het gesprek helemaal niet tot stand - het platform weigerde het of het netwerk viel uit. Uitzondering (kaart K6, audit R02): endReason: engine_error betekent dat het gesprek WEL tot stand kwam maar eindigde door een technische fout aan onze kant of werd afgekapt door een herstart van de spraakengine - het bevat een transcript en meestal een opname (wanneer de engine die kon opslaan; na een herstart van de engine slechts een gedeeltelijke of lege transcriptie en geen opname), en wordt nooit in rekening gebracht. endReason geeft altijd de specifieke reden. |
Gesprekstype: het veld kind
kind zegt hoe een gesprek tot stand kwam - en dus hoe het in rekening wordt gebracht. Het is het enige veld waarmee u op een factuur het gesprek van een eigen agent (BYO) kunt onderscheiden van het gesprek van een ingebouwde agent, omdat relaybenen de toeslag voor een agent niet bevatten.
| kind | Wat het is | Facturering |
|---|---|---|
agent | Een uitgaand gesprek van de ingebouwde spraakagent. | 0,92 kr/min + 2,50 kr/min |
bridge | Een directe verbinding tussen twee nummers, zonder agent. | 0,92 kr/min (≈ 0,04 EUR) voor elk van de twee benen |
relay | Een uitgaand gesprek van uw eigen agent via POST /v1/relay. | 0,92 kr/min (≈ 0,04 EUR), geen toeslag voor een agent |
inbound | Een inkomend gesprek naar uw nummer. | 0,50 kr/min (+ agent, als er een het afhandelde) |
transfer | Het tweede been dat ontstaat door een inkomend gesprek door te verbinden naar een persoon. De agent verlaat het gesprek na de doorverbinding, dus dit been wordt als een gewoon uitgaand gesprek in rekening gebracht. | 0,92 kr/min, geen toeslag voor een agent |
sip | Een gesprek dat rechtstreeks wordt gebeld vanaf een SIP-client (softphone, uw eigen centrale) die op uw lijn is geregistreerd, buiten onze API om - achteraf afgerekend op basis van de gespreksgegevens van het netwerk. | 0,92 kr/min, geen toeslag voor een agent |
voice_message | Een spraakbericht: belt, leest een tekst voor en hangt op (POST /v1/voice-messages). Het resultaat staat in het blok voiceMessage. | alleen de uitgaande minuut (0,92 kr/min), per seconde, geen toeslag voor een agent; een onbeantwoord gesprek is 0 |
ring | Een beltoon om een poort te openen: een korte beltoon die nooit wordt verbonden (POST /v1/rings). Het resultaat staat in het blok ring. | 0,25 kr per poging, plus seconden als er wordt opgenomen |
Waarom een gesprek eindigde: het veld endReason
We voegen endReason toe aan elk afgesloten gesprek. Let op: een gesprek dat is geblokkeerd vanwege een onbetaalde schuld of door een handmatige opschorting heeft status completed, maar endReason is respectievelijk credit_blocked of suspended en de prijs is 0 (hetzelfde geldt voor een gesprek dat door het maandelijkse bestedingsplafond is geweigerd: spend_cap) - status alleen onderscheidt het niet van een gesprek dat werkelijk is doorgekomen. Hetzelfde geldt voor een gesprek met een geautomatiseerde beller aan de andere kant die steeds hetzelfde bericht herhaalt (caller_loop): het heeft status completed, maar als het tot vijf minuten duurde, is de prijs 0. De drie "onbeantwoord"-varianten zijn bewust verschillend, geen gedeelde toestand.
| endReason | Betekenis |
|---|---|
completed | Het gesprek kwam door en eindigde normaal. |
no_answer | Het ging over en de gebelde nam niet op. |
busy | Een bezettoon - dit is NIET hetzelfde als niet opnemen. |
rejected | Het platform weigerde de verbinding rechtstreeks. |
capacity | De uitgaande lijnen (relaypool) waren vol. Bij een inkomend gesprek: de lijnen waren vol en de spraakengine wees het gesprek af, dus de beller kwam er niet doorheen; het gesprek heeft status missed, duur 0 en prijs 0, en er wordt een webhook call.missed voor verstuurd. |
blocked | De bestemming staat op de bel-mij-niet-lijst of in de automatische blokkade van 30 dagen (zie Bel-mij-niet-lijst hieronder). |
loop_guard | Bescherming tegen het bellen van uw eigen volai-nummer of dat van een ander account. |
credit_blocked | Een inkomend gesprek naar een agent waarvan de eigenaar de schuldlimiet heeft overschreden - de agent hangt direct na zijn openingszin op, prijs 0. |
suspended | Een inkomend gesprek naar een agent die de volai-beheerder handmatig heeft opgeschort (of waarvan het hele account is opgeschort) - de agent hangt direct na zijn openingszin op, prijs 0. |
spend_cap | Een inkomend gesprek naar een agent van een account dat het door de eigenaar ingestelde maandelijkse bestedingsplafond (GET /v1/account/spend-cap) heeft bereikt en niet voor inboundAboveCap koos - de agent hangt direct na zijn openingszin op, prijs 0. |
failed | Het gesprek kwam helemaal niet tot stand (platform of netwerk). |
engine_error | Het gesprek kwam tot stand, maar eindigde door een technische fout in onze spraakengine - niet de schuld van de gebelde. Wordt nooit in rekening gebracht. Dit omvat ook een gesprek dat een herstart of stop van de engine halverwege de conversatie afkapte (bijvoorbeeld tijdens een deployment): het heeft slechts een gedeeltelijke of lege transcript en geen opname, een korte tekst in summary die het verklaart, geen blok engine, en de reason van de webhook call.failed is unfinished. |
caller_loop | Een inkomend gesprek waarbij de beller een geautomatiseerd systeem was dat steeds hetzelfde bericht herhaalde (een wachtrij, een dialer) - volai beëindigt het na kort op een mens te hebben gewacht. Het gesprek heeft status completed en een positieve duur, maar is gratis als het tot vijf minuten duurt (prijs 0); een langer gesprek wordt als elk ander in rekening gebracht. |
Agents
GET/v1/agents
De spraakagents van het account.
Verzoek
curl https://volai.cz/v1/agents \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"agents": [
{
"id": "ag_pw7q2vnd",
"name": "Receptie",
"language": "nl",
"systemPrompt": "U bent de receptie van café Nula. U neemt bestellingen om af te halen aan...",
"firstMessage": "Goedendag, café Nula, waarmee kan ik u helpen?",
"voiceId": "7JbZPqJGWUfXXBim0T8U",
"provider": "elevenlabs",
"createdAt": 1756111000000,
"status": "active",
"toolIds": ["tl_5c2a91f4"],
"dataFields": [
{
"key": "name",
"type": "string",
"description": "De naam van de beller, zoals hij die zelf noemde."
}
],
"recordCalls": true,
"recordingNotice": false,
"goal": null,
"knowledge": null,
"notifyEmail": true,
"silencePromptSecs": 10,
"laughter": "auto",
"firstMessageProtectSecs": null,
"silencePromptText": null,
"sttLanguageHintsStrict": false,
"turnDigitTailFloorMs": null,
"turnLongSpeechFloorMs": null,
"turnLongSpeechSecs": null,
"missedCallSms": { "enabled": false },
"suspended": false
},
{
"id": "ag_kx91fa2b",
"name": "Receptie",
"language": "nl",
"systemPrompt": "U bent de receptie van café Nula. U neemt bestellingen om af te halen aan...",
"firstMessage": "Goedendag, café Nula, waarmee kan ik u helpen?",
"voiceId": "milena",
"provider": "engine",
"numberE164": "+420601234567",
"createdAt": 1756111000000,
"status": "active",
"toolIds": ["tl_5c2a91f4"],
"transferTo": "+420777123456",
"transferCondition": "De beller vraagt uitdrukkelijk om doorverbonden te worden met een persoon.",
"dataFields": [
{
"key": "name",
"type": "string",
"description": "De naam van de beller, zoals hij die zelf noemde."
},
{
"key": "urgency",
"type": "string",
"description": "Hoe dringend het verzoek was.",
"enumValues": ["low","normal","high"]
}
],
"recordCalls": true,
"recordingNotice": false,
"goal": null,
"knowledge": null,
"notifyEmail": true,
"silencePromptSecs": 10,
"laughter": "auto",
"firstMessageProtectSecs": null,
"silencePromptText": null,
"sttLanguageHintsStrict": false,
"turnDigitTailFloorMs": null,
"turnLongSpeechFloorMs": null,
"turnLongSpeechSecs": null,
"missedCallSms": { "enabled": false },
"suspended": false
}
]
}POST/v1/agents
Maakt een nieuwe spraakagent aan. Alleen name en systemPrompt zijn verplicht - hoe u een goede prompt schrijft, staat op de pagina Spraakagent . Als u numberE164 meestuurt, wordt de agent direct aan dat nummer gekoppeld. POST geeft alleen het id terug; gebruik een vervolg-GET /v1/agents/{id} om de feitelijke provider te lezen. transferTo start na het opslaan een poging om een ElevenLabs-agent naar de engine te verplaatsen, maar een gepauzeerde poging, een niet beschikbare engine of een mislukking kan hem op ElevenLabs laten staan, waar doorverbinden niet werkt. voiceId kiest de stem: expliciete useForOutboundTasks: true vervangt een herkende catalogusstem die niet op de engine beschikbaar is, inclusief katty, door de standaardstem van de engine. Buiten deze terugval accepteert de engine catalogusstemmen behalve de ElevenLabs-stemmen (katty, de oudere anet); een ruwe ElevenLabs-ID of een andere incompatibele stem geeft 400. Bij false moet de aanvankelijke stem geldig zijn voor ElevenLabs (katty of een ruwe ElevenLabs-ID; ElevenLabs wijst anet niet meer toe aan nieuwe agents en volai weigert het met 400); een latere succesvolle overstap voor transferTo kan het op de standaardstem van de engine afbeelden. Zonder voiceId gebruikt de engine Milena en gebruikt ElevenLabs de stem van de sjabloon (Katty). De optionele velden (toolIds, transferTo, transferCondition, dataFields, recordCalls, recordingNotice, goal, knowledge, notifyEmail, useForOutboundTasks, voicemail) worden hieronder beschreven.
Verzoek
curl -X POST "https://volai.cz/v1/agents" \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: REPLACE_WITH_UNIQUE_POST_V1_AGENTS_ID" \
-d '{"name":"Receptie","systemPrompt":"U bent de receptie van café Nula. U neemt bestellingen om af te halen aan en beantwoordt vragen over openingstijden. Verzin nooit prijzen die u niet kent. Sluit het gesprek af met een samenvatting van de bestelling.","firstMessage":"Goedendag, café Nula, waarmee kan ik u helpen?","language":"nl","numberE164":"+420601234567","toolIds":["tl_5c2a91f4"],"transferTo":"+420777123456","transferCondition":"De beller wil een medewerker spreken, of heeft een klacht.","dataFields":[{"key":"name","type":"string","description":"De naam van de beller, zoals hij die zelf noemde."},{"key":"coffee_count","type":"number","description":"Hoeveel koffie de beller bestelde."}],"recordCalls":true}'Vervang vóór het uitvoeren REPLACE_WITH_UNIQUE… door een unieke ID voor deze actie, bijvoorbeeld een UUID, en bewaar die. Gebruik dezelfde ID opnieuw als u hetzelfde verzoek na een time-out herhaalt. Gebruik een nieuwe ID voor een andere actie of een gewijzigde body, ook bij een ander endpoint.
Antwoord
{
"id": "ag_kx91fa2b"
}Foutcodes
- 400
invalid_nameDe naam van de agent moet 1 tot 60 tekens lang zijn. - 400
invalid_system_promptDe instructies van de agent moeten 10 tot 6000 tekens lang zijn. - 400
invalid_languagelanguage moet cs, sk, en, de of pl zijn. - 400
agent_limitHet account heeft al het maximale aantal agents bereikt (10). - 400
tool_limittoolIds bevat meer dan 10 tools. - 400
invalid_data_fieldsdataFields heeft een ongeldige sleutel, een ongeldig type, een ongeldige beschrijving, of meer dan 10 items. - 400
invalid_goalgoal is te lang - er passen 300 tekens in. - 400
invalid_knowledgeknowledge is te lang - er passen 20000 tekens in. - 400
transfer_looptransferTo wijst naar een volai-nummer op hetzelfde account - het gesprek zou in zichzelf terugkeren. - 400
blocked_destinationtransferTo is een premium-tariefnummer met bijzondere tarieven. - 400
destination_not_allowedDe agent moet een nummer voor bellen en sms gebruiken, entransferTois geen Tsjechisch (+420) nummer: gesprekken die vanaf zo'n nummer worden doorverbonden, kunnen alleen naar Tsjechische nummers gaan. Geef een TsjechischtransferTomee, of koppel dit nummer niet aan de agent. Bij een nummer in een ander land moet het doorverbindnummer in het land van het nummer liggen (zodra bellen daarheen is ingeschakeld) of in Tsjechië. Er is niets gewijzigd. - 400
unsupported_for_number_typeDe agent moet een nummer voor bellen en sms gebruiken, maar zou niet op de eigen spraakengine van volai worden aangemaakt: dit soort nummer wordt alleen door een engine-agent beantwoord (useForOutboundTasks: true, of laat het veld weg wanneer de engine de standaard is voor de taal van de agent). De agent is niet aangemaakt en er is niets gewijzigd. - 400
invalid_numbertransferTo is geen geldig telefoonnummer. - 400
invalid_voicemail_messagevoicemail.message ontbreekt of is te lang - verplicht wanneer action "message" is, er passen 400 tekens in. - 400
voicemail_requires_enginevoicemail vereist de engine als aanvankelijke providerkeuze: useForOutboundTasks: true, of weglaten terwijl de engine de standaard is. false mislukt zelfs met transferTo, omdat de validatie vóór de overstapproging komt. - 400
invalid_workflowEen ongeldige workflowgraaf (een onbereikbaar knooppunt, een ontbrekend startknooppunt, een gereserveerde toolnaam...); er wordt niets opgeslagen, de specifieke reden staat altijd in message. - 404
calendar_integration_not_foundcalendar.integrationId wijst niet naar een agendakoppeling op uw account - kies hem opnieuw uit GET /v1/integrations. - 400
calendar_invalid_calendarcalendar.calendarId staat niet tussen de agenda's van die koppeling, of is alleen-lezen (accessRole reader of freeBusyReader), dus de agent kon er geen afspraak in schrijven. Kies een beschrijfbare agenda uit list_calendars. - 400
calendar_invalid_timezonecalendar.timezone is geen geldige IANA-zone, bijvoorbeeld "Europe/Prague". - 400
calendar_invalid_windowsDe vensters in calendar.windows gebruiken geen HH:MM, overlappen elkaar, of er zijn er meer dan vier op één dag. - 400
calendar_no_windowsDe agenda staat aan maar geen enkele weekdag heeft een venster - de agent zou niets te bieden hebben. - 400
calendar_invalid_rulesDe numerieke agendaregels vallen buiten het bereik, of de minimale aanlooptijd is langer dan de zoekhorizon. - 404
tool_not_foundEen tool uit toolIds bestaat niet of hoort niet bij uw account. - 404
not_foundnumberE164 bestaat niet of hoort niet bij uw account. - 400
validationDe vorm van de invoer klopt niet - meestal komt voiceId niet overeen met een item uit GET /v1/voices en lijkt het niet op een ruwe ElevenLabs-ID (de foutmelding noemt de geldige waarden), of silencePromptSecs valt buiten 3 tot 25, firstMessageProtectSecs buiten 0 tot 20, of silencePromptText is langer dan 80 tekens, of inboundVariables heeft meer dan 10 sleutels, een gereserveerde of onjuiste sleutelnaam, of een waarde langer dan 200 tekens, of sttLanguageHintsStrict is niet true, false of null, of turnDigitTailFloorMs of turnLongSpeechFloorMs valt buiten 0 en 400 tot 2300, of turnLongSpeechSecs valt buiten 1 tot 30. - 409
invalid_transitionHet nummer voor bellen en sms dat u aan de agent koppelt is niet actief (het wacht op registratie, is geweigerd, of wordt vrijgegeven). Een agent kan alleen aan een actief nummer worden gekoppeld. De agent is niet aangemaakt en er is niets gewijzigd. - 409
in_progressEen andere bewerking (een activering, afrekening of vrijgave) verwerkt het nummer voor bellen en sms dat u aan de agent koppelt. De agent is niet aangemaakt en er is niets gewijzigd; herhaal het verzoek over een minuut. - 500
agent_template_missingDe sjabloon voor het aanmaken van agents ontbreekt of is aan onze kant beschadigd - een tijdelijke fout aan onze kant, neem contact op met support. - 502
engine_unavailableBij het koppelen van het nummer voor bellen en sms reageerde de eigen spraakengine van volai niet. De agent is niet aangemaakt en er is niets gewijzigd; herhaal het verzoek zo meteen. Dezelfde code wordt teruggegeven bij een schrijfactie die door het standby-knooppunt van de engine werd geweigerd (de engine draait momenteel op een standby-knooppunt dat geen configuratie accepteert): de melding zegt dat en het verzoek kan over een paar minuten worden herhaald. - 502
engine_readback_mismatchBij het koppelen van het nummer voor bellen en sms antwoordde de engine, maar het terugleggen toonde een andere koppeling dan de koppeling die we verstuurden. De agent is niet aangemaakt en er is niets gewijzigd; herhaal het verzoek. - 503
engine_not_configuredU vroeg om de eigen spraakengine van volai (useForOutboundTasks: true), maar de engine of het uitgaande verkeer erop is nu niet geconfigureerd. Controleer de configuratie, of laat het veld weg - de agent wordt dan op ElevenLabs aangemaakt. - 503
engine_setup_incompleteDe agent is aangemaakt, maar het instellen van uitgaande taken op de eigen engine van volai is niet voltooid. Probeer het aanmaken NIET opnieuw, dat zou een duplicaat maken. Zoek hem op naam inGET /v1/agents, bekijkprovider, en rond het af in het agentenportaal of met support;PATCH /v1/agents/{id}alleen kan geen provider kiezen.
GET/v1/agents/{id}
Het detail van één agent. Een verwijderde agent geeft 404 agent_not_found. Bij het aanmaken kiest useForOutboundTasks het aanvankelijke platform: true kiest de engine van volai, false ElevenLabs, en weglaten gebruikt de standaard van de installatie. Omdat POST alleen een id teruggeeft, controleert u het resultaat in deze vervolg-GET en het veld provider ervan. Het inschakelen van transferTo bij een ElevenLabs-agent start een poging om naar de engine over te stappen, maar die kan gepauzeerd zijn, niet beschikbaar zijn of mislukken; doorverbinden werkt niet zolang provider elevenlabs blijft. Een succesvolle overstap kan de stem veranderen. Het wijzigen van language met PATCH van een taal waarin nieuwe agents op ElevenLabs starten (nu en, de, pl) naar een taal waarin ze op de engine starten (cs, sk) start dezelfde poging: bij succes krijgt de agent de standaardstem van de engine voor de nieuwe taal (Milena, Katarina), bij een mislukking blijft hij op ElevenLabs. Een wijziging tussen cs en sk verplaatst niets. Andere providerwijzigingen worden door support afgehandeld. ElevenLabs accepteert uit de catalogus alleen anet of een ruwe ElevenLabs-stem-id; andere catalogusstemmen vereisen de engine. Het antwoord bevat ook altijd een veld laughterEffective ({active, reason, sensitiveCategory} | null) - de actuele uitkomst van het lachbeleid voor de GEPUBLICEERDE configuratie van de agent, vers berekend buiten enig gesprek. Voor een ElevenLabs-agent is het altijd {active: false, reason: "not_engine", sensitiveCategory: null} zonder de engine aan te roepen; voor een engine-agent is reason een van meerdere waarden (ok, agent_off, sensitive_prompt, voice_not_listed en meer) en de engine kan er in de loop van de tijd nieuwe toevoegen - behandel een onbekende waarde als uit, niet als een fout. Als de engine niet binnen 3 seconden antwoordt, uitgevallen is, of de agent niet kent (een stille 404 bij een oudere build), is laughterEffective gewoonweg null - nooit een gok.
Als de engine niet binnen 3 seconden antwoordt, uitgevallen is, of de agent niet kent, ziet het antwoord er zo uit: "laughterEffective": null - geen object met geraden waarden.
Verzoek
curl https://volai.cz/v1/agents/ag_kx91fa2b \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"agent": {
"id": "ag_kx91fa2b",
"name": "Receptie",
"language": "nl",
"systemPrompt": "U bent de receptie van café Nula. U neemt bestellingen om af te halen aan...",
"firstMessage": "Goedendag, café Nula, waarmee kan ik u helpen?",
"voiceId": "milena",
"provider": "engine",
"numberE164": "+420601234567",
"createdAt": 1756111000000,
"status": "active",
"toolIds": ["tl_5c2a91f4"],
"transferTo": "+420777123456",
"transferCondition": "De beller vraagt uitdrukkelijk om doorverbonden te worden met een persoon.",
"dataFields": [
{
"key": "name",
"type": "string",
"description": "De naam van de beller, zoals hij die zelf noemde."
},
{
"key": "urgency",
"type": "string",
"description": "Hoe dringend het verzoek was.",
"enumValues": ["low","normal","high"]
}
],
"recordCalls": true,
"recordingNotice": false,
"goal": null,
"knowledge": null,
"notifyEmail": true,
"silencePromptSecs": 10,
"laughter": "auto",
"firstMessageProtectSecs": null,
"silencePromptText": null,
"sttLanguageHintsStrict": false,
"turnDigitTailFloorMs": null,
"turnLongSpeechFloorMs": null,
"turnLongSpeechSecs": null,
"missedCallSms": { "enabled": false },
"suspended": false,
"laughterEffective": {
"active": true,
"reason": "ok",
"sensitiveCategory": null
}
}
}Foutcodes
- 404
agent_not_foundDe agent bestaat niet, is verwijderd, of hoort niet bij uw account.
GET/v1/agents/{id}/call-hold
Leest de permanente gespreksblokkade van de agent van de eigenaar. Het ruwe antwoord is {agentId, held, hold}; vóór het stoppen held:false, hold:null. Dit beschrijft geen opschorting door de beheerder of andere toelatingspoorten voor gesprekken.
Verzoek
curl https://volai.cz/v1/agents/ag_example/call-hold -H 'Authorization: Bearer vk_YOUR_KEY'Antwoord
{
"agentId": "ag_example",
"held": false,
"hold": null
}Foutcodes
- 404
agent_not_foundDe agent bestaat niet, is verwijderd, of hoort niet bij uw account.
POST/v1/agents/{id}/call-hold
Stopt permanent nieuwe uitgaande gesprekken en geverifieerde campagneterugbelacties voor een eigen engine-agent. Geef precies reason:budget_limit en operationId mee (1-128 letters, cijfers, punten, onderstrepingstekens, dubbele punten of koppeltekens, beginnend met een letter of cijfer). Er is geen verloop- of hervatbewerking. Een identiek ID met dezelfde inhoud speelt de oorspronkelijke audit opnieuw af; een andere bewerking geeft 409 revision_conflict. Lees na een onzeker antwoord GET. hold bevat agentId, reason, operationId, fingerprint en createdAt. Blokkades door de beheerder, de demo en actieve gesprekken blijven ongewijzigd. Onafhankelijke fouten van de terugbelresolver laten de standaardagent van het nummer ongemoeid. Idempotency-Key is hier niet het idempotentiemechanisme.
Verzoek
curl https://volai.cz/v1/agents/ag_example/call-hold -X POST -H 'Authorization: Bearer vk_YOUR_KEY' -H 'Content-Type: application/json' -d '{"reason":"budget_limit","operationId":"budget-stop-001"}'Antwoord
{
"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"
}
}Foutcodes
- 404
agent_not_foundDe agent bestaat niet, is verwijderd, of hoort niet bij uw account. - 400
validationOngeldige body of niet-ondersteunde agent. Kies een eigen engine-agent buiten de gedeelde demo. - 409
revision_conflictEr bestaat een andere permanente blokkade. De oorspronkelijke audit blijft behouden; lees de status ervan met GET.
PATCH/v1/agents/{id}
Werkt een bestaande agent bij - dezelfde velden als bij het aanmaken, allemaal optioneel. Verstuur alleen wat moet veranderen. Twee ervan worden echter altijd VOLLEDIG verstuurd, omdat ze de bestaande lijst vervangen: toolIds en dataFields. Een lege array schakelt de functie dus uit ("toolIds": [] verwijdert alle tools van de agent), terwijl een ontbrekende sleutel betekent "laat zoals het is". Doorverbinden schakelt u uit met een lege string: "transferTo": "".
Verzoek
curl -X PATCH https://volai.cz/v1/agents/ag_kx91fa2b \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"firstMessage": "Goedendag, café Nula, wat mag het zijn?"}'Antwoord
{
"agent": {
"id": "ag_kx91fa2b",
"name": "Receptie",
"language": "nl",
"systemPrompt": "U bent de receptie van café Nula. U neemt bestellingen om af te halen aan...",
"firstMessage": "Goedendag, café Nula, wat mag het zijn?",
"voiceId": "milena",
"provider": "engine",
"numberE164": "+420601234567",
"createdAt": 1756111000000,
"status": "active",
"toolIds": ["tl_5c2a91f4"],
"transferTo": "+420777123456",
"transferCondition": "De beller vraagt uitdrukkelijk om doorverbonden te worden met een persoon.",
"recordCalls": true,
"recordingNotice": false,
"goal": null,
"knowledge": null,
"notifyEmail": true,
"silencePromptSecs": 10,
"laughter": "auto",
"firstMessageProtectSecs": null,
"silencePromptText": null,
"sttLanguageHintsStrict": false,
"turnDigitTailFloorMs": null,
"turnLongSpeechFloorMs": null,
"turnLongSpeechSecs": null,
"missedCallSms": { "enabled": false },
"suspended": false
}
}Foutcodes
- 400
invalid_nameDe naam van de agent moet 1 tot 60 tekens lang zijn. - 400
invalid_system_promptDe instructies van de agent moeten 10 tot 6000 tekens lang zijn. - 400
invalid_languagelanguage moet cs, sk, en, de of pl zijn. - 400
tool_limittoolIds bevat meer dan 10 tools. - 400
invalid_data_fieldsdataFields heeft een ongeldige sleutel, een ongeldig type, een ongeldige beschrijving, of meer dan 10 items. - 400
invalid_goalgoal is te lang - er passen 300 tekens in. - 400
invalid_knowledgeknowledge is te lang - er passen 20000 tekens in. - 400
transfer_looptransferTo wijst naar een volai-nummer op hetzelfde account. - 400
blocked_destinationtransferTo is een premium-tariefnummer met bijzondere tarieven. - 400
destination_not_allowedDe agent heeft een nummer voor bellen en sms (of u koppelt er nu een), entransferTois geen Tsjechisch (+420) nummer: gesprekken die vanaf zo'n nummer worden doorverbonden, kunnen alleen naar Tsjechische nummers gaan. Geef een TsjechischtransferTomee, of koppel dit nummer niet aan de agent. Bij een nummer in een ander land moet het doorverbindnummer in het land van het nummer liggen (zodra bellen daarheen is ingeschakeld) of in Tsjechië. Er is niets gewijzigd. - 400
unsupported_for_number_typeU wijst een nummer voor bellen en sms toe (of wisselt er een in), maar de agent draait niet op de eigen spraakengine van volai: dit soort nummer wordt alleen door een engine-agent beantwoord. Er is niets gewijzigd. - 400
invalid_voicemail_messagevoicemail.message ontbreekt of is te lang - verplicht wanneer action "message" is, er passen 400 tekens in. - 400
voicemail_requires_enginevoicemail is alleen voor een agent met provider "engine". Support verplaatst een ElevenLabs-agent naar de engine; laat het veld tot die tijd weg. - 400
invalid_workflowEen ongeldige workflowgraaf (een onbereikbaar knooppunt, een ontbrekend startknooppunt, een gereserveerde toolnaam...); er wordt niets opgeslagen, de specifieke reden staat altijd in message. - 404
calendar_integration_not_foundcalendar.integrationId wijst niet naar een agendakoppeling op uw account - kies hem opnieuw uit GET /v1/integrations. - 400
calendar_invalid_calendarcalendar.calendarId staat niet tussen de agenda's van die koppeling, of is alleen-lezen (accessRole reader of freeBusyReader), dus de agent kon er geen afspraak in schrijven. Kies een beschrijfbare agenda uit list_calendars. - 400
calendar_invalid_timezonecalendar.timezone is geen geldige IANA-zone, bijvoorbeeld "Europe/Prague". - 400
calendar_invalid_windowsDe vensters in calendar.windows gebruiken geen HH:MM, overlappen elkaar, of er zijn er meer dan vier op één dag. - 400
calendar_no_windowsDe agenda staat aan maar geen enkele weekdag heeft een venster - de agent zou niets te bieden hebben. - 400
calendar_invalid_rulesDe numerieke agendaregels vallen buiten het bereik, of de minimale aanlooptijd is langer dan de zoekhorizon. - 404
tool_not_foundEen tool uit toolIds bestaat niet of hoort niet bij uw account. - 404
agent_not_foundDe agent bestaat niet of hoort niet bij uw account. - 400
validationDe vorm van de invoer klopt niet - meestal komt voiceId niet overeen met een item uit GET /v1/voices en lijkt het niet op een ruwe ElevenLabs-ID (de foutmelding noemt de geldige waarden), of silencePromptSecs valt buiten 3 tot 25, firstMessageProtectSecs buiten 0 tot 20, of silencePromptText is langer dan 80 tekens, of inboundVariables heeft meer dan 10 sleutels, een gereserveerde of onjuiste sleutelnaam, of een waarde langer dan 200 tekens, of sttLanguageHintsStrict is niet true, false of null, of turnDigitTailFloorMs of turnLongSpeechFloorMs valt buiten 0 en 400 tot 2300, of turnLongSpeechSecs valt buiten 1 tot 30. - 409
invalid_transitionHet nummer voor bellen en sms dat u aan de agent toewijst is niet actief (het wacht op registratie, is geweigerd, of wordt vrijgegeven). Een agent kan alleen aan een actief nummer worden gekoppeld. Er is niets gewijzigd. - 409
in_progressOf er loopt al een andere wijziging (een concept of een publicatie) op deze agent, of een eerdere wijziging is doorgekomen maar kon niet veilig worden bevestigd - lees vóór het opnieuw proberen van deze PATCHGET /v1/agents/{id}/draft/operationen beslis op basis vanoperation. Dezelfde code wordt teruggegeven wanneer het nummer voor bellen en sms dat u aan de agent koppelt of inwisselt door een andere bewerking wordt verwerkt (een activering, afrekening of vrijgave): dat is tijdelijk, maar bij het wisselen van nummers kan het oorspronkelijke nummer al zijn losgekoppeld, dus lees eerst de agent (GET /v1/agents/{id}) en herhaal de PATCH over een minuut. - 502
engine_unavailableBij een agent die al op de eigen engine van volai draait: de engine reageert nu niet, de wijziging is niet opgeslagen. Probeer het zo meteen opnieuw. Dezelfde code wordt teruggegeven bij een schrijfactie die door het standby-knooppunt van de engine werd geweigerd (de engine draait momenteel op een standby-knooppunt dat geen configuratie accepteert, meestal na een failover): de melding zegt dat, de wijziging is zeker niet opgeslagen, de agent is niet als onzeker vergrendeld, en hetzelfde verzoek kan over een paar minuten worden herhaald. - 503
engine_not_configuredBij een agent die al op de eigen engine van volai draait: de engine is aan onze kant nu niet geconfigureerd, de wijziging is niet opgeslagen. Neem contact op met support. - 502
engine_readback_mismatchBij een agent die al op de eigen engine van volai draait: de engine heeft een andere configuratie opgeslagen dan we verstuurden, de wijziging is niet opgeslagen. Probeer het opnieuw.
DELETE/v1/agents/{id}
Verwijdert de agent zowel bij volai als bij ElevenLabs. Een nummer dat eraan was gekoppeld blijft van u - geef het alleen een nieuwe routering: een spraaknummer via PATCH /v1/numbers/{e164}, een nummer voor bellen en sms via PATCH /v1/voice-sms-numbers/{e164} (MCP set_voice_sms_number_routing). Het loskoppelen van het nummer van de agent maakt deel uit van het verwijderen: mislukt het (bij een nummer voor bellen en sms 409 in_progress, 502 routing_failed), dan wordt de agent niet verwijderd en blijft het nummer zoals het was - herhaal het verzoek zo meteen.
Verzoek
curl -X DELETE https://volai.cz/v1/agents/ag_kx91fa2b \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"deleted": true
}Foutcodes
- 404
agent_not_foundDe agent bestaat niet of hoort niet bij uw account. - 409
in_progressHet nummer van de agent wordt door een andere bewerking verwerkt (bij een nummer voor bellen en sms: een activering, afrekening of vrijgave). De agent is niet verwijderd en het nummer blijft zoals het was; herhaal het verzoek over een minuut. - 502
routing_failedHet loskoppelen van het nummer van de agent is mislukt (bij een nummer voor bellen en sms: in de eigen spraakengine). De agent is niet verwijderd en het nummer blijft gerouteerd zoals het was; herhaal het verzoek zo meteen.
De antwoorden van POST /v1/agents, PATCH /v1/agents/{id}, POST /v1/agents/{id}/draft/publish en POST /v1/agents/{id}/draft/rollback kunnen een optioneel veld warnings bevatten. Meerdere codes betreffen de openingszin: first_message_no_ai_disclosure wanneer de opgeslagen openingszin van de agent niet vermeldt dat het een digitale assistent is (EU AI Act, artikel 50), en first_message_too_long wanneer de geschatte uitspraakduur van wat de beller werkelijk hoort (de openingszin plus de zin met de opnamemelding, wanneer die aan staat: recordingNotice samen met recordCalls) langer is dan 7 seconden (ongeveer 2,53 woorden per seconde; cijfers altijd, en afkortingen in hoofdletters tot vier tekens, tellen per teken). Een ongewijzigde standaardopeningszin activeert deze code nooit, alleen een die de klant heeft bewerkt. Bellers die de agent onderbreken doen dat meestal binnen een seconde hoorbare spraak. Een agent met fasen (het veld workflow, zie het onderdeel Gespreksfasen (workflow) hieronder) voegt tien extra codes toe aan hetzelfde veld - 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 en workflow_node_tool_missing (een knooppunt verwijst naar een tool die niet meer op het account staat - hij is verwijderd; de fase moet zonder hem worden opgeslagen) - en bevat ze meestal zelfs wanneer de openingszin in orde is (de sjabloon Receptie geeft er drie terug). Een waarschuwing die bij één specifieke fase of overgang hoort, bevat ook nodeId of edgeId. Geen van de codes blokkeert het opslaan - ze markeren het alleen. Het veld ontbreekt alleen in het antwoord wanneer er niets te markeren is. Een concept terugdraaien naar een eerdere revisie (draft/rollback) geeft warnings terug net als publiceren, omdat het ook de openingszin naar de live agent schrijft. Alleen publiceren en terugdraaien (niet POST/PATCH /v1/agents) kunnen in hetzelfde veld ook number_changed_by_publish teruggeven, met waarden from/to (E.164, of null voor geen nummer), wanneer het opnieuw publiceren het telefoonnummer van de live agent daadwerkelijk wijzigt - ook dat blokkeert het publiceren niet. Een aparte code, calendar_pending_engine_switch, verschijnt wanneer de agent calendar (afspraken op basis van een agenda) heeft geconfigureerd maar op ElevenLabs draait - de instellingen worden opgeslagen en gaan werken zodra de agent naar de eigen engine van volai verhuist, wat het inschakelen van de agenda zelf in gang zet. Een agent met een gekoppeld telefoonnummer (numberE164) waarvan de instructies (systemPrompt of firstMessage) een {{variable}} gebruiken buiten de set die een inkomend gesprek werkelijk levert (caller_number, called_number, attempt_id, volai_blocked, volai_block_reason, trial_prompt, trial_first_message en zeven system__*-variabelen) krijgt prompt_variables_unavailable_inbound - bij een inkomend gesprek wordt die variabele door lege tekst vervangen. Wanneer na het verwijderen van elk blok {{...}} uit een niet-lege openingszin buiten de variabelen geen letter of cijfer overblijft, krijgt hij first_message_empty. Een aparte code, agent_misuse_suspected, verschijnt wanneer de configuratie van de agent (voor de bovenstaande antwoorden) formuleringen bevat die kunnen overkomen als zich voordoen als een autoriteit of een andere instelling, of als een verzoek om gevoelige informatie - zie de voorwaarden van volai, artikel 10. Dezelfde code in dezelfde vorm komt ook terug in het optionele veld warnings van PUT /v1/agents/{id}/draft en POST /v1/agents/{id}/simulate (het concept), POST /v1/calls (de proefgespreksprompt of variables), POST /v1/tasks en PATCH /v1/tasks/{id} (de taaknaam en ontvangervariabelen) en POST /v1/tools en PATCH /v1/tools/{id} (de beschrijving van de tool en zijn parameters). Het blokkeert het opslaan of verzenden nooit - het is slechts een waarschuwing. Alleen POST /v1/agents kan ook elevenlabs_by_explicit_choice teruggeven, wanneer het verzoek useForOutboundTasks: false verstuurde voor een taal die de installatie anders op de eigen spraakengine van volai zou starten (nu cs en sk), zodat de agent op ElevenLabs bleef - zonder de enginestemmen en zonder de functies die alleen op de engine draaien. De agent wordt nog steeds aangemaakt, de waarschuwing markeert het alleen; als u alleen een agent voor inkomende gesprekken wilde, laat het veld dan weg.
{
"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."
}
}
]
}Nieuwe agentvelden
Naast de naam, prompt, stem en het nummer accepteren POST /v1/agents en PATCH /v1/agents/{id} ook deze velden. Alle zijn optioneel bij het aanmaken. toolIds, transferTo, transferCondition, dataFields en recordCalls ontbreken in het antwoord bij een oudere agent totdat iemand ze instelt. goal en knowledge worden daarentegen altijd teruggegeven - niet ingesteld als null, niet als ontbrekende sleutel - en notifyEmail is altijd een boolean (ontbreken betekent aan). recordingNotice is ook altijd een boolean - false totdat iemand het inschakelt. Het antwoord bevat ook altijd provider (engine of elevenlabs) - het zegt op welk platform de agent werkelijk draait, beide waarden komen in productie voor. Het bevat evenzo altijd suspended (boolean) - true wanneer de volai-beheerder de agent handmatig heeft opgeschort; zo'n agent kan geen gesprekken ontvangen of voeren (agent_suspended), en het veld kan niet via de API of MCP worden ingesteld. useForOutboundTasks is de uitzondering - alleen POST /v1/agents accepteert het, het zit niet in PATCH (volai, niet de klant, verplaatst een bestaande agent tussen providers; uit zichzelf doet het dat na het inschakelen van transferTo of calendar, of na een wijziging van language naar een taal waarin nieuwe agents op de engine starten - zie het agentdetail hierboven). voicemail is alleen beschikbaar voor een agent met provider: "engine" - voor andere mislukt POST/PATCH met dit veld. silencePromptSecs (een herinnering bij stilte) wordt daarentegen voor beide providers geaccepteerd en teruggegeven - bij een agent op ElevenLabs wordt de waarde alleen opgeslagen, en is hoorbaar zodra de agent naar de eigen spraakengine van volai overstapt. firstMessageProtectSecs (beschermt de openingszin tegen onderbreking, 0 tot 20 seconden) en silencePromptText (uw eigen zin voor de herinnering bij stilte, tot 80 tekens) worden voor BEIDE providers ook geaccepteerd en teruggegeven, maar anders dan silencePromptSecs ZONDER afgedwongen standaard: een ontbrekend veld bij POST wordt niet opgeslagen en het antwoord toont null (bescherming uit, standaardtekst), en een PATCH zonder de sleutel laat de opgeslagen waarde ongemoeid. workflow (gespreksfasen) is beschikbaar bij beide providers, maar niet even volledig - zie het onderdeel Gespreksfasen (workflow) hieronder. laughter (lachen van de agent) is ook altijd aanwezig, net als goal/knowledge/notifyEmail hierboven - bij een oudere agent zonder ingestelde waarde is het auto, nooit een ontbrekende sleutel; voor BEIDE providers geaccepteerd en opgeslagen, maar alleen hoorbaar bij een agent op de engine met een Tsjechische Cartesia-stem. inboundVariables (standaardwaarden voor {{variable}}-plaatshouders bij INKOMENDE gesprekken) is optioneel zoals toolIds - een agent zonder heeft zo'n sleutel niet in het antwoord; voor BEIDE providers geaccepteerd en teruggegeven, omdat de engine en ElevenLabs dezelfde initiatiewebhook lezen, en niet gebruikt bij uitgaande gesprekken. sttLanguageHintsStrict (strikte taalvergrendeling voor spraakherkenning) is altijd aanwezig als boolean - false wanneer de agent het niet heeft ingesteld; voor BEIDE providers geaccepteerd en opgeslagen, maar alleen gelezen door de eigen engine van volai, ElevenLabs negeert het. turnDigitTailFloorMs, turnLongSpeechFloorMs en turnLongSpeechSecs (ondergrenzen voor stilte aan het einde van het antwoord van de beller) zijn altijd aanwezig als getallen of null, net als firstMessageProtectSecs - ZONDER afgedwongen standaard: een ontbrekend veld bij POST wordt niet opgeslagen en het antwoord toont null (de engine gebruikt zijn standaardwaarden 0 / 0 / 5), een PATCH zonder de sleutel laat de opgeslagen waarde ongemoeid en null verwijdert hem; voor BEIDE providers geaccepteerd en opgeslagen, alleen gelezen door de eigen engine van volai.
| Veld | Wat het doet |
|---|---|
toolIds | Een array van id's van webhooktools uit GET /v1/tools die de agent tijdens een gesprek mag aanroepen. Hoogstens 10. Een tool hoort bij het account, dus meer dan één agent kan hem ingeschakeld hebben. |
transferTo | Een nummer in E.164 waarnaar de agent de beller doorverbindt wanneer die om een mens vraagt. Een lege string schakelt doorverbinden uit. Het eigen volai-nummer van het account kan hier niet worden gebruikt (transfer_loop). |
transferCondition | Wanneer er moet worden doorverbonden, geschreven als instructie aan het model (tot 500 tekens). Als u hem niet meestuurt, vullen we een standaardzin in. |
dataFields | Wat de agent uit het gesprek moet vastleggen: een array van objecten {key, type, description, enumValues}, hoogstens 10. key bestaat uit kleine letters, cijfers en onderstrepingstekens; type is string, number of boolean; enumValues kan alleen bij een tekstveld worden ingesteld. Ingevulde waarden komen daarna binnen in het veld data van het gesprek. |
recordCalls | Of de gesprekken van deze agent worden opgenomen. Standaard aan; bij false wordt voor gesprekken die na het opslaan van de wijziging beginnen geen opname gemaakt, en GET /v1/calls/{id}/recording geeft er no_recording voor terug (een gesprek dat al overgaat of loopt, volgt de instelling vanaf het moment dat het begon). Opnemen alleen vertelt de beller niets - recordingNotice schakelt de zin met de melding in. Dat de melding voldoet aan de wet voor uw situatie, is uw verantwoordelijkheid. |
recordingNotice | Of de agent zijn openingszin afsluit met de mededeling aan de beller dat het gesprek wordt opgenomen - een korte zin in de taal van de agent (in het Engels “Please note this call is recorded.”). Standaard uit: een weggelaten veld slaat bij POST niets op, en een PATCH zonder de sleutel laat het ongewijzigd. Wordt alleen afgespeeld zolang recordCalls aan staat; het antwoord bevat het altijd als boolean. Sinds 22 okt 2026 voegt volai het niet meer uit zichzelf toe. |
goal | Het doel van het gesprek in één zin - een LLM-beoordelaar gebruikt het na het gesprek om te bepalen of de agent bereikte wat de beller wilde (alleen beoordeeld bij een agent op de eigen engine van volai, zie het onderdeel Doel en beoordeling in de docs Spraakagent). Tot 300 tekens. PATCH met null of een lege string wist het doel. |
knowledge | Tekst die de agent kent naast zijn instructies - een prijslijst, openingstijden, veelgestelde vragen. Achteraan de prompt toegevoegd. Tot 20000 tekens. PATCH met null of een lege string wist de kennis. |
notifyEmail | Of de accounteigenaar na elk gesprek dat deze agent voert een samenvatting per e-mail ontvangt - nu wordt de e-mail alleen verstuurd voor een agent op de eigen engine van volai (provider: "engine"); bij een agent op ElevenLabs wordt de waarde opgeslagen, maar er wordt nog geen e-mail verstuurd. Standaard aan. |
useForOutboundTasks | true kiest bij het aanmaken de engine van volai, false start op ElevenLabs, en weglaten gebruikt de standaard van de installatie, die afhangt van de beschikbaarheid van de engine, de inschakeling en de geconfigureerde talen. Alleen cs of sk kiezen garandeert de engine niet; is de engine-standaard uitgeschakeld, dan start het aanmaken op ElevenLabs. POST geeft alleen een id terug, dus lees provider met een vervolg-GET /v1/agents/{id}. Het veld geldt alleen bij het aanmaken; support handelt andere verplaatsingen af. transferTo start ook een poging om van ElevenLabs naar de engine te verhuizen, waarvan het resultaat moet worden gecontroleerd. |
voicemail | Voicemaildetectie bij UITGAANDE gesprekken. message is verplicht bij action: "message", tot 400 tekens. PATCH accepteert het alleen met provider: "engine". Bij POST moet de aanvankelijke keuze de engine zijn: expliciete useForOutboundTasks: true, of weglaten terwijl de engine de standaard is. false mislukt met voicemail_requires_engine, ook met transferTo, omdat de validatie vóór de overstapproging komt. PATCH met null wist het. |
silencePromptSecs | Hoeveel seconden stilte van de beller één herinnering activeren ("Bent u er nog?"), een geheel getal van 3 tot 25. null schakelt het uit. Ontbreken bij POST slaat de standaard 10 op. Voor BEIDE providers geaccepteerd en opgeslagen - bij een agent op het standaardplatform ElevenLabs (provider: "elevenlabs") wordt de waarde alleen opgeslagen, en is hoorbaar zodra de agent naar de eigen spraakengine van volai overstapt, de enige die het kan afspelen. Een waarde buiten 3 tot 25 mislukt met de algemene validatiefout (validation, 400) - geen aparte code. |
firstMessageProtectSecs | Hoeveel seconden van de openingszin worden beschermd tegen een korte reactie van de persoon die opnam ("Hallo?", "Ja?"), een getal van 0 tot 20 (decimalen toegestaan), alleen bij UITGAANDE gesprekken. Een antwoord van de persoon die opnam zonder onderbrekend woord (bijvoorbeeld stop, nee, wacht) wordt in het beschermde venster weggelaten: het onderbreekt de zin niet, staat niet in de transcriptie en de agent reageert er niet op. Een onderbrekend woord of een uitdrukkelijk verzoek om niet te bellen onderbreekt hem wel. De onderbrekende woorden volgen de taal van de agent: een Nederlandstalige agent wordt onderbroken door stop, nee, wacht of momentje. null (of 0) schakelt de bescherming uit. Er wordt geen standaard toegepast: een ontbrekend veld bij POST slaat niets op en het antwoord toont null; een PATCH zonder de sleutel laat de opgeslagen waarde ongemoeid. Voor BEIDE providers geaccepteerd en opgeslagen - bij een agent op het standaardplatform ElevenLabs (provider: "elevenlabs") wordt de waarde alleen opgeslagen, en is hoorbaar zodra de agent naar de eigen spraakengine van volai overstapt. Een waarde buiten 0 tot 20 mislukt met de algemene validatiefout (validation, 400) - geen aparte code. |
silencePromptText | Uw eigen zin voor de herinnering bij stilte, tot 80 tekens, woordelijk uitgesproken (zonder variabelen te vervangen) in plaats van de standaardherinnering voor de taal van de agent ("Bent u er nog?" in het Nederlands). Eindig bij voorkeur met een vraagteken, zodat een kort "Ja" erna als antwoord wordt behouden. Omringende witruimte wordt weggeknipt; een lege string of null herstelt de standaardtekst voor de taal van de agent. Er wordt geen standaard toegepast: een ontbrekend veld bij POST slaat niets op en het antwoord toont null; een PATCH zonder de sleutel laat de opgeslagen tekst ongemoeid. Voor BEIDE providers geaccepteerd en opgeslagen, maar alleen hoorbaar op de eigen engine van volai en alleen zolang de herinnering bij stilte aan staat (silencePromptSecs is niet null). Tekst langer dan 80 tekens mislukt met de algemene validatiefout (validation, 400). |
inboundVariables | Standaardwaarden voor {{variable}}-plaatshouders bij INKOMENDE gesprekken, een object {"name": "value"} - bijvoorbeeld {"uvod": "Waarmee kan ik u helpen?"} voor een openingszin "... dit gesprek wordt opgenomen. {{uvod}}". Bij een uitgaand gesprek geeft u variabelen mee bij het bellen (variables in POST /v1/calls); bij een inkomend gesprek levert niemand ze en zou de engine lege tekst invullen, dus deze waarden worden bij inkomende gesprekken samengevoegd met de variabelen van het gesprek - alleen voor namen die het gesprek niet zelf levert (caller_number, called_number, attempt_id, volai_*, trial_* en system__* gaan altijd voor en de validatie weigert zulke namen). Niet gebruikt bij uitgaande gesprekken. Hoogstens 10 sleutels van letters, cijfers en onderstrepingstekens (1 tot 40 tekens), waarden zijn tekst tot 200 tekens (omringende witruimte wordt weggeknipt). Bij PATCH VERVANGT een object de opgeslagen waarden volledig (geen samenvoegen per sleutel), null of {} verwijdert ze, een weggelaten sleutel laat ze ongemoeid; bij POST slaat een weggelaten veld niets op. Het antwoord laat de sleutel weg wanneer de agent geen standaardwaarden heeft. Werkt voor BEIDE providers - de engine en ElevenLabs lezen dezelfde webhook. Een variabele met een standaardwaarde wordt niet meer gemeld door de waarschuwing prompt_variables_unavailable_inbound. Een vorm buiten deze limieten mislukt met de algemene validatiefout (validation, 400). |
sttLanguageHintsStrict | Strikte taalvergrendeling voor spraakherkenning (Soniox language_hints_strict): het transcriptiemodel geeft sterk de voorkeur aan de taal van de agent, zodat een kort antwoord als "Ano" bij een uitgaand gesprek niet als een vreemd woord of in een ander schrift wordt geschreven. Best effort, geen garantie. Alleen voor agents op de eigen engine van volai met taal cs en uitgaande gesprekken; zet het NIET aan voor sk - Tsjechische bellers zouden Slowaakse spelling krijgen. De taal van de agent wordt niet afgedwongen. true schakelt het in, false of null schakelt het uit; een ontbrekend veld bij POST slaat niets op en het antwoord toont false; een PATCH zonder de sleutel laat de opgeslagen waarde ongemoeid. Het antwoord bevat het veld altijd als boolean. Voor BEIDE providers geaccepteerd en opgeslagen, alleen gelezen door de engine - ElevenLabs negeert het. Het wordt bij elke opslag van de agent naar de engine gestuurd, dus opslaan in het portaal schakelt een ingeschakelde vergrendeling niet uit. |
turnDigitTailFloorMs | Ondergrens voor stilte in milliseconden voor een antwoord dat eindigt met een groep van 3 tot 8 cijfers (een beller die een nummer groep voor groep dicteert, haalt tussen de groepen adem): nadat de beller stopt, wacht de engine minstens zo lang voordat hij het antwoord afsluit, zodat de agent niet in het nummer springt. 0 schakelt het uit; anders 400 tot 2300 (1 tot 399 wordt geweigerd). De aanbevolen eerste stap voor Tsjechische agents die gedicteerde nummers opnemen is 800, de tweede stap 1200, het praktische plafond 1500. Datums, postcodes en jaartallen krijgen de ondergrens ook (een geaccepteerde prijs). Een ontbrekend veld bij POST slaat niets op en het antwoord toont null (de engine gebruikt zijn standaard 0); PATCH met null verwijdert de opgeslagen waarde, zonder de sleutel laat het haar ongemoeid. Voor BEIDE providers geaccepteerd en opgeslagen, alleen gelezen door de eigen engine van volai - ElevenLabs negeert het. Het wordt bij elke opslag van de agent naar de engine gestuurd, dus opslaan in het portaal schakelt een ingeschakelde ondergrens niet uit. |
turnLongSpeechFloorMs | Ondergrens voor stilte in milliseconden nadat de beller langer sprak dan turnLongSpeechSecs: na een lange spreekbeurt halen bellers adem en gaan door, de ondergrens geeft hun daarvoor tijd. 0 schakelt het uit; anders 400 tot 2300, voorgesteld 800. Dezelfde opslagregels als turnDigitTailFloorMs: een ontbrekend veld bij POST slaat niets op en het antwoord toont null (standaard van de engine 0), PATCH met null verwijdert de waarde. Alleen engine. |
turnLongSpeechSecs | Spreeklengte in seconden voor turnLongSpeechFloorMs, 1 tot 30 (decimalen zijn goed). Gemeten vanaf de eerste bevestigde spraak van het antwoord tot het laatste einde van spraak, pauzes inbegrepen; de ondergrens geldt voor spraak die STRIKT langer is dan dit. Heeft geen effect zolang turnLongSpeechFloorMs nul is. Een ontbrekend veld bij POST slaat niets op en het antwoord toont null (standaard van de engine 5), PATCH met null verwijdert de waarde. |
workflow | Splitst het gesprek in fasen (knooppunten) - zie het onderdeel Gespreksfasen (workflow) hieronder voor de vorm, de limieten en een voorbeeld. PATCH met null wist de fasen en brengt de agent terug naar één fase, zoals eerder. |
laughter | Of de agent tijdens het gesprek mag lachen wanneer de beller lacht of een grapje maakt: auto laat de beslissing aan de detectie van gevoelige onderwerpen (incasso, gezondheidszorg, overheid, uitvaartdiensten), on overschrijft die detectie, off onderdrukt het altijd; laughterEffective toont de feitelijke uitkomst voor een gegeven configuratie. Een weggelaten sleutel wordt bij POST als auto opgeslagen, en het antwoord bevat laughter altijd, nooit als ontbrekende sleutel. Voor BEIDE providers geaccepteerd en opgeslagen, maar alleen hoorbaar bij een agent op de engine met een Tsjechische Cartesia-stem - het heeft geen effect op ElevenLabs. De wijziging gaat in vanaf het volgende gesprek. |
Doorverbinden: wat de API werkelijk doet
Op de engine van volai wordt het doorverbinden overbrugd: de agent belt het doel als tweede deelnemer en valt na het opnemen stil. Doorverbinden werkt niet op het standaardplatform. transferTo start een poging om naar de engine over te stappen, maar een gepauzeerde poging, een niet beschikbare engine of een mislukking kan de agent op ElevenLabs laten staan; controleer provider. We kunnen geen gesprekssamenvatting meegeven op beide platforms, dus er is geen "warm doorverbinden".
Het tweede been is een gewoon uitgaand gesprek en wordt ook zo in rekening gebracht: 0,92 kr/min (≈ 0,04 EUR) zonder toeslag voor een agent. U herkent het in de gesprekslijst aan kind: "transfer".
Gespreksfasen (workflow)
Het optionele veld workflow splitst het gesprek in fasen (nodes). Een knooppunt is een SCOPE, geen script: het voegt alleen een prompt-, kennis- en toolsaanvulling toe die gelden zolang het gesprek erin zit, plus een optionele zin die de agent direct bij het binnengaan uitspreekt. Het model zelf beslist wanneer het tussen knooppunten wisselt door een tool aan te roepen - u scriptet dit nooit als vaste volgorde. De basis-systemPrompt van de agent moet alleen bevatten wat in ELKE fase waar is (identiteit, toon, hoe een bericht wordt opgenomen); de instructies van één fase horen in het eigen knooppunt, anders heeft het knooppunt geen effect. De basisprompt moet ook de twee zinnen bevatten zonder welke de agent het vaakst antwoordt wat hij niet weet: "Noem of raad geen operationele details die u niet in uw instructies hebt (openingstijden, termijnen, prijzen, wie beschikbaar is) - zeg dat een collega dat bevestigt." en "Vraag altijd maar naar één ding tegelijk."
entry is het id van het knooppunt waarin het gesprek begint. edges zijn overgangen: condition (5 tot 200 tekens) beschrijft WANNEER er moet worden gewisseld, en moet worden geschreven over wat de BELLER zei (nooit over wat de agent of een tool deed) - het komt letterlijk in de beschrijving van de tool die het model aanroept om te wisselen. Limieten: 2 tot 8 knooppunten, 1 tot 24 overgangen, hoogstens 5 overgangen die een knooppunt verlaten, slechts één overgang tussen twee knooppunten (in beide richtingen, duplicate_pair); de prompt van een knooppunt is tot 2.000 tekens, knowledge tot 4.000, prompt plus knowledge samen begrensd op 4.000 per knooppunt, en toolIds hoogstens 10 tools per knooppunt.
enterPhrase is een zin die eenmaal wordt uitgesproken bij het binnengaan van een knooppunt (tot 160 tekens). enterBehavior: "wait" zegt het model in diezelfde beurt daarna niets anders te zeggen en in plaats daarvan op de beller te wachten - meestal voor een gevoelige fase zoals een overlijdensmelding; het vereist enterPhrase, anders wait_without_phrase. Een knooppunt zonder eigen enterPhrase krijgt bij het binnengaan een korte standaardzin (in het Nederlands “Ik begrijp het.”) alleen op de eigen engine van volai (provider: "engine"); bij een agent die op ElevenLabs draait wordt er bij het binnengaan niets afgespeeld - een fasespecifieke zin klinkt natuurlijker en is de enige die bij beide wordt afgespeeld, daarom blijft de editor er via de waarschuwing missing_enter_phrase een aanbevelen.
Er is geen eindknooppunt - fasen beëindigen een gesprek nooit zelf. Een gesprek eindigt nog steeds alleen via het ingebouwde eindgedrag, precies zoals eerder.
enterBehavior: "wait" werkt ook door bij agents op ElevenLabs, niet alleen bij de eigen spraakengine van volai (provider: "engine"); de openingszin van het knooppunt wordt op de engine woordelijk uitgesproken, terwijl hij op ElevenLabs als instructie om hem uit te spreken in de prompt van de fase komt - het model zegt meestal iets wat er dicht bij komt, maar niet noodzakelijk de exacte woorden. Een knooppunt ZONDER eigen enterPhrase zegt bij het binnengaan op ElevenLabs helemaal niets: de korte standaardzin (in het Nederlands “Ik begrijp het.”) wordt alleen door de eigen engine van volai toegevoegd. Twee verschillen zijn groter. Een tool die ALLEEN aan een knooppunt is toegewezen (node.toolIds) komt nog niet door bij een agent op ElevenLabs - de fase draait daar zonder, en het antwoord markeert dat met de code workflow_node_tool_not_synced; wilt u hem daar wel, voeg hem dan ook toe aan de tools van de agent (het veld toolIds, het tabblad Geavanceerd in het portaal). Op de eigen engine van volai komt een knooppunttool ongewijzigd door. En workflowPath bij het gesprek wordt alleen door de post-call van de engine geschreven, dus een gesprek bij een agent op ElevenLabs bevat het nooit, ook al doorliep het gesprek fasen.
Een ongeldige graaf (een onbereikbaar knooppunt, een ontbrekend startknooppunt, een gereserveerde toolnaam...) geeft invalid_workflow en er wordt niets opgeslagen; de specifieke reden staat altijd in message.
Voorbeeld - de receptionist van een uitvaartonderneming
{
"workflow": {
"version": 1,
"entry": "triage",
"nodes": [
{
"id": "triage",
"label": "Triage",
"prompt": "Find out in one question why the caller is calling. You help callers report a death, arrange a grave dig or urn interment, and request headstone work."
},
{
"id": "bereavement",
"label": "Bereavement",
"prompt": "Find out the deceased's name, where the death occurred, whether the body is at a hospital or at home, a contact and a good time to call back. Promise a funeral home staff member will call within 15 minutes. Do not handle questions about a headstone or its price here - just say a colleague will go over that with the family, explain nothing, and return to the bereavement. Never bring up a headstone yourself.",
"enterPhrase": "My condolences.",
"enterBehavior": "wait"
},
{
"id": "burial",
"label": "Burial and interment",
"prompt": "The caller needs a grave dug, an urn interred, or a tomb opened. Find out the date, cemetery and grave number, a contact, and whether a clergy member needs to be arranged."
},
{
"id": "inquiry",
"label": "Work inquiry",
"prompt": "The caller wants new headstone work - an inscription, a repair or a grave restoration. Find out the cemetery, grave number, scope of work and a callback contact for a price."
}
],
"edges": [
{
"id": "triage_bereavement",
"from": "triage",
"to": "bereavement",
"condition": "The caller is reporting the death of a loved one or needs funeral services."
},
{
"id": "triage_burial",
"from": "triage",
"to": "burial",
"condition": "The caller needs a grave dug, an urn interred, or a tomb opened."
},
{
"id": "triage_inquiry",
"from": "triage",
"to": "inquiry",
"condition": "The caller wants new work: a headstone, an inscription, a repair or a grave restoration."
},
{
"id": "burial_bereavement",
"from": "burial",
"to": "bereavement",
"condition": "The caller reports the death of a loved one during the call."
},
{
"id": "inquiry_bereavement",
"from": "inquiry",
"to": "bereavement",
"condition": "The caller reports the death of a loved one during the call."
}
]
}
}Een gesprek dat fasen doorliep op de eigen spraakengine van volai (provider: "engine") bevat ook workflowPath in GET /v1/calls/{id} (en in de lijst GET /v1/calls): een array van stappen {node, label, viaEdge, turnIndex, at, enterPhraseSpoken}, at als unix-ms. De startstap heeft viaEdge: null. Een gesprek bij een agent die op ElevenLabs draait bevat het veld nooit, ook al doorliep het fasen - alleen de post-call van de engine schrijft het. Een gesprek zonder fasen laat het veld helemaal weg.
Agentconcepten
Een concept (draft) is een afzonderlijke momentopname van een agent in bewerking - u slaat er een wijziging in op, bekijkt die, probeert hem eventueel uit (simulatie hieronder) en zet hem pas daarna met één aanroep live op de agent. PUT /v1/agents/{id}/draft raakt de spraakprovider (ElevenLabs of de eigen engine van volai) of de telefoonroutering NOOIT - het slaat alleen het concept op. POST /v1/agents/{id}/draft/publish doet dat wel - het is de enige conceptstap die de live agent werkelijk wijzigt.
PATCH /v1/agents/{id} (zie het onderdeel Agents hierboven) raakt daarentegen de provider ZELF, METEEN, zonder concept en zonder tussenstap.
Publiceren kan elke fout van PATCH /v1/agents/{id} teruggeven - het is dezelfde schrijfactie naar de provider.
Concept, of PATCH?
PATCH - u bent de enige die de agent bewerkt en de wijziging moet direct live. Concept - een mens bewerkt de agent tegelijk ook in het volai-portaal (de portaleditor werkt UITSLUITEND via concepten, nooit via PATCH), u wilt de wijziging bekijken of uitproberen (simulatie) voordat hij live gaat, of u hebt optimistische gelijktijdigheid nodig (expectedRevision beschermt tegen het overschrijven van een bewerking van iemand anders die nog bezig is).
Wat er gebeurt als u het concept omzeilt
Het slot op een agent wordt GEDEELD tussen PATCH /v1/agents/{id} en POST .../draft/publish - een PATCH tijdens een lopende publicatie geeft 409 in_progress. Maar een PATCH buiten een lopende publicatie gaat gewoon door: hij verhoogt revision met 1, overschrijft published met de nieuwe live waarde, wist de markering tested (een eerdere simulatie geldt niet meer voor de nieuwe configuratie) en laat draft ongemoeid. Een concept in bewerking (meestal een mens in het portaal) krijgt dan bij het volgende opslaan 409 revision_conflict - dat is BEWUST zo, geen bug: een conflicterende PATCH mag nooit stilzwijgend verloren gaan.
De agenteditor in het portaal (/agent/{id}) werkt UITSLUITEND via concepten - zelfs een wijziging van één veld roept intern save_agent_draft + publish_agent_draft aan.
Velden van de conceptmomentopname
draft en published hebben in elk antwoord DEZELFDE vorm - AgentDraftSnapshot, een volledige configuratiemomentopname. De body van PUT/publish is .strict() - anders dan PATCH /v1/agents, dat een onbekende sleutel stilzwijgend weglaat, geeft een onbekende sleutel hier 400 validation. Het veld workflow wordt bij het opslaan van een concept alleen op zijn JSON-vorm gecontroleerd - de grafregels (bereikbare fasen, bekende tools, limieten per fase) worden gecontroleerd bij het publiceren en bij PATCH /v1/agents/{id}, dus een concept wordt ook opgeslagen met een graaf die publiceren met invalid_workflow weigert. Sommige velden zijn uitzonderingen op "elke keer een volledige momentopname": een ontbrekende silencePromptSecs in de momentopname betekent ongewijzigd, niet uitgeschakeld en niet de standaard 10 - bij een ElevenLabs-agent bevat het de opgeslagen waarde, die alleen hoorbaar is zodra de agent naar de engine van volai overstapt. Een ontbrekende firstMessageProtectSecs en silencePromptText betekenen ook ongewijzigd, niet uitgeschakeld en niet de standaardtekst (null schakelt de bescherming van de openingszin uit, of herstelt de standaardtekst van de herinnering). Een ontbrekende inboundVariables (standaardwaarden van variabelen voor inkomende gesprekken) betekent ook ongewijzigd - null of {} verwijdert de opgeslagen waarden, een object vervangt ze volledig. Een ontbrekende recordingNotice (de opnamemelding aan het einde van de openingszin) betekent ook ongewijzigd, niet uit - true/false stelt het in. Een ontbrekende sttLanguageHintsStrict (strikte taalvergrendeling voor spraakherkenning) betekent ook ongewijzigd - null schakelt de vergrendeling uit (verwijdert de opgeslagen sleutel), true/false stelt het in. Ontbrekende turnDigitTailFloorMs, turnLongSpeechFloorMs en turnLongSpeechSecs (ondergrenzen voor stilte aan het einde van het antwoord van de beller) betekenen ook ongewijzigd - null verwijdert de opgeslagen waarde (de engine keert terug naar zijn standaard 0 / 0 / 5), een getal stelt het in. Hetzelfde geldt voor een ontbrekende numberE164 - die betekent ook ongewijzigd, dus publiceren laat het nummer van de live agent zoals het is; alleen null koppelt het los en een string koppelt dat nummer, dezelfde drieledige betekenis die PATCH /v1/agents/{id} al heeft. Een string die overeenkomt met het huidige LIVE nummer van de agent op dat moment (niet met de opgeslagen momentopname published) telt ook als ongewijzigd - het gedraagt zich als een ontbrekende sleutel. Die controle wordt bij elke conceptlezing opnieuw beoordeeld tegen het nummer dat dan live is, dus het is geen blijvende beslissing: verhuist het nummer later buiten dit concept naar een andere agent, dan wordt dezelfde opgeslagen waarde weer een expliciete keuze en koppelt het volgende publiceren of terugdraaien hem hier opnieuw - niet stilzwijgend, maar gemarkeerd met een waarschuwing number_changed_by_publish in warnings.
| Veld | Opmerking |
|---|---|
name, language, systemPrompt, provider | name, language, systemPrompt en provider zijn VERPLICHT - een concept is een volledige momentopname, geen gedeeltelijke patch. |
provider | provider ("engine" of "elevenlabs") moet exact overeenkomen met de huidige provider van de agent, anders krijgt u invalid_draft - bij POST/PATCH /v1/agents wordt het helemaal niet verstuurd (de server leidt het zelf af). Het is het enige veld dat een concept naast een gewone agent bevat. |
toolIds, dataFields | toolIds en dataFields zijn elk begrensd op 10 items - dezelfde limiet als POST /v1/agents en PATCH /v1/agents/{id} (zie de tabel Nieuwe agentvelden hierboven). |
De overige velden (firstMessage, voiceId, transferTo, transferCondition, recordCalls, recordingNotice, goal, knowledge, notifyEmail) hebben dezelfde betekenis en limieten als bij POST/PATCH /v1/agents - zie de tabel Nieuwe agentvelden hierboven.
operation: wat de agent momenteel blokkeert
operation (zowel bij GET .../draft als bij GET .../draft/operation) is null wanneer niets de agent blokkeert, anders een object { id, kind, status, expectedRevision, createdAt, updatedAt }. De waarden van kind en status zijn teruggebracht tot wat u nodig hebt om te beslissen wat te doen - interne details (wie precies de bewerking in gang zette, de exacte fouttekst van de provider) verlaten de server nooit.
| kind | Opmerking |
|---|---|
update | Een live wijziging via PATCH /v1/agents/{id} (buiten het concept). |
publish | Het publiceren van een opgeslagen concept (POST .../draft/publish). |
rollback | Een terugdraaiing naar een eerdere gepubliceerde revisie uit de geschiedenis (POST .../draft/rollback) - net als publish raakt het de provider en de telefonie. |
outbound_task | Een uitgaande batchopdracht (een uitgaande campagne) - een andere batchopdracht gebruikt deze agent momenteel; publish/PATCH wacht tot die klaar is. |
| status | Opmerking |
|---|---|
running | De bewerking loopt momenteel bij de provider. |
uncertain | De provider heeft de wijziging mogelijk wel of niet toegepast - controleer de live agent en probeer de bewerking NIET blindelings opnieuw (zie POST .../draft/operation hieronder). |
drift | De live configuratie van de provider wijkt af van wat wij denken te hebben verstuurd - vereist een handmatige beslissing. |
failed | De bewerking is zeker mislukt - het concept is ongewijzigd, probeer het gerust opnieuw. |
done | De bewerking is geslaagd (een publicatie, een bevestigde terugdraaiing, of een veilig opgeloste onzekerheid). |
tested (zowel bij GET .../draft als in het antwoord van POST .../simulate) is { revision, testedAt } | null - wanneer PRECIES DEZE conceptrevisie voor het laatst met een simulatie is gecontroleerd. Elke verdere conceptwijziging (PUT) wist de markering; een simulatie is GEEN vereiste voor publiceren, het is slechts een optionele controle van de formulering voordat u live gaat.
history is een lijst van { revision, publishedAt } voor de laatste publicaties (zonder de opgeslagen configuratiemomentopnamen) - alleen het portaal bewaart de volledige geschiedenis inclusief inhoud.
GET/v1/agents/{id}/draft
Leest het concept, de laatst gepubliceerde configuratie, de revisie (voor expectedRevision bij verdere aanroepen), het resultaat van de laatste simulatie (tested) en of er momenteel iets de agent blokkeert (operation).
Verzoek
curl https://volai.cz/v1/agents/ag_kx91fa2b/draft \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"draft": {
"name": "Receptie",
"language": "nl",
"systemPrompt": "U bent de receptie van café Nula. U neemt bestellingen om af te halen aan...",
"firstMessage": "Goedendag, café Nula, wat mag het zijn?",
"voiceId": "milena",
"numberE164": "+420601234567",
"provider": "engine"
},
"published": {
"name": "Receptie",
"language": "nl",
"systemPrompt": "U bent de receptie van café Nula. U neemt bestellingen om af te halen aan...",
"firstMessage": "Goedendag, café Nula, waarmee kan ik u helpen?",
"voiceId": "milena",
"numberE164": "+420601234567",
"provider": "engine"
},
"revision": 4,
"tested": {
"revision": 4,
"testedAt": 1756118920000
},
"history": [
{
"revision": 3,
"publishedAt": 1756118840000
},
{
"revision": 2,
"publishedAt": 1756112640000
}
],
"operation": null
}Foutcodes
- 403
forbiddenDe gedeelde demoagent van volai is alleen-lezen. - 404
agent_not_foundDe agent bestaat niet, is verwijderd, of hoort niet bij uw account. - 500
storage_failedHet opslaan of lezen van het concept is aan onze kant mislukt - probeer het opnieuw.
PUT/v1/agents/{id}/draft
Slaat een concept op tegen expectedRevision. Verstuur de body als PRECIES het object dat u uit GET hebt gelezen, met één wijziging toegepast - de body is .strict(), dus zelfs een kleine onbekende sleutel geeft 400. Opslaan wijzigt de lopende agent NOOIT. Het voorbeeld van GET .../draft hierboven toont de toestand NA deze PUT (revisie 3 -> 4): daarom verstuurt deze PUT expectedRevision: 3 (de revisie waarop de wijziging was gebaseerd), en geeft de GET hierboven al revision: 4 terug (de nieuwe revisie na het opslaan).
Verzoek
curl -X PUT "https://volai.cz/v1/agents/ag_kx91fa2b/draft" \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"expectedRevision":3,"draft":{"name":"Receptie","language":"nl","systemPrompt":"U bent de receptie van café Nula. U neemt bestellingen om af te halen aan...","firstMessage":"Goedendag, café Nula, wat mag het zijn?","voiceId":"milena","numberE164":"+420601234567","provider":"engine"}}'Antwoord
{
"draft": {
"name": "Receptie",
"language": "nl",
"systemPrompt": "U bent de receptie van café Nula. U neemt bestellingen om af te halen aan...",
"firstMessage": "Goedendag, café Nula, wat mag het zijn?",
"voiceId": "milena",
"numberE164": "+420601234567",
"provider": "engine"
},
"revision": 4
}Foutcodes
- 400
validationOngeldige vorm van de body - zie de limieten bij het specifieke veld. - 400
invalid_draftDeprovidervan het concept komt niet overeen met deprovidervan de agent - een concept kan alleen binnen dezelfde provider worden bewerkt. - 403
forbiddenDe gedeelde demoagent van volai is alleen-lezen. - 404
agent_not_foundDe agent bestaat niet, is verwijderd, of hoort niet bij uw account. - 409
revision_conflictIemand anders heeft het concept intussen bewerkt (of omzeild met een PATCH). De fout bevatcurrentRevision- laad de huidige toestand viaGET .../draften bekijk het verschil voordat u het opslaan opnieuw probeert. - 409
publish_in_progressOp deze agent loopt momenteel een andere publicatie of batchopdracht - probeer het zo meteen opnieuw. - 409
operation_uncertainEen onzekere bewerking van een eerdere poging blokkeert deze agent - controleer de live agent en los het op viaGET/POST .../draft/operationvoordat u het opnieuw probeert. - 500
storage_failedHet opslaan of lezen van het concept is aan onze kant mislukt - probeer het opnieuw.
POST/v1/agents/{id}/draft/publish
Zet het opgeslagen concept live op de agent - de enige conceptstap die de provider (ElevenLabs of de engine van volai) en de telefoonroutering raakt. Accepteert een Idempotency-Key (zie Idempotentie hierboven) - een tweede aanroep met dezelfde sleutel binnen 24 uur geeft precies hetzelfde opgeslagen antwoord, of het nu slaagde of als operation_uncertain terugkwam.
Deployfouten (deploy) worden ONGEWIJZIGD doorgegeven - publiceren erft de HELE foutentabel van PATCH /v1/agents/{id} hierboven (in_progress, engine_unavailable, engine_not_configured, engine_readback_mismatch en de validatiecodes), bovenop zijn eigen conceptfoutentabel hierboven.
Verzoek
curl -X POST "https://volai.cz/v1/agents/ag_kx91fa2b/draft/publish" \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: REPLACE_WITH_UNIQUE_POST_V1_AGENTS_AG_KX91FA2B_DRAFT_PUBLISH_ID" \
-d '{"expectedRevision":4}'Vervang vóór het uitvoeren REPLACE_WITH_UNIQUE… door een unieke ID voor deze actie, bijvoorbeeld een UUID, en bewaar die. Gebruik dezelfde ID opnieuw als u hetzelfde verzoek na een time-out herhaalt. Gebruik een nieuwe ID voor een andere actie of een gewijzigde body, ook bij een ander endpoint.
Antwoord
{
"revision": 5,
"published": {
"name": "Receptie",
"language": "nl",
"systemPrompt": "U bent de receptie van café Nula. U neemt bestellingen om af te halen aan...",
"firstMessage": "Goedendag, café Nula, wat mag het zijn?",
"voiceId": "milena",
"numberE164": "+420601234567",
"provider": "engine"
}
}Foutcodes
- 400
validationOngeldige vorm van de body - zie de limieten bij het specifieke veld. - 400
invalid_draftDeprovidervan het concept komt niet overeen met deprovidervan de agent - een concept kan alleen binnen dezelfde provider worden bewerkt. - 403
forbiddenDe gedeelde demoagent van volai is alleen-lezen. - 404
agent_not_foundDe agent bestaat niet, is verwijderd, of hoort niet bij uw account. - 409
revision_conflictIemand anders heeft het concept intussen bewerkt (of omzeild met een PATCH). De fout bevatcurrentRevision- laad de huidige toestand viaGET .../draften bekijk het verschil voordat u het opslaan opnieuw probeert. - 409
publish_in_progressOp deze agent loopt momenteel een andere publicatie of batchopdracht - probeer het zo meteen opnieuw. - 409
operation_uncertainEen onzekere bewerking van een eerdere poging blokkeert deze agent - controleer de live agent en los het op viaGET/POST .../draft/operationvoordat u het opnieuw probeert. - 409
idempotency_in_progressEen gelijktijdige aanroep met dezelfdeIdempotency-Keyloopt nog - probeer het zo meteen opnieuw. - 500
storage_failedHet opslaan of lezen van het concept is aan onze kant mislukt - probeer het opnieuw. - 503
engine_not_configuredBij een agent op de eigen engine van volai: de engine is aan onze kant momenteel niet geconfigureerd, de publicatie is niet opgeslagen. Neem contact op met support. - 502
engine_unavailableBij een agent op de eigen engine van volai: de engine reageert nu niet, de publicatie is niet opgeslagen. Probeer het zo meteen opnieuw. Dezelfde code wordt teruggegeven bij een schrijfactie die door het standby-knooppunt van de engine werd geweigerd (de engine draait momenteel op een standby-knooppunt dat geen configuratie accepteert): de melding zegt dat, de publicatie is zeker niet opgeslagen, noch het concept noch de agent is als onzeker vergrendeld, en de publicatie kan over een paar minuten worden herhaald. - 502
engine_readback_mismatchBij een agent op de eigen engine van volai: de engine heeft een andere configuratie opgeslagen dan we verstuurden, de publicatie is niet opgeslagen. Probeer het opnieuw. - 409
in_progressOf er loopt nu een andere wijziging op de agent (een live PATCH of een andere publicatie), of een eerdere wijziging is doorgekomen maar kon niet veilig worden bevestigd - lees vóór het opnieuw proberen van de publicatieGET /v1/agents/{id}/draft/operationen beslis op basis vanoperation.
POST/v1/agents/{id}/draft/rollback
Draait de agent terug naar een eerdere GEPUBLICEERDE revisie uit de geschiedenis (targetRevision, zie het veld history bij GET .../draft) en zet die direct live bij de provider - het is een publicatie, niet slechts een overschreven concept. expectedRevision beschermt tegen een gelijktijdige wijziging op dezelfde manier als PUT .../draft. Anders dan .../draft/publish ZONDER Idempotency-Key - dezelfde body herhalen is op zichzelf veilig.
Deelt precies dezelfde twee foutbronnen als publiceren hierboven: de eigen conceptfoutentabel (validation, invalid_draft, forbidden, agent_not_found, revision_conflict, publish_in_progress, operation_uncertain, storage_failed) en de HELE foutentabel van PATCH /v1/agents/{id} (het deployen raakt de provider op dezelfde manier als PATCH/publish).
Verzoek
curl -X POST "https://volai.cz/v1/agents/ag_kx91fa2b/draft/rollback" \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"expectedRevision":4,"targetRevision":2}'Antwoord
{
"revision": 5,
"published": {
"name": "Receptie",
"language": "nl",
"systemPrompt": "U bent de receptie van café Nula. U neemt bestellingen om af te halen aan...",
"firstMessage": "Goedendag, café Nula, wat mag het zijn?",
"voiceId": "milena",
"numberE164": "+420601234567",
"provider": "engine"
}
}Foutcodes
- 400
validationOngeldige vorm van de body - zie de limieten bij het specifieke veld. - 400
invalid_drafttargetRevision staat niet meer tussen de bewaarde historische revisies (historybijGET .../draftbevat hem niet). - 403
forbiddenDe gedeelde demoagent van volai is alleen-lezen. - 404
agent_not_foundDe agent bestaat niet, is verwijderd, of hoort niet bij uw account. - 409
revision_conflictIemand anders heeft het concept intussen bewerkt (of omzeild met een PATCH). De fout bevatcurrentRevision- laad de huidige toestand viaGET .../draften bekijk het verschil voordat u het opslaan opnieuw probeert. - 409
publish_in_progressOp deze agent loopt momenteel een andere publicatie of batchopdracht - probeer het zo meteen opnieuw. - 409
operation_uncertainEen onzekere bewerking van een eerdere poging blokkeert deze agent - controleer de live agent en los het op viaGET/POST .../draft/operationvoordat u het opnieuw probeert. - 500
storage_failedHet opslaan of lezen van het concept is aan onze kant mislukt - probeer het opnieuw. - 503
engine_not_configuredBij een agent op de eigen engine van volai: de engine is aan onze kant momenteel niet geconfigureerd, de publicatie is niet opgeslagen. Neem contact op met support. - 502
engine_unavailableBij een agent op de eigen engine van volai: de engine reageert nu niet, de publicatie is niet opgeslagen. Probeer het zo meteen opnieuw. Dezelfde code wordt teruggegeven bij een schrijfactie die door het standby-knooppunt van de engine werd geweigerd (de engine draait momenteel op een standby-knooppunt dat geen configuratie accepteert): de melding zegt dat, de publicatie is zeker niet opgeslagen, noch het concept noch de agent is als onzeker vergrendeld, en de publicatie kan over een paar minuten worden herhaald. - 502
engine_readback_mismatchBij een agent op de eigen engine van volai: de engine heeft een andere configuratie opgeslagen dan we verstuurden, de publicatie is niet opgeslagen. Probeer het opnieuw. - 409
in_progressOf er loopt nu een andere wijziging op de agent (een live PATCH of een andere publicatie), of een eerdere wijziging is doorgekomen maar kon niet veilig worden bevestigd - lees vóór het opnieuw proberen van de publicatieGET /v1/agents/{id}/draft/operationen beslis op basis vanoperation.
GET/v1/agents/{id}/draft/operation
Leest de huidige toestand van een blokkerende bewerking (null wanneer niets de agent blokkeert) - dezelfde vorm als operation bij GET .../draft.
Verzoek
curl https://volai.cz/v1/agents/ag_kx91fa2b/draft/operation \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"operation": {
"id": "ado_4f2a91cd",
"kind": "publish",
"status": "uncertain",
"expectedRevision": 4,
"createdAt": 1756118900000,
"updatedAt": 1756118905000
}
}Foutcodes
- 403
forbiddenDe gedeelde demoagent van volai is alleen-lezen. - 404
agent_not_foundDe agent bestaat niet, is verwijderd, of hoort niet bij uw account. - 500
storage_failedHet opslaan of lezen van het concept is aan onze kant mislukt - probeer het opnieuw.
POST/v1/agents/{id}/draft/operation
Sluit, nadat u de live agent handmatig hebt gecontroleerd, veilig een uncertain bewerking af - u bevestigt dat u de toestand hebt bekeken, en de server verifieert de provider (een terugleesactie) en sluit de bewerking af.
Verzoek
curl -X POST https://volai.cz/v1/agents/ag_kx91fa2b/draft/operation \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"operationId":"ado_4f2a91cd","decision":"acknowledge_live_state"}'Antwoord
{
"reconciled": true,
"revision": 5
}Foutcodes
- 400
validationoperationId ontbreekt, of decision is niet"acknowledge_live_state". - 403
forbiddenDe gedeelde demoagent van volai is alleen-lezen. - 404
agent_not_foundDe agent bestaat niet, is verwijderd, of hoort niet bij uw account. - 409
revision_conflictIemand anders heeft het concept intussen bewerkt (of omzeild met een PATCH). De fout bevatcurrentRevision- laad de huidige toestand viaGET .../draften bekijk het verschil voordat u het opslaan opnieuw probeert. - 409
operation_uncertainEen onzekere bewerking van een eerdere poging blokkeert deze agent - controleer de live agent en los het op viaGET/POST .../draft/operationvoordat u het opnieuw probeert. - 500
storage_failedHet opslaan of lezen van het concept is aan onze kant mislukt - probeer het opnieuw.
POST/v1/agents/{id}/simulate
Verstuurt een tekstbericht aan het OPGESLAGEN concept (niet aan de live agent) en stelt de markering tested in. Gratis (0 CZK, volai betaalt), GEEN vereiste voor publiceren, begrensd op 20 per uur per account, draait op een vast Anthropic-model (de naam wordt nooit getoond), ZONDER tools en ZONDER spraak (ASR/TTS) - het is een controle van de formulering van de prompt, geen test van een werkelijk gesprek.
messages (optioneel) zijn eerdere gespreksbeurten ZONDER het huidige bericht - { role: "user" | "assistant", content }. Limieten: message 1 tot 4000 tekens, hoogstens 8 geschiedenisberichten, 2000 tekens per geschiedenisbericht, 8000 tekens geschiedenis in totaal.
Verzoek
curl -X POST https://volai.cz/v1/agents/ag_kx91fa2b/simulate \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"expectedRevision":4,"message":"Dobry den"}'Antwoord
{
"revision": 4,
"text": "Goedendag, café Nula, wat mag het zijn?",
"usage": {
"inputTokens": 412,
"outputTokens": 58
},
"tested": {
"revision": 4,
"testedAt": 1756118920000
}
}Foutcodes
- 400
validationOngeldige vorm van de body - zie de limieten bij het specifieke veld. - 403
forbiddenDe gedeelde demoagent van volai is alleen-lezen. - 404
agent_not_foundDe agent bestaat niet, is verwijderd, of hoort niet bij uw account. - 409
revision_conflictIemand anders heeft het concept intussen bewerkt (of omzeild met een PATCH). De fout bevatcurrentRevision- laad de huidige toestand viaGET .../draften bekijk het verschil voordat u het opslaan opnieuw probeert. - 429
rate_limitedU hebt de limiet van 20 simulaties per uur per account opgebruikt. - 502
simulation_failedHet Anthropic-model gaf een fout of een ongeldig antwoord - probeer het opnieuw. - 503
simulation_not_configuredSimulatie is aan onze kant tijdelijk niet geconfigureerd - neem contact op met support. - 503
simulation_unavailableHet simulatiequotum kon niet worden geverifieerd (een tijdelijke Redis-storing) - probeer het opnieuw. - 409
publish_in_progressDe simulatie zelf slaagde, maar het schrijven van de markeringtestedstuitte op een lopende conceptpublicatie - probeer het zo meteen opnieuw. - 409
operation_uncertainDe simulatie zelf slaagde, maar het schrijven van de markeringtestedstuitte op een onzekere bewerking van een eerdere poging - los die op viaGET/POST .../draft/operationvoordat u het opnieuw probeert. - 500
storage_failedDe simulatie zelf slaagde, maar het schrijven van de markeringtestedis aan onze kant mislukt - probeer het opnieuw.
POST/v1/agents/{id}/draft/knowledge-import
Leest de kennis van de agent uit een openbaar webadres (url) of uit de tekst van een .txt/.md-bestand tot 1 MB (text + filename) en schrijft die in het CONCEPT (knowledge plus de lijst knowledgeSources). Het adres moet een openbaar http/https-adres zijn (privé- en interne adressen, ook doorverwijzingen ernaartoe, worden geweigerd), de pagina hoogstens 2 MB en binnen 10 s antwoorden, met hoogstens 3 doorverwijzingen. Wat niet in de vrije ruimte van het kennisveld past (in totaal 20.000 tekens) wordt samengevat door een Anthropic-model; is dat niet beschikbaar, dan wordt de tekst eerlijk afgekapt (method: truncated). preview: true schrijft niets weg. Het wijzigt de live agent nooit, publiceren blijft expliciet. Beperkt tot 10 importen per uur per account.
Verzoek
curl -X POST https://volai.cz/v1/agents/ag_kx91fa2b/draft/knowledge-import \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/pricing","mode":"append","expectedRevision":4}'Antwoord
{
"saved": true,
"revision": 5,
"mode": "append",
"text": "### example.com/pricing\n\nHaircut 25 EUR\nColouring from 60 EUR\nOpen Mon-Fri 9:00-18:00",
"chars": 85,
"originalChars": 412,
"method": "verbatim",
"inputTrimmed": false,
"title": "Pricing",
"source": {
"url": "https://example.com/pricing",
"importedAt": 1793000000000,
"chars": 85
},
"knowledgeChars": 85
}Foutcodes
- 400
validationOngeldige vorm van de body - zie de limieten bij het specifieke veld. - 400
invalid_draftDeprovidervan het concept komt niet overeen met deprovidervan de agent - een concept kan alleen binnen dezelfde provider worden bewerkt. - 400
invalid_import_urlHet adres is geen geldighttp/https-adres, bevat inloggegevens of gebruikt een niet-standaardpoort. - 400
private_addressHet adres (of het doel van een doorverwijzing) wijst naar een intern netwerk, loopback of cloudmetadata. - 400
invalid_knowledgeEr is bijna geen ruimte meer in het kennisveld (in totaal 20.000 tekens) - kort de bestaande tekst in of verstuurmode: replace. - 403
forbiddenDe gedeelde demoagent van volai is alleen-lezen. - 404
agent_not_foundDe agent bestaat niet, is verwijderd, of hoort niet bij uw account. - 409
revision_conflictIemand anders heeft het concept intussen bewerkt (of omzeild met een PATCH). De fout bevatcurrentRevision- laad de huidige toestand viaGET .../draften bekijk het verschil voordat u het opslaan opnieuw probeert. - 409
publish_in_progressOp deze agent loopt momenteel een andere publicatie of batchopdracht - probeer het zo meteen opnieuw. - 409
operation_uncertainEen onzekere bewerking van een eerdere poging blokkeert deze agent - controleer de live agent en los het op viaGET/POST .../draft/operationvoordat u het opnieuw probeert. - 413
import_too_largeDe pagina is groter dan 2 MB, of het bestand is groter dan 1 MB. - 415
import_unsupported_typeHet adres is noch HTML noch een tekstbestand (pdf wordt nog niet ondersteund), of het bestand eindigt niet op.txt/.md. - 422
import_fetch_failedDe pagina kon niet worden gelezen: DNS, verbinding, een status die geen 2xx is, de timeout van 10 s, of meer dan 3 doorverwijzingen. - 422
import_emptyDe bron bevat geen bruikbare tekst. - 429
rate_limitedHoogstens 10 importen per uur per account. - 500
storage_failedHet opslaan of lezen van het concept is aan onze kant mislukt - probeer het opnieuw.
Stemmen
De stemcatalogus voor voiceId bij POST/PATCH /v1/agents. provider zegt waar de agent draait; useForOutboundTasks maakt de aanvankelijke keuze. Bij een bestaande engine-agent accepteert PATCH elke catalogusstem BEHALVE de ElevenLabs-stemmen (katty, de oudere anet); katty en ruwe ElevenLabs-ID's zijn alleen geldig op ElevenLabs (anet blijft daar alleen bij agents die hem al hebben). POST heeft één uitzondering: expliciete true vervangt een herkende stem die niet op de engine beschikbaar is, inclusief katty, door de standaardstem van de engine. Als weglaten via de standaard van de installatie de engine kiest, geeft dezelfde waarde 400; een ruwe ElevenLabs-ID wordt voor de engine altijd geweigerd. ?provider=engine geeft enginestemmen en ?provider=elevenlabs geeft ElevenLabs-stemmen. Een niet-herkende waarde geeft 400 validation en toont de geldige waarden. Dezelfde catalogus, met een beschrijving van elke stem, staat op de pagina Spraakagent.
GET/v1/voices
De catalogus van stemmen die beschikbaar zijn voor het voiceId van een agent.
Verzoek
curl https://volai.cz/v1/voices \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"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
},
{
"id": "ariel",
"name": "Ariel",
"gender": "female",
"provider": "elevenlabs",
"tone": "natural, conversational",
"description": "Dutch female voice - natural conversational delivery.",
"languages": [
"nl"
],
"preview": {
"studio": "https://volai.cz/hlasy/ariel-studio.mp3",
"phone": "https://volai.cz/hlasy/ariel-telefon.mp3"
},
"default": false
},
{
"id": "alexander",
"name": "Alexander",
"gender": "male",
"provider": "elevenlabs",
"tone": "warm, natural",
"description": "Dutch male voice - warm, natural delivery.",
"languages": [
"nl"
],
"preview": {
"studio": "https://volai.cz/hlasy/alexander-studio.mp3",
"phone": "https://volai.cz/hlasy/alexander-telefon.mp3"
},
"default": false
},
{
"id": "christine",
"name": "Christine",
"gender": "female",
"provider": "elevenlabs",
"tone": "conversational, relaxed",
"description": "Portuguese female voice - relaxed conversational delivery.",
"languages": [
"pt"
],
"preview": {
"studio": "https://volai.cz/hlasy/christine-studio.mp3",
"phone": "https://volai.cz/hlasy/christine-telefon.mp3"
},
"default": false
},
{
"id": "filipe",
"name": "Filipe",
"gender": "male",
"provider": "elevenlabs",
"tone": "natural, expressive",
"description": "Portuguese male voice - natural, expressive delivery.",
"languages": [
"pt"
],
"preview": {
"studio": "https://volai.cz/hlasy/filipe-studio.mp3",
"phone": "https://volai.cz/hlasy/filipe-telefon.mp3"
},
"default": false
},
{
"id": "mikidutza",
"name": "Mikidutza",
"gender": "female",
"provider": "elevenlabs",
"tone": "clear, friendly",
"description": "Romanian female voice - clear, friendly delivery.",
"languages": [
"ro"
],
"preview": {
"studio": "https://volai.cz/hlasy/mikidutza-studio.mp3",
"phone": "https://volai.cz/hlasy/mikidutza-telefon.mp3"
},
"default": false
},
{
"id": "oliver",
"name": "Oliver",
"gender": "male",
"provider": "elevenlabs",
"tone": "matter-of-fact, clear",
"description": "Romanian male voice - matter-of-fact, clear delivery.",
"languages": [
"ro"
],
"preview": {
"studio": "https://volai.cz/hlasy/oliver-studio.mp3",
"phone": "https://volai.cz/hlasy/oliver-telefon.mp3"
},
"default": false
},
{
"id": "isabella",
"name": "Isabella",
"gender": "female",
"provider": "elevenlabs",
"tone": "warm, calm",
"description": "Danish female voice - warm, calm delivery.",
"languages": [
"da"
],
"preview": {
"studio": "https://volai.cz/hlasy/isabella-studio.mp3",
"phone": "https://volai.cz/hlasy/isabella-telefon.mp3"
},
"default": false
},
{
"id": "soren",
"name": "Søren",
"gender": "male",
"provider": "elevenlabs",
"tone": "clear, confident",
"description": "Danish male voice - clear, confident delivery.",
"languages": [
"da"
],
"preview": {
"studio": "https://volai.cz/hlasy/soren-studio.mp3",
"phone": "https://volai.cz/hlasy/soren-telefon.mp3"
},
"default": false
},
{
"id": "yoana",
"name": "Yoana",
"gender": "female",
"provider": "elevenlabs",
"tone": "fresh, engaging",
"description": "Bulgarian female voice - fresh, engaging delivery.",
"languages": [
"bg"
],
"preview": {
"studio": "https://volai.cz/hlasy/yoana-studio.mp3",
"phone": "https://volai.cz/hlasy/yoana-telefon.mp3"
},
"default": false
},
{
"id": "peterk",
"name": "Peter K",
"gender": "male",
"provider": "elevenlabs",
"tone": "warm, smooth",
"description": "Bulgarian male voice - warm, smooth delivery.",
"languages": [
"bg"
],
"preview": {
"studio": "https://volai.cz/hlasy/peterk-studio.mp3",
"phone": "https://volai.cz/hlasy/peterk-telefon.mp3"
},
"default": false
},
{
"id": "soili",
"name": "Soili",
"gender": "female",
"provider": "elevenlabs",
"tone": "neutral, light",
"description": "Finnish female voice - neutral, light delivery.",
"languages": [
"fi"
],
"preview": {
"studio": "https://volai.cz/hlasy/soili-studio.mp3",
"phone": "https://volai.cz/hlasy/soili-telefon.mp3"
},
"default": false
},
{
"id": "jussi",
"name": "Jussi",
"gender": "male",
"provider": "elevenlabs",
"tone": "strong, lively",
"description": "Finnish male voice - strong, lively delivery.",
"languages": [
"fi"
],
"preview": {
"studio": "https://volai.cz/hlasy/jussi-studio.mp3",
"phone": "https://volai.cz/hlasy/jussi-telefon.mp3"
},
"default": false
},
{
"id": "crhysa",
"name": "Crhysa",
"gender": "female",
"provider": "elevenlabs",
"tone": "professional, lively",
"description": "Greek female voice - professional, lively delivery.",
"languages": [
"el"
],
"preview": {
"studio": "https://volai.cz/hlasy/crhysa-studio.mp3",
"phone": "https://volai.cz/hlasy/crhysa-telefon.mp3"
},
"default": false
},
{
"id": "iordanis",
"name": "Iordanis",
"gender": "male",
"provider": "elevenlabs",
"tone": "natural, conversational",
"description": "Greek male voice - natural conversational delivery.",
"languages": [
"el"
],
"preview": {
"studio": "https://volai.cz/hlasy/iordanis-studio.mp3",
"phone": "https://volai.cz/hlasy/iordanis-telefon.mp3"
},
"default": false
},
{
"id": "natasha",
"name": "Natasha",
"gender": "female",
"provider": "elevenlabs",
"tone": "soft, conversational",
"description": "French female voice - soft, conversational delivery.",
"languages": [
"fr"
],
"preview": {
"studio": "https://volai.cz/hlasy/natasha-studio.mp3",
"phone": "https://volai.cz/hlasy/natasha-telefon.mp3"
},
"default": false
},
{
"id": "max",
"name": "Max",
"gender": "male",
"provider": "elevenlabs",
"tone": "charismatic, friendly",
"description": "French male voice - charismatic, friendly delivery.",
"languages": [
"fr"
],
"preview": {
"studio": "https://volai.cz/hlasy/max-studio.mp3",
"phone": "https://volai.cz/hlasy/max-telefon.mp3"
},
"default": false
},
{
"id": "rossana",
"name": "Rossana",
"gender": "female",
"provider": "elevenlabs",
"tone": "warm, conversational",
"description": "Italian female voice - warm conversational delivery.",
"languages": [
"it"
],
"preview": {
"studio": "https://volai.cz/hlasy/rossana-studio.mp3",
"phone": "https://volai.cz/hlasy/rossana-telefon.mp3"
},
"default": false
},
{
"id": "marco",
"name": "Marco",
"gender": "male",
"provider": "elevenlabs",
"tone": "charismatic, friendly",
"description": "Italian male voice - charismatic, friendly delivery.",
"languages": [
"it"
],
"preview": {
"studio": "https://volai.cz/hlasy/marco-studio.mp3",
"phone": "https://volai.cz/hlasy/marco-telefon.mp3"
},
"default": false
},
{
"id": "zsofia",
"name": "Zsófia",
"gender": "female",
"provider": "elevenlabs",
"tone": "soft, inviting",
"description": "Hungarian female voice - soft, inviting delivery.",
"languages": [
"hu"
],
"preview": {
"studio": "https://volai.cz/hlasy/zsofia-studio.mp3",
"phone": "https://volai.cz/hlasy/zsofia-telefon.mp3"
},
"default": false
},
{
"id": "gabor",
"name": "Gábor",
"gender": "male",
"provider": "elevenlabs",
"tone": "warm, confident",
"description": "Hungarian male voice - warm, natural, confident delivery.",
"languages": [
"hu"
],
"preview": {
"studio": "https://volai.cz/hlasy/gabor-studio.mp3",
"phone": "https://volai.cz/hlasy/gabor-telefon.mp3"
},
"default": false
},
{
"id": "sanna",
"name": "Sanna",
"gender": "female",
"provider": "elevenlabs",
"tone": "direct, natural",
"description": "Swedish female voice - direct, natural delivery.",
"languages": [
"sv"
],
"preview": {
"studio": "https://volai.cz/hlasy/sanna-studio.mp3",
"phone": "https://volai.cz/hlasy/sanna-telefon.mp3"
},
"default": false
},
{
"id": "filip",
"name": "Filip",
"gender": "male",
"provider": "elevenlabs",
"tone": "energetic, expressive",
"description": "Swedish male voice - energetic, expressive delivery.",
"languages": [
"sv"
],
"preview": {
"studio": "https://volai.cz/hlasy/filip-studio.mp3",
"phone": "https://volai.cz/hlasy/filip-telefon.mp3"
},
"default": false
},
{
"id": "sarah-sl",
"name": "Sarah",
"gender": "female",
"provider": "elevenlabs",
"tone": "calm, confident",
"description": "Slovenian female voice - calm, confident delivery (multilingual voice, the model drives the pronunciation).",
"languages": [
"sl"
],
"preview": {
"studio": "https://volai.cz/hlasy/sarah-sl-studio.mp3",
"phone": "https://volai.cz/hlasy/sarah-sl-telefon.mp3"
},
"default": false
},
{
"id": "daniel-sl",
"name": "Daniel",
"gender": "male",
"provider": "elevenlabs",
"tone": "steady, clear",
"description": "Slovenian male voice - steady, broadcaster-style delivery (multilingual voice, the model drives the pronunciation).",
"languages": [
"sl"
],
"preview": {
"studio": "https://volai.cz/hlasy/daniel-sl-studio.mp3",
"phone": "https://volai.cz/hlasy/daniel-sl-telefon.mp3"
},
"default": false
},
{
"id": "alice-et",
"name": "Alice",
"gender": "female",
"provider": "elevenlabs",
"tone": "clear, engaging",
"description": "Estonian female voice - clear, engaging delivery (multilingual voice, the model drives the pronunciation).",
"languages": [
"et"
],
"preview": {
"studio": "https://volai.cz/hlasy/alice-et-studio.mp3",
"phone": "https://volai.cz/hlasy/alice-et-telefon.mp3"
},
"default": false
},
{
"id": "chris-et",
"name": "Chris",
"gender": "male",
"provider": "elevenlabs",
"tone": "down-to-earth, charming",
"description": "Estonian male voice - down-to-earth, charming delivery (multilingual voice, the model drives the pronunciation).",
"languages": [
"et"
],
"preview": {
"studio": "https://volai.cz/hlasy/chris-et-studio.mp3",
"phone": "https://volai.cz/hlasy/chris-et-telefon.mp3"
},
"default": false
},
{
"id": "matilda-lv",
"name": "Matilda",
"gender": "female",
"provider": "elevenlabs",
"tone": "professional, matter-of-fact",
"description": "Latvian female voice - knowledgeable, professional delivery (multilingual voice, the model drives the pronunciation).",
"languages": [
"lv"
],
"preview": {
"studio": "https://volai.cz/hlasy/matilda-lv-studio.mp3",
"phone": "https://volai.cz/hlasy/matilda-lv-telefon.mp3"
},
"default": false
},
{
"id": "george-lv",
"name": "George",
"gender": "male",
"provider": "elevenlabs",
"tone": "warm, captivating",
"description": "Latvian male voice - warm, captivating delivery (multilingual voice, the model drives the pronunciation).",
"languages": [
"lv"
],
"preview": {
"studio": "https://volai.cz/hlasy/george-lv-studio.mp3",
"phone": "https://volai.cz/hlasy/george-lv-telefon.mp3"
},
"default": false
},
{
"id": "sarah-lt",
"name": "Sarah",
"gender": "female",
"provider": "elevenlabs",
"tone": "calm, confident",
"description": "Lithuanian female voice - calm, confident delivery (multilingual voice, the model drives the pronunciation).",
"languages": [
"lt"
],
"preview": {
"studio": "https://volai.cz/hlasy/sarah-lt-studio.mp3",
"phone": "https://volai.cz/hlasy/sarah-lt-telefon.mp3"
},
"default": false
},
{
"id": "daniel-lt",
"name": "Daniel",
"gender": "male",
"provider": "elevenlabs",
"tone": "steady, clear",
"description": "Lithuanian male voice - steady, broadcaster-style delivery (multilingual voice, the model drives the pronunciation).",
"languages": [
"lt"
],
"preview": {
"studio": "https://volai.cz/hlasy/daniel-lt-studio.mp3",
"phone": "https://volai.cz/hlasy/daniel-lt-telefon.mp3"
},
"default": false
}
]
}Foutcodes
- 400
validationprovider is niet een van: elevenlabs, engine.
Het veld tone beschrijft de aard van de stem (rustig, zakelijk); languages bevat de taalcodes waarin de stem spreekt - cs: Milena, Jan, Tereza, Marek; sk: Katarina, Peter; en: Skylar, Daniel; de: Alina, Lukas; pl: Ewa, Kacper; nl: Ariel, Alexander; pt: Christine, Filipe; sl: Sarah, Daniel; ro: Mikidutza, Oliver; da: Isabella, Søren; et: Alice, Chris; lv: Matilda, George; bg: Yoana, Peter K; fi: Soili, Jussi; el: Crhysa, Iordanis; fr: Natasha, Max; it: Rossana, Marco; hu: Zsófia, Gábor; lt: Sarah, Daniel; sv: Sanna, Filip; preview geeft de URL's van de studio- en telefoonvoorbeelden; default markeert het aanbevolen item van de catalogus - in het portaal krijgt de kaart er alleen een badge 'Standaard' voor en wordt er zelf niets voorgeselecteerd. Een nieuwe agent die zonder voiceId is aangemaakt, schakelt er niet naar over - hij erft de standaardstem van zijn provider (Milena op de engine, de stem van de sjabloon op ElevenLabs; zie het onderdeel Agents hierboven).
Agenttools
Een tool is het adres van uw API dat de agent midden in een gesprek aanroept (een bestelling controleren, een boeking vastleggen) en waarvan hij het antwoord in zijn spraak gebruikt. Een tool hoort bij het account en wordt via toolIds aan een agent toegewezen - een tool aanmaken wijzigt op zichzelf geen enkele agent.
Limieten: hoogstens 10 tools per account, 10 parameters en 5 headers per tool, timeoutSecs 5 tot 30 (standaard 20). Het adres moet https en openbaar zijn - we weigeren interne netwerken en loopback (private_address), omdat onze server anders zou kunnen worden gebruikt om de infrastructuur van anderen af te tasten.
Bevestigingspoort (requireConfirmation, standaard false): voor een tool die iets verstuurt of aanmaakt (een sms, een verzoek) stuurt u requireConfirmation: true mee in POST /v1/tools of PATCH /v1/tools/{id}. De spraakengine van volai voert de tool dan alleen uit wanneer het laatste antwoord van de beller duidelijk ja zegt; anders wordt de tool niet uitgevoerd, krijgt de agent te horen opnieuw te vragen, en wordt een poging om het gesprek in dezelfde ronde te beëindigen tegengehouden. Het geldt alleen voor een agent op de engine van volai waarvan de taal cs of sk is (de engine kent toestemmingswoorden alleen in het Tsjechisch en Slowaaks; voor en, de en pl zou het zelfs een duidelijk 'Ja' blokkeren); voor elke andere agent wordt het veld genegeerd, maar het opslaan van de tool wordt nooit geweigerd vanwege de taal, omdat een tool door meerdere agents wordt gedeeld. ElevenLabs gebruikt het veld niet. De poort leest alleen het laatste antwoord van de beller en kent de vraag van de agent niet: hij vangt onduidelijke toestemming op ('Hallo?', 'Ja?', 'Ja, maar later'), niet elk 'Ja' dat op een andere vraag is gezegd. Een wijziging gaat bij een agent die al op de engine draait niet direct in: de tool wordt daar pas herschreven wanneer die agent de volgende keer wordt opgeslagen of gepubliceerd (POST /v1/agents/{id}/draft/publish, Publiceren in het portaal, of PATCH /v1/agents/{id}) - het bewerken van een tool herschrijft agents nooit uit zichzelf. Voor een lopende campagne maakt de manier uit: het ongewijzigd opnieuw publiceren van het geteste concept behoudt de testmarkering, dus de taak blijft lopen, nu met een poort die de test niet dekte; een directe PATCH /v1/agents/{id} wist de testmarkering en stopt de taak met agent_version_changed. Plan het inschakelen van de poort buiten een lopende campagne en test de campagne daarna opnieuw.
GET/v1/tools
De webhooktools van het account. Headerwaarden zijn altijd gemaskeerd.
Verzoek
curl https://volai.cz/v1/tools \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"tools": [
{
"id": "tl_5c2a91f4",
"name": "verify_order",
"label": "Bestelling controleren",
"description": "Zoekt de status van een bestelling op aan de hand van het nummer. Gebruik dit wanneer de beller vraagt waar zijn bestelling is.",
"url": "https://api.yourapp.com/orders",
"method": "POST",
"headers": { "Authorization": "Bear***" },
"params": [
{
"name": "order_number",
"type": "string",
"source": "llm",
"description": "Het bestelnummer dat de beller heeft opgegeven.",
"required": true
},
{ "name": "phone", "type": "string", "source": "caller_number" },
{ "name": "source", "type": "string", "source": "constant", "constantValue": "phone" }
],
"timeoutSecs": 20,
"requireConfirmation": false,
"createdAt": 1756111000000,
"updatedAt": 1756111000000
}
]
}POST/v1/tools
Maakt een tool aan. label is de leesbare naam; de naam die het model ziet wordt er automatisch van afgeleid (Verify order -> verify_order) en teruggegeven in het veld name. description is het enige waarop het model zijn beslissing baseert WANNEER de tool te gebruiken - schrijf het als instructie ("Gebruik wanneer de beller vraagt waar zijn bestelling is."), 10 tot 1000 tekens.
Verzoek
curl -X POST https://volai.cz/v1/tools \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"label": "Bestelling controleren",
"description": "Zoekt de status van een bestelling op aan de hand van het nummer. Gebruik dit wanneer de beller vraagt waar zijn bestelling is.",
"url": "https://api.yourapp.com/orders",
"method": "POST",
"headers": { "Authorization": "Bearer your_key" },
"params": [
{
"name": "order_number",
"type": "string",
"source": "llm",
"description": "Het bestelnummer dat de beller heeft opgegeven.",
"required": true
},
{ "name": "phone", "type": "string", "source": "caller_number" },
{ "name": "source", "type": "string", "source": "constant", "constantValue": "phone" }
],
"timeoutSecs": 20
}'Antwoord
{
"tool": {
"id": "tl_5c2a91f4",
"name": "verify_order",
"label": "Bestelling controleren",
"description": "Zoekt de status van een bestelling op aan de hand van het nummer. Gebruik dit wanneer de beller vraagt waar zijn bestelling is.",
"url": "https://api.yourapp.com/orders",
"method": "POST",
"headers": { "Authorization": "Bear***" },
"params": [
{
"name": "order_number",
"type": "string",
"source": "llm",
"description": "Het bestelnummer dat de beller heeft opgegeven.",
"required": true
},
{ "name": "phone", "type": "string", "source": "caller_number" },
{ "name": "source", "type": "string", "source": "constant", "constantValue": "phone" }
],
"timeoutSecs": 20,
"requireConfirmation": false,
"createdAt": 1756111000000,
"updatedAt": 1756111000000
}
}Foutcodes
- 400
tool_limitHet account heeft al 10 tools. - 400
invalid_tool_namelabel moet 1 tot 60 tekens lang zijn, of de ervan afgeleide naam zou gereserveerd zijn (end_call,transfer_to_human,switch_to_*). - 400
invalid_tool_urlurl is geen geldig adres, of geen https. - 400
private_addressurl wijst naar een intern netwerk of loopback. - 400
invalid_tool_paramsEen parameter heeft een ongeldige naam, een ongeldig type of een ongeldige bron, of er zijn er meer dan 10. - 400
invalid_tool_headersEen header is verboden (host, content-length, x-conversation-id, x-caller-id), dubbel, leeg, of er zijn er meer dan 5. - 400
validationdescription valt buiten 10 tot 1000 tekens, timeoutSecs valt buiten 5 tot 30, requireConfirmation is geen boolean, of method is iets anders dan GET/POST. - 502
eleven_labs_errorDe tool kon niet bij het spraakplatform worden aangemaakt - we hebben lokaal niets opgeslagen, probeer het opnieuw.
GET/v1/tools/{id}
Het detail van één tool - dezelfde velden als in de lijst, plus agents: de agents die hem via toolIds hebben ingeschakeld (alleen id en name; haal de volledige agent op via GET /v1/agents/{id}).
Verzoek
curl https://volai.cz/v1/tools/tl_5c2a91f4 \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"tool": {
"id": "tl_5c2a91f4",
"name": "verify_order",
"label": "Bestelling controleren",
"description": "Zoekt de status van een bestelling op aan de hand van het nummer. Gebruik dit wanneer de beller vraagt waar zijn bestelling is.",
"url": "https://api.yourapp.com/orders",
"method": "POST",
"headers": { "Authorization": "Bear***" },
"params": [
{
"name": "order_number",
"type": "string",
"source": "llm",
"description": "Het bestelnummer dat de beller heeft opgegeven.",
"required": true
},
{ "name": "phone", "type": "string", "source": "caller_number" },
{ "name": "source", "type": "string", "source": "constant", "constantValue": "phone" }
],
"timeoutSecs": 20,
"requireConfirmation": false,
"createdAt": 1756111000000,
"updatedAt": 1756111000000,
"agents": [{ "id": "ag_kx91fa2b", "name": "Receptie" }]
}
}Foutcodes
- 404
tool_not_foundDe tool bestaat niet of hoort niet bij uw account.
PATCH/v1/tools/{id}
Werkt een tool bij - alle velden optioneel, alleen de verstuurde worden gewijzigd. Twee dingen om op te letten: params en headers vervangen allebei de volledige bestaande lijst, en het wijzigen van label herschrijft ook de naam waaronder het model de tool kent - verwijst u ernaar in systemPrompt, werk dat dan tegelijk bij.
Headers komen alleen gemaskeerd terug ("Bear***"). Stuurt u precies die gemaskeerde waarde terug, dan beschouwen we dat als "behoud het origineel" - de gebruikelijke werkwijze "laad de tool, wijzig één veld, stuur het hele object terug" vernietigt uw inloggegevens dus niet. Stuur de echte waarde volledig mee om hem werkelijk te wijzigen.
Verzoek
curl -X PATCH https://volai.cz/v1/tools/tl_5c2a91f4 \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"timeoutSecs": 10}'Antwoord
{
"tool": {
"id": "tl_5c2a91f4",
"name": "verify_order",
"label": "Bestelling controleren",
"description": "Zoekt de status van een bestelling op aan de hand van het nummer. Gebruik dit wanneer de beller vraagt waar zijn bestelling is.",
"url": "https://api.yourapp.com/orders",
"method": "POST",
"headers": { "Authorization": "Bear***" },
"params": [
{
"name": "order_number",
"type": "string",
"source": "llm",
"description": "Het bestelnummer dat de beller heeft opgegeven.",
"required": true
},
{ "name": "phone", "type": "string", "source": "caller_number" },
{ "name": "source", "type": "string", "source": "constant", "constantValue": "phone" }
],
"timeoutSecs": 10,
"requireConfirmation": false,
"createdAt": 1756111000000,
"updatedAt": 1756111890000
}
}Foutcodes
- 400
invalid_tool_urlurl is geen geldig adres, of geen https. - 400
private_addressurl wijst naar een intern netwerk of loopback. - 400
invalid_tool_paramsEen parameter heeft een ongeldige naam, een ongeldig type of een ongeldige bron. - 400
invalid_tool_headersEen header is verboden, dubbel of leeg. - 400
validationdescription (indien verstuurd) valt buiten 10 tot 1000 tekens, timeoutSecs valt buiten 5 tot 30, requireConfirmation is geen boolean, of method is iets anders dan GET/POST. - 404
tool_not_foundDe tool bestaat niet of hoort niet bij uw account. - 502
eleven_labs_mismatchDe wijziging is niet precies zoals opgegeven bij het spraakplatform opgeslagen - we hebben lokaal niets overschreven.
DELETE/v1/tools/{id}
Verwijdert een tool en geeft de agents terug waarvoor hij niet meer werkt (alleen id en name; haal de volledige agent op via GET /v1/agents/{id}). Anders dan het vrijgeven van een nummer is dit een herstelbaar verlies - dezelfde tool kan opnieuw worden aangemaakt, alleen met een nieuw id dat dan opnieuw aan de agents moet worden toegewezen.
Verzoek
curl -X DELETE https://volai.cz/v1/tools/tl_5c2a91f4 \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"deleted": true,
"agents": [
{ "id": "ag_kx91fa2b", "name": "Receptie" }
]
}Foutcodes
- 404
tool_not_foundDe tool bestaat niet of hoort niet bij uw account. - 502
eleven_labs_errorDe tool kon niet bij het spraakplatform worden verwijderd - probeer het opnieuw.
POST/v1/tools/test
Verstuurt een ECHT verzoek volgens de definitie die u meestuurt en geeft terug wat er terugkwam - de tool wordt NIET opgeslagen (anders dan bij POST /v1/tools zijn label/description hier optioneel). Handig om een adres uit te proberen voordat u het als agenttool opslaat. timeoutSecs wordt hier alleen gevalideerd (5 tot 30) - een test wacht altijd een vaste 10 seconden, zodat u niet een volledige runtimetimeout hoeft uit te zitten; het is de waarde die de tool tijdens runtime zou gebruiken zodra hij is opgeslagen. Deelt een snelheidslimiet met POST /v1/tools/{id}/test hieronder - samen 10 pogingen per minuut per account.
Verzoek
curl -X POST https://volai.cz/v1/tools/test \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://api.yourapp.com/orders", "method": "GET", "params": [{"name": "order", "type": "string", "source": "llm", "description": "Order number."}]}'Antwoord
{
"status": 200,
"durationMs": 214,
"body": "{\"status\":\"shipped\"}",
"note": null
}Foutcodes
- 400
validationmethod anders dan GET/POST, of timeoutSecs buiten 5 tot 30. - 400
invalid_tool_urlurl is geen geldig adres, of er trad een netwerk-/DNS-/TLS-fout op bij het versturen van het verzoek (alles in die fase - ook een timeout - valt onder dezelfde code). - 400
private_addressurl wijst naar een intern netwerk of loopback (SSRF-bescherming) - gecontroleerd VÓÓR het versturen. - 400
invalid_tool_paramsEen parameter heeft een ongeldige naam, een ongeldig type of een ongeldige bron, of er zijn er meer dan 10. - 400
invalid_tool_headersEen header is verboden, dubbel, leeg, of er zijn er meer dan 5. - 429
rate_limitedU hebt 10 pogingen per minuut opgebruikt (gedeeld metPOST /v1/tools/{id}/test) - probeer het zo meteen opnieuw.
POST/v1/tools/{id}/test
Test een REEDS OPGESLAGEN tool op id - de body is altijd een lege {}, het endpoint neemt het adres, de methode en de ECHTE (ongemaskeerde) headers uit het opgeslagen record, niet uit de request body. private_address/invalid_tool_params/invalid_tool_headers kunnen hier niet terugkomen (het opgeslagen record doorloopt de invoervalidatie niet opnieuw) - een netwerkfout valt nog steeds onder invalid_tool_url.
Verzoek
curl -X POST https://volai.cz/v1/tools/tl_5c2a91f4/test \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{}'Antwoord
{
"status": 200,
"durationMs": 214,
"body": "{\"status\":\"shipped\"}",
"note": null
}Foutcodes
- 400
validationDe request body is niet leeg - verstuur{}, of helemaal geen body. - 400
invalid_tool_urlEen netwerk-/DNS-/TLS-fout (ook een timeout) bij het versturen van het echte verzoek. - 404
tool_not_foundDe tool bestaat niet of hoort niet bij uw account.
De parameters van een tool: het veld source
Elke parameter heeft een name, een type (string, number, boolean) en een source - waar de waarde vandaan komt. Bij GET komen parameters in de querystring; bij POST in de JSON-body.
| source | Waar de waarde vandaan komt | Wat u verder invult |
|---|---|---|
llm | Het model haalt het uit het gesprek. | description (precies wat erin moet) en optioneel required. |
caller_number | Het nummer van de beller in E.164, automatisch ingevuld. | Niets. Het model ziet dit veld nooit. |
called_number | Uw gebelde nummer in E.164, automatisch ingevuld. | Niets. Het model ziet dit veld nooit. |
constant | Een vaste waarde die u zelf opgeeft. | constantValue. |
Testgesprek
"Laat uw agent u bellen" - een echt uitgaand gesprek via makeAgentCall, alleen met een eigen dagplafond van 3 gesprekken per account (over alle agents heen), zodat een testgesprek niet als achterdeur om de gewone gesprekslimiet kan worden gebruikt.
POST/v1/agents/{id}/test-call
Belt u terug vanaf uw eigen agent - dezelfde prijs en dezelfde regels als POST /v1/calls, geen korting en geen bijzonder tarief. Een agent op de eigen spraakengine van volai belt zelfs ZONDER toegewezen nummer - vanaf de gedeelde lijn van volai, zodat de ontvanger het nummer van volai ziet, niet het uwe. Wijs een bestaande agent een eigen nummer toe via PATCH /v1/numbers/{e164} (routering agent) of het veld numberE164 bij de agent (POST/PATCH /v1/agents), of in het onderdeel Nummers van het portaal. Het antwoord bevat testCalls - max, remaining en resetAt (wanneer het venster van 24 uur dat met de eerste poging begon eindigt, epoch-ms) - en GET /v1/account geeft hetzelfde veld terug, zodat u kunt controleren wat er over is voordat u belt. Elke gebelde poging telt mee, ook een onbeantwoorde, die niet in rekening wordt gebracht.
Verzoek
curl -X POST https://volai.cz/v1/agents/ag_kx91fa2b/test-call \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"to": "+420777123456"}'Antwoord
{
"id": "c_9d4e2b7f",
"status": "initiated",
"testCalls": { "max": 3, "remaining": 2, "resetAt": 1755086400000 }
}Foutcodes
- 400
validationto ontbreekt of is langer dan 32 tekens, of de body is geen geldige JSON. - 400
destination_not_allowedDe agent gebruikt een nummer voor bellen en sms entois geen Tsjechisch nummer (+420): een nummer voor bellen en sms kan alleen Tsjechische nummers bellen. Geef een Tsjechischtomee. Er wordt niets afgeschreven. - 404
agent_not_foundagentId bestaat niet of hoort niet bij uw account. - 404
agent_no_numberAan de agent is geen telefoonnummer toegewezen - deze fout wordt alleen teruggegeven voor een agent op ElevenLabs; een agent op de eigen engine van volai belt ook zonder nummer, vanaf de gedeelde lijn van volai. - 403
agent_suspendedDe volai-beheerder heeft deze agent handmatig opgeschort - het testgesprek kan nu niet worden gevoerd. Een opgeschort account geeft in plaats daarvanaccount_suspended. - 402
insufficient_creditUw tegoed dekt het minimum om een gesprek te starten niet. - 402
subscription_cap_reachedHet account heeft een maandabonnement met het bestedingsplafond, het maandtegoed is op en het opgewaardeerde tegoed dekt de actie niet. Het vervangtinsufficient_creditvoor zo'n account (dezelfde HTTP-status). Er is niets afgeschreven. - 402
spend_cap_reachedHet door u ingestelde maandelijkse bestedingsplafond is bereikt. Er is niets geplaatst of verzonden en er is niets in rekening gebracht. Verhoog of verwijder het plafond metPUTofDELETE /v1/account/spend-cap, of wacht tot de volgende kalendermaand. - 503
capacity_busyAlle uitgaande lijnen zijn nu bezet, probeer het over een minuut opnieuw. Een nummer voor bellen en sms kan slechts één uitgaand gesprek tegelijk voeren. - 429
rate_limitedHet dagplafond van 3 testgesprekken per account is opgebruikt - een gewoon gesprek via POST /v1/calls heeft deze limiet niet.
Relay - eigen agent meenemen
Een eenmalige huur van een SIP-naam uit de relaypool, voor een uitgaand gesprek vanaf uw eigen spraakagent (ElevenLabs of een ander platform) - zonder toeslag voor een agent (die is normaal 2,50 kr/min (≈ 0,10 EUR), maar hier betaalt u ervoor op uw eigen platform). De volledige installatiehandleiding voor ElevenLabs staat op de pagina Eigen agent meenemen.
POST/v1/relay
Maakt een lease aan - to is het bestemmingsnummer, from is uw eigen volai-nummer waarvan het gesprek lijkt te komen. De teruggegeven sipName is de KALE naam voor de to_number van ElevenLabs (niet sipUri - ElevenLabs weigert een volledige SIP-URI). Stel de uitgaande trunk van uw platform in op outboundTrunkAddress uit GET /v1/numbers/{e164}/sip - een ander adres betekent dat het gesprek niet op het netwerk wordt gerouteerd en het platform een timeout meldt.
ttlSecs (optioneel, standaard 120) - hoeveel seconden de lease op het eerste gesprek wacht voordat een ongebruikte vervalt. Het toegestane bereik is 15 tot 300 seconden - daarbuiten geeft dit 400 validation.
Verzoek
curl -X POST https://volai.cz/v1/relay \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"to": "+420777123456", "from": "+420601234567"}'Antwoord
{
"id": "rl_4f2a91cd",
"sipName": "volai_relay_2",
"sipUri": "sip:volai_relay_2@sip.volai.cz",
"expiresAt": 1756111760000,
"callId": "c_9d4e2b7f"
}Foutcodes
- 400
invalid_numberto of from is geen geldig telefoonnummer. - 400
validationttlSecs valt buiten het toegestane bereik van 15 tot 300 seconden. - 404
from_number_not_ownedfrom hoort niet bij uw account. - 400
unsupported_for_number_typefromis uw eigen nummer voor bellen en sms, en alleen een volai-agent op de ingebouwde spraakengine mag ervandaan bellen, dus relay via een agent van een derde is er niet mee beschikbaar. Gebruik een spraaknummer voor relay. Er wordt niets afgeschreven. - 400
on_dncto staat op uw bel-mij-niet-lijst. - 400
relay_lease_limitU hebt al een actieve lease op dit nummer, of twee op het hele account. - 402
insufficient_creditUw tegoed dekt het minimum om een gesprek te starten niet. - 402
subscription_cap_reachedHet account heeft een maandabonnement met het bestedingsplafond, het maandtegoed is op en het opgewaardeerde tegoed dekt de actie niet. Het vervangtinsufficient_creditvoor zo'n account (dezelfde HTTP-status). Er is niets afgeschreven. - 402
spend_cap_reachedHet door u ingestelde maandelijkse bestedingsplafond is bereikt. Er is niets geplaatst of verzonden en er is niets in rekening gebracht. Verhoog of verwijder het plafond metPUTofDELETE /v1/account/spend-cap, of wacht tot de volgende kalendermaand. - 409
destination_busyEr loopt al een ander gesprek naar dat nummer. - 503
capacity_busyDe relaypool is nu vol, probeer het zo meteen opnieuw - hiervoor rekenen we niets af.
GET/v1/relay
De lijst van uw relay-leases - pending wacht op zijn eerste gesprek, active betekent dat er nu een gesprek loopt.
Verzoek
curl https://volai.cz/v1/relay \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"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}
Geeft de lease vrij en brengt het slot terug naar de pool, zolang hij nog op zijn eerste gesprek wacht (pending). Het beëindigt een lopend gesprek niet - geen API kan dat momenteel (niet het telefoonnetwerk, niet ElevenLabs) - zo'n lease (active) loopt gewoon uit; dit voorkomt alleen dat dezelfde lease opnieuw wordt gebruikt.
Verzoek
curl -X DELETE https://volai.cz/v1/relay/rl_4f2a91cd \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"cancelled": true
}Foutcodes
- 404
lease_not_foundDe lease bestaat niet, is verlopen, of hoort niet bij uw account. - 409
lease_already_activeOp de lease loopt al een gesprek - zo'n lease kan niet worden geannuleerd (hij loopt vanzelf uit). Geldt alleen voor DELETE /v1/relay/{id}, niet voor het aanmaken van een nieuwe lease.
Bel-mij-niet-lijst (DNC)
Nummers die uw agent (uw eigen of de ingebouwde) niet mag bellen - of het nu via POST /v1/calls, een testgesprek of relay is. DNC geldt alleen voor gesprekken - het versturen van een sms naar een nummer op de lijst wordt niet beperkt.
Naast de handmatige lijst blokkeert volai zelf automatisch (en tijdelijk) een bestemming - na 3 mislukte pogingen binnen 24 uur (bezet, onbeantwoord, geweigerd) stopt het 30 dagen met het bellen van dat nummer. De blokkade kan worden opgeheven in het portaal (Instellingen) of via POST /v1/dnc/{e164}/unblock - anders verloopt ze vanzelf.
GET/v1/dnc
De handmatige lijst (numbers) plus automatisch geblokkeerde bestemmingen (blocked) - blockedAt en expiresAt zijn Unix-milliseconden. Een automatische blokkade die vóór versie 1.8.0 begon, kan in blocked ontbreken; geeft een gesprek destination_auto_blocked, roep dan het unblock-endpoint rechtstreeks aan met dat nummer, ook zonder rij in de lijst.
Verzoek
curl https://volai.cz/v1/dnc \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"numbers": ["+420777998877"],
"blocked": [
{
"e164": "+420777123456",
"blockedAt": 1757600000000,
"expiresAt": 1760192000000
}
]
}POST/v1/dnc
Voegt nummers toe aan de bel-mij-niet-lijst. Verstuur precies één van e164 (één nummer) en e164s (een lijst van 1 tot 500 nummers). Het antwoord op e164 is { "added": true, "e164": "..." } en is ongewijzigd. Het antwoord op e164s bevat addedCount (nummers die niet op de lijst stonden en er nu op staan), alreadyOnListCount (geldige items die niets wijzigden omdat het nummer al op de lijst stond, ook een nummer dat in hetzelfde verzoek herhaald is), rejectedCount en rejected (items die geen geldig telefoonnummer zijn, elk met zijn index in de lijst, de input zoals verstuurd en reason: "invalid_number"). De drie aantallen tellen op tot het aantal verstuurde items. Een geweigerd item laat het verzoek niet mislukken: de geldige items worden altijd toegevoegd, dus een import van 500 contacten stopt niet bij één slecht nummer. Het verzoek is idempotent: dezelfde lijst opnieuw versturen voegt niets toe en geeft addedCount: 0.
Verzoek
curl -X POST https://volai.cz/v1/dnc \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"e164": "+420777998877"}'Antwoord
{
"added": true,
"e164": "+420777998877"
}Foutcodes
- 400
validationNoche164noche164sis verstuurd, beide zijn verstuurd,e164is geen geldig telefoonnummer,e164sis leeg of heeft meer dan 500 items, of een item is geen string of is langer dan 256 tekens.
Veel nummers tegelijk importeren
Verstuur e164s in plaats van e164 om tot 500 nummers in één verzoek toe te voegen. De API staat 60 verzoeken per minuut per sleutel toe voor alle aanroepen samen, dus 5.000 nummers kosten 10 verzoeken in plaats van 5.000. Items die geen geldig telefoonnummer zijn (ook langere tekst, zoals twee nummers in één cel) komen terug in rejected met de invoer afgekapt tot 32 tekens; de geldige worden toch toegevoegd. Een 200-antwoord alleen betekent niet dat er iets is toegevoegd: controleer rejectedCount. alreadyOnListCount maakt het veilig de aanroep na een timeout te herhalen. Nummers worden één voor één verwijderd via DELETE /v1/dnc/{e164}.
curl -X POST https://volai.cz/v1/dnc \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"e164s": ["+420777998877", "00420777998878", "+420777998879", "777 99"]}'{
"addedCount": 2,
"alreadyOnListCount": 1,
"rejectedCount": 1,
"rejected": [
{ "index": 3, "input": "777 99", "reason": "invalid_number" }
]
}DELETE/v1/dnc/{e164}
Verwijdert een nummer van de bel-mij-niet-lijst.
Verzoek
curl -X DELETE "https://volai.cz/v1/dnc/+420777998877" \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"removed": true,
"e164": "+420777998877"
}Foutcodes
- 400
validatione164 in de URL is geen geldig telefoonnummer.
POST/v1/dnc/{e164}/unblock
Heft een automatische blokkade van een bestemming op als die bestaat, ook een blokkade van vóór 1.8.0 die in de lijst blocked ontbreekt. unblocked komt terug als false, niet als 404, wanneer er geen was.
Verzoek
curl -X POST "https://volai.cz/v1/dnc/+420777998877/unblock" \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"unblocked": true,
"e164": "+420777998877"
}Foutcodes
- 400
validatione164 in de URL is geen geldig telefoonnummer.
Webhook
GET/v1/webhook
De huidige instellingen van de uitgaande webhook - zonder het ondertekeningsgeheim, dat alleen van PUT terugkomt.
Verzoek
curl https://volai.cz/v1/webhook \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"url": "https://tvoje-appka.cz/webhooks/volai",
"events": ["call.completed", "call.failed", "message.sent"],
"headers": { "Authorization": "Bear***" },
"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
Stelt de doel-URL en de gebeurtenissen waarop u zich abonneert in (of vervangt ze). Het antwoord bevat ook het ondertekeningsgeheim secret - sla het direct op, GET geeft het later niet meer terug. Een lege events: [] betekent abonneren op elke gebeurtenis, niet op geen - ook op later toegevoegde. Wilt u slechts enkele gebeurtenissen, noem ze dan. Omgekeerd annuleert een lege url: "" de webhook - volai verstuurt dan niets, en het antwoord geeft url: null terug. Hoe u de handtekening verifieert, staat op de pagina Webhooks. De optionele headers zijn eigen HTTP-headers die bij elke aflevering worden meegestuurd, bijvoorbeeld Authorization: Bearer <key> voor een doel dat alleen geauthenticeerde verzoeken accepteert. Ze komen ingekort terug (Bear***); een teruggestuurd masker behoudt de opgeslagen waarde, het veld weglaten behoudt de opgeslagen headers en {} verwijdert ze. De URL wijzigen naar een ander domein laat de opgeslagen headers vallen.
Verzoek
curl -X PUT "https://volai.cz/v1/webhook" \
-H "Authorization: Bearer vk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://tvoje-appka.cz/webhooks/volai","events":["call.completed","call.failed","message.sent"],"headers":{"Authorization":"Bearer tvuj-klic"}}'Antwoord
{
"url": "https://tvoje-appka.cz/webhooks/volai",
"secret": "whsec_9f2b7a1c4e6d8f0a",
"events": ["call.completed", "call.failed", "message.sent"],
"headers": { "Authorization": "Bear***" }
}Foutcodes
- 400
validationurl ontbreekt of is langer dan 2000 tekens, events heeft meer dan 20 items of een item is langer dan 100 tekens, ofheadersis geen object met stringwaarden. - 400
invalid_urlDe URL moet met https:// beginnen en naar een openbaar adres wijzen (localhost, interne netwerken en adressen met inloggegevens worden geweigerd; verstuur authenticatie in een eigen header viaheaders). - 400
invalid_eventsEen van de gebeurtenissen wordt niet herkend (call.completed, call.failed, call.missed, call.no_answer, message.sent, message.received). - 400
invalid_webhook_headersEen header kan niet worden verstuurd: een verboden, ongeldige of dubbele naam, een lege of te lange waarde (meer dan 1000 tekens), een teken buiten afdrukbaar ASCII inclusief een regeleinde, meer dan 5 headers, of een masker (***) in plaats van een waarde, zowel bij een nieuwe header als na het wijzigen van de URL naar een ander domein.
DELETE/v1/webhook
Verwijdert de webhook - hetzelfde effect als PUT /v1/webhook met een lege url, alleen expliciet benoemd. Stel op elk moment een nieuwe webhook in via PUT. De MCP-tegenhanger is remove_webhook; list_webhook_deliveries is de MCP-tegenhanger van GET /v1/webhook/deliveries hierboven.
Verzoek
curl -X DELETE https://volai.cz/v1/webhook \
-H "Authorization: Bearer $VOLAI_API_KEY"Antwoord
{"removed":true}POST/v1/webhook/test
Verstuurt één testpoging naar de geconfigureerde URL, ONGEACHT het filter events van PUT /v1/webhook - dit test de bezorgbaarheid, niet het abonnement. Het verstuurt ook uw eigen headers, dus het controleert ook de sleutel van het doel. Geen nieuwe poging; ook een mislukking wordt in de afleveringsgeschiedenis vastgelegd. Hoogstens 10 per uur.
Verzoek
curl -X POST https://volai.cz/v1/webhook/test \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"delivered": true,
"status": 200,
"attempts": 1,
"durationMs": 184
}Foutcodes
- 404
not_foundEr is geen webhook ingesteld - stel er eerst een in metPUT /v1/webhook. - 429
rate_limitedU hebt de limiet van 10 testgebeurtenissen per uur bereikt.
GET/v1/webhook/deliveries
De laatste 50 afleveringen (geslaagde, mislukte en testafleveringen), nieuwste eerst - dezelfde vorm als recentDeliveries bij GET /v1/webhook.
Verzoek
curl https://volai.cz/v1/webhook/deliveries \
-H "Authorization: Bearer vk_YOUR_KEY"Antwoord
{
"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
Open Koppelingen in het portaal en koppel een account: Google met toestemming in de browser (toegang tot het tonen van agenda's en afspraken), of Apple met het e-mailadres van uw Apple Account en een app-specifiek wachtwoord (vereist tweestapsverificatie); volai ontdekt iCloud-agenda's automatisch. REST en MCP gebruiken daarna de gewone volai API-sleutel van dat account en inloggegevens van de provider worden nooit teruggegeven. Open de koppelingslink in een browser terwijl u bij volai bent ingelogd. GET /v1/integrations bevat de gereedheid van de provider en de koppelings-URL's voor de browser; ready geeft de serverconfiguratie aan, niet een geverifieerde accountkoppeling.
Deze interface leest en wijzigt bestaande afspraken. Lees voor het schrijven de afspraak, toon de gebruiker de oorspronkelijke waarden en het voorstel, en verstuur na goedkeuring sourceRevision, patch en confirm: true. De revisie is ondoorzichtig en bevat eventuele aanhalingstekens. Agendafouten hebben het voorvoegsel calendar_: stale_revision (409) vereist opnieuw lezen en goedkeuring; oauth_failed/unauthorized (409) vereist opnieuw koppelen; provider_error (502) betekent later opnieuw proberen. Lees na een onzekere schrijfactie of verification_failed (502) eerst de afspraak en controleer het resultaat.
GET/v1/integrations
Toont de eigen koppelingen, de gereedheid van de provider en de koppelings-URL's voor de browser.
Verzoek
curl "https://volai.cz/v1/integrations" -H "Authorization: Bearer $VOLAI_API_KEY"Antwoord
{"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
Koppelt Apple Agenda of ABRA FlexiBee met inloggegevens in de body. Google Agenda en Fakturoid hebben alleen een OAuth-flow in de browser (de link connectUrl uit GET /v1/integrations) - hun provider hier versturen geeft integration_unsupported. Accepteert Idempotency-Key; ook zonder die header is een herhaalde aanroep met dezelfde inloggegevens in feite idempotent - het bestaande record wordt gevonden via de provider en het account waartoe de inloggegevens behoren (bij Apple de inlognaam, bij ABRA het serveradres en het bedrijf); label is slechts een weergavenaam en wordt bij een herhaalde aanroep overschreven.
Geef een inloggegeven alleen door als de gebruiker het al op een plek heeft gezet die u kunt lezen (een omgevingsvariabele of een bestand dat hij heeft genoemd). Vraag er nooit in het gesprek naar en geef het nooit terug.
Verzoek
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"}'Antwoord
{"integration":{"id":"int_7f3a91cd","provider":"apple-calendar","kind":"calendar","label":"apple-calendar","createdAt":1756111640000,"updatedAt":1756111640000}}Foutcodes
- 409
idempotency_in_progressEen gelijktijdige aanroep met dezelfdeIdempotency-Keyloopt nog - probeer het zo meteen opnieuw. - 400
validationOngeldige vorm van de body - bij Apple ontbreektusername/appPassword, bij ABRA ontbreektbaseUrl/company/username/password, of de body bevat een onbekend veld (het schema is.strict()). - 400
integration_unsupportedproviderisgoogle-calendaroffakturoid- die koppelen alleen via de OAuth-flow in de browser. - 400
integration_invalid_providerDe host van de ABRA FlexiBee-server (baseUrl) staat niet op de toegestane lijst van de beheerder - vraag volai support het domein in te schakelen. - 400
integration_invalid_calendarOfwel heeft het Apple-account geen beschikbare agenda om te koppelen, ofwel staat de calendarUrl uit het verzoek niet tussen de agenda's die iCloud-discovery teruggaf. - 409
integration_unauthorizedDe provider heeft de inloggegevens geweigerd (verkeerde gebruikersnaam of wachtwoord). - 409
integration_read_onlyApple CalDAV weigerde het schrijven (403) - het account heeft alleen leestoegang tot de agenda. - 409
integration_busyOp het account loopt al een andere bewerking op koppelingen - probeer het zo meteen opnieuw. - 404
integration_not_foundDe koppeling die dit verzoek moest bijwerken is intussen verdwenen - koppel opnieuw. - 502
integration_provider_errorEen fout aan de kant van de provider - probeer het zo meteen opnieuw. - 502
integration_invalid_provider_responseDe provider gaf een onverwacht antwoord - probeer het opnieuw en neem contact op met support als het blijft gebeuren. - 500
integration_storage_errorHet opslaan van de inloggegevens is aan onze kant mislukt - probeer het opnieuw.
DELETE/v1/integrations/{id}
Ontkoppelt elke koppeling (Google, Apple, Fakturoid, ABRA) - onomkeerbaar, zonder bevestiging en zonder MCP-tool, omdat ontkoppelen niet ongedaan kan worden gemaakt. Opnieuw koppelen via REST werkt alleen voor Apple en ABRA (zie POST /v1/integrations hierboven) - Google en Fakturoid gaan opnieuw via de OAuth-flow in de browser.
Verzoek
curl -X DELETE https://volai.cz/v1/integrations/int_7f3a91cd \
-H "Authorization: Bearer $VOLAI_API_KEY"Antwoord
{"disconnected":true}Foutcodes
- 404
integration_not_foundDe koppeling bestaat niet of hoort niet bij uw account. - 409
integration_busyOp de koppeling loopt al een andere bewerking (meestal een tokenvernieuwing) - probeer het zo meteen opnieuw.
GET/v1/integrations/{id}/calendars
Agenda's van een eigen koppeling. Haal het id uit de lijst met koppelingen.
Verzoek
curl "https://volai.cz/v1/integrations/int_CONNECTION/calendars" -H "Authorization: Bearer $VOLAI_API_KEY"Antwoord
{"calendars":[{"id":"primary","label":"Work","timezone":"Europe/Prague","accessRole":"owner"}]}Foutcodes
- 404
calendar_not_foundNoch de koppeling noch een agenda met dit id bestaat, of ze hoort niet bij uw account. - 400
calendar_unsupportedDe koppeling is geen agenda (maar facturatie), dus dit endpoint is er niet op van toepassing. - 409
calendar_read_onlyDe agenda is alleen-lezen gekoppeld (Apple CalDAV weigerde het schrijven) - controleer uw rechten op de gedeelde agenda in Apple Agenda. - 409
calendar_busyOp deze koppeling loopt op dit moment een andere bewerking (meestal een tokenvernieuwing) - probeer het zo meteen opnieuw. - 409
calendar_refresh_in_progressDe toegang tot het account wordt op de achtergrond vernieuwd - probeer het zo meteen opnieuw. - 409
calendar_unauthorizedDe koppeling heeft de toegang bij de provider verloren - koppel het account opnieuw in het portaal. - 409
calendar_oauth_failedInloggen bij het account van de provider is mislukt - koppel het account opnieuw in het portaal. - 409
calendar_credentials_unavailableDe opgeslagen inloggegevens van de koppeling konden niet worden ontsleuteld - koppel het account opnieuw in het portaal; neem contact op met support als het blijft gebeuren. - 409
calendar_invalid_credentialsDe provider heeft de opgeslagen inloggegevens van de koppeling geweigerd - koppel het account opnieuw in het portaal. - 503
calendar_not_configuredDe agendakoppeling is aan onze kant momenteel niet geconfigureerd - neem contact op met support. - 502
calendar_provider_errorEen fout aan de kant van de provider (Google, Apple) - probeer het zo meteen opnieuw. - 502
calendar_invalid_provider_responseDe provider gaf een onverwacht antwoord - probeer het opnieuw en neem contact op met support als het blijft gebeuren.
POST/v1/integrations/{id}/availability
Geeft vrije tijdsloten in een gekoppelde agenda terug ZONDER opgeslagen agent - dezelfde berekening als check_availability tijdens een gesprek, alleen zonder de regels van de agent. Als windows ontbreekt, geldt elke dag 24 uur; als u het meestuurt, heeft een weggelaten weekdag geen venster. to moet na from liggen en hoogstens 31 dagen later. Het antwoord bevat alleen start/end en timezone - geen titels of beschrijvingen van afspraken.
Verzoek
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"}]}}'Antwoord
{"slots":[{"start":"2026-09-22T09:00:00+02:00","end":"2026-09-22T09:30:00+02:00"}],"timezone":"Europe/Prague"}Foutcodes
- 404
calendar_not_foundNoch de koppeling noch een agenda met dit id bestaat, of ze hoort niet bij uw account. - 400
calendar_unsupportedDe koppeling is geen agenda (maar facturatie), dus dit endpoint is er niet op van toepassing. - 409
calendar_read_onlyDe agenda is alleen-lezen gekoppeld (Apple CalDAV weigerde het schrijven) - controleer uw rechten op de gedeelde agenda in Apple Agenda. - 409
calendar_busyOp deze koppeling loopt op dit moment een andere bewerking (meestal een tokenvernieuwing) - probeer het zo meteen opnieuw. - 409
calendar_refresh_in_progressDe toegang tot het account wordt op de achtergrond vernieuwd - probeer het zo meteen opnieuw. - 409
calendar_unauthorizedDe koppeling heeft de toegang bij de provider verloren - koppel het account opnieuw in het portaal. - 409
calendar_oauth_failedInloggen bij het account van de provider is mislukt - koppel het account opnieuw in het portaal. - 409
calendar_credentials_unavailableDe opgeslagen inloggegevens van de koppeling konden niet worden ontsleuteld - koppel het account opnieuw in het portaal; neem contact op met support als het blijft gebeuren. - 409
calendar_invalid_credentialsDe provider heeft de opgeslagen inloggegevens van de koppeling geweigerd - koppel het account opnieuw in het portaal. - 503
calendar_not_configuredDe agendakoppeling is aan onze kant momenteel niet geconfigureerd - neem contact op met support. - 502
calendar_provider_errorEen fout aan de kant van de provider (Google, Apple) - probeer het zo meteen opnieuw. - 502
calendar_invalid_provider_responseDe provider gaf een onverwacht antwoord - probeer het opnieuw en neem contact op met support als het blijft gebeuren. - 400
validationOngeldige vorm van de body (calendarId, from, to, ...) - de body is.strict(), een onbekend veld wordt geweigerd; from/to vereisen RFC3339 met een offset. - 400
calendar_invalid_calendarcalendarId komt niet overeen met een agenda uit GET .../calendars voor deze koppeling. - 400
calendar_too_many_eventsHet gevraagde bereik bevat meer afspraken dan één opvraging kan lezen - vraag een korter bereik (dagen in plaats van weken) en probeer het opnieuw. - 400
calendar_invalid_rangeto moet later zijn dan from en hoogstens 31 dagen erna liggen. - 400
calendar_invalid_timezonetimezone is geen geldige IANA-zone, bijvoorbeeld Europe/Prague. - 400
calendar_invalid_windowsEen venster heeft from vóór to in HH:MM nodig, hoogstens 4 vensters per dag, en vensters op dezelfde dag mogen elkaar niet overlappen. - 400
calendar_no_windowsGeen enkele weekdag heeft een beschikbaarheidsvenster - voeg er minstens één toe. - 400
calendar_invalid_rulesEen van de getallen valt buiten het bereik, of minNoticeMinutes is langer dan de hele zoekhorizon.
GET/v1/integrations/{id}/events
Verplichte query calendarId. Maximaal 50 Google-afspraken of Apple-afspraken en terugkerende reeksen, standaard de komende. Optioneel search, timeMin, timeMax en pageToken. Tijdgrenzen hebben offsets nodig. Ga verder met nextPageToken en identieke filters, inclusief een vaste timeMin. Apple recurring:true geeft een hele reeks aan; start/end beschrijven de oorspronkelijke afspraak, en wijzigingen passen de reeksmaster aan met behoud van afzonderlijke uitzonderingen. Een onbekende queryparameter wordt hier geweigerd met 400 validation - anders dan in de rest van v1 worden extra parameters niet genegeerd.
Verzoek
curl "https://volai.cz/v1/integrations/int_CONNECTION/events?calendarId=primary" -H "Authorization: Bearer $VOLAI_API_KEY"Antwoord
{"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"}]}Foutcodes
- 404
calendar_not_foundNoch de koppeling noch een agenda met dit id bestaat, of ze hoort niet bij uw account. - 400
calendar_unsupportedDe koppeling is geen agenda (maar facturatie), dus dit endpoint is er niet op van toepassing. - 409
calendar_read_onlyDe agenda is alleen-lezen gekoppeld (Apple CalDAV weigerde het schrijven) - controleer uw rechten op de gedeelde agenda in Apple Agenda. - 409
calendar_busyOp deze koppeling loopt op dit moment een andere bewerking (meestal een tokenvernieuwing) - probeer het zo meteen opnieuw. - 409
calendar_refresh_in_progressDe toegang tot het account wordt op de achtergrond vernieuwd - probeer het zo meteen opnieuw. - 409
calendar_unauthorizedDe koppeling heeft de toegang bij de provider verloren - koppel het account opnieuw in het portaal. - 409
calendar_oauth_failedInloggen bij het account van de provider is mislukt - koppel het account opnieuw in het portaal. - 409
calendar_credentials_unavailableDe opgeslagen inloggegevens van de koppeling konden niet worden ontsleuteld - koppel het account opnieuw in het portaal; neem contact op met support als het blijft gebeuren. - 409
calendar_invalid_credentialsDe provider heeft de opgeslagen inloggegevens van de koppeling geweigerd - koppel het account opnieuw in het portaal. - 503
calendar_not_configuredDe agendakoppeling is aan onze kant momenteel niet geconfigureerd - neem contact op met support. - 502
calendar_provider_errorEen fout aan de kant van de provider (Google, Apple) - probeer het zo meteen opnieuw. - 502
calendar_invalid_provider_responseDe provider gaf een onverwacht antwoord - probeer het opnieuw en neem contact op met support als het blijft gebeuren. - 400
validationOngeldige vorm van de query (bijvoorbeeld timeMax ligt niet na timeMin, of er is een onbekende parameter) - zie de limieten bij elke parameter hierboven. - 400
calendar_invalid_calendarcalendarId komt niet overeen met een agenda uit GET .../calendars voor deze koppeling. - 400
calendar_invalid_queryOngeldige pageToken of Apple eventId. Gebruik externalId uit een verse afsprakenlijst en laat de paginafilters ongewijzigd. - 409
calendar_stale_revisionsourceRevision komt niet overeen met de huidige revisie van de afspraak - iemand heeft hem intussen gewijzigd. Lees hem opnieuw, toon de gebruiker de nieuwe toestand en laat het nieuwe voorstel goedkeuren. Bij een Apple-afsprakenlijst betekent dezelfde code dat de lijst tussen twee pagina's is veranderd: laat de pageToken weg en lees de eerste pagina opnieuw.
POST/v1/integrations/{id}/events
Maakt na uitdrukkelijke bevestiging van de gebruiker (confirm: true) een afspraak aan in een gekoppelde agenda. Geef precies één van end of durationMinutes op. idempotencyKey is optioneel - dezelfde waarde opnieuw versturen geeft de reeds aangemaakte afspraak terug in plaats van een tweede schrijfactie. Vlak voor het schrijven wordt gecontroleerd of het tijdslot nog vrij is; het antwoord is de vers gelezen afspraak, in dezelfde vorm als GET .../events/{eventId}.
Verzoek
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}'Antwoord
{
"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"
}
}Foutcodes
- 404
calendar_not_foundNoch de koppeling noch een agenda met dit id bestaat, of ze hoort niet bij uw account. - 400
calendar_unsupportedDe koppeling is geen agenda (maar facturatie), dus dit endpoint is er niet op van toepassing. - 409
calendar_read_onlyDe agenda is alleen-lezen gekoppeld (Apple CalDAV weigerde het schrijven) - controleer uw rechten op de gedeelde agenda in Apple Agenda. - 409
calendar_busyOp deze koppeling loopt op dit moment een andere bewerking (meestal een tokenvernieuwing) - probeer het zo meteen opnieuw. - 409
calendar_refresh_in_progressDe toegang tot het account wordt op de achtergrond vernieuwd - probeer het zo meteen opnieuw. - 409
calendar_unauthorizedDe koppeling heeft de toegang bij de provider verloren - koppel het account opnieuw in het portaal. - 409
calendar_oauth_failedInloggen bij het account van de provider is mislukt - koppel het account opnieuw in het portaal. - 409
calendar_credentials_unavailableDe opgeslagen inloggegevens van de koppeling konden niet worden ontsleuteld - koppel het account opnieuw in het portaal; neem contact op met support als het blijft gebeuren. - 409
calendar_invalid_credentialsDe provider heeft de opgeslagen inloggegevens van de koppeling geweigerd - koppel het account opnieuw in het portaal. - 503
calendar_not_configuredDe agendakoppeling is aan onze kant momenteel niet geconfigureerd - neem contact op met support. - 502
calendar_provider_errorEen fout aan de kant van de provider (Google, Apple) - probeer het zo meteen opnieuw. - 502
calendar_invalid_provider_responseDe provider gaf een onverwacht antwoord - probeer het opnieuw en neem contact op met support als het blijft gebeuren. - 400
validationOngeldige vorm van de body - precies één van end/durationMinutes ontbreekt, een lege of te lange title, of een onbekend veld (.strict()). - 400
calendar_invalid_calendarcalendarId komt niet overeen met een agenda uit GET .../calendars voor deze koppeling. - 400
calendar_invalid_rangeto moet later zijn dan from en hoogstens 31 dagen erna liggen. - 400
calendar_confirmation_requiredconfirm moet true zijn - zonder uitdrukkelijke goedkeuring van de gebruiker wordt de afspraak niet aangemaakt. - 409
calendar_slot_takenDe gevraagde tijd is nu niet vrij - lees de beschikbaarheid opnieuw (POST .../availability) en kies een andere tijd. - 503
calendar_unavailableDe agenda is nu niet bereikbaar om te lezen of te schrijven - probeer het zo meteen opnieuw.
GET/v1/integrations/{id}/events/{eventId}
Verplichte query calendarId. Verse afspraak met sourceRevision en fetchedAt. Codeer id's in beide paden en queryvalues als URL. Geef bij Apple de ondoorzichtige externalId uit de afsprakenlijst ongewijzigd door als eventId. Die verwijst naar de CalDAV-resource, niet naar de iCalendar-UID; bouw of decodeer hem niet zelf.
Verzoek
curl "https://volai.cz/v1/integrations/int_CONNECTION/events/EVENT_ID?calendarId=primary" -H "Authorization: Bearer $VOLAI_API_KEY"Antwoord
{
"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"
}
}Foutcodes
- 404
calendar_not_foundNoch de koppeling noch een agenda met dit id bestaat, of ze hoort niet bij uw account. - 400
calendar_unsupportedDe koppeling is geen agenda (maar facturatie), dus dit endpoint is er niet op van toepassing. - 409
calendar_read_onlyDe agenda is alleen-lezen gekoppeld (Apple CalDAV weigerde het schrijven) - controleer uw rechten op de gedeelde agenda in Apple Agenda. - 409
calendar_busyOp deze koppeling loopt op dit moment een andere bewerking (meestal een tokenvernieuwing) - probeer het zo meteen opnieuw. - 409
calendar_refresh_in_progressDe toegang tot het account wordt op de achtergrond vernieuwd - probeer het zo meteen opnieuw. - 409
calendar_unauthorizedDe koppeling heeft de toegang bij de provider verloren - koppel het account opnieuw in het portaal. - 409
calendar_oauth_failedInloggen bij het account van de provider is mislukt - koppel het account opnieuw in het portaal. - 409
calendar_credentials_unavailableDe opgeslagen inloggegevens van de koppeling konden niet worden ontsleuteld - koppel het account opnieuw in het portaal; neem contact op met support als het blijft gebeuren. - 409
calendar_invalid_credentialsDe provider heeft de opgeslagen inloggegevens van de koppeling geweigerd - koppel het account opnieuw in het portaal. - 503
calendar_not_configuredDe agendakoppeling is aan onze kant momenteel niet geconfigureerd - neem contact op met support. - 502
calendar_provider_errorEen fout aan de kant van de provider (Google, Apple) - probeer het zo meteen opnieuw. - 502
calendar_invalid_provider_responseDe provider gaf een onverwacht antwoord - probeer het opnieuw en neem contact op met support als het blijft gebeuren. - 400
validationcalendarId in de query is verplicht en maximaal 500 tekens lang. - 400
calendar_invalid_calendarcalendarId komt niet overeen met een agenda uit GET .../calendars voor deze koppeling. - 400
calendar_invalid_queryOngeldige pageToken of Apple eventId. Gebruik externalId uit een verse afsprakenlijst en laat de paginafilters ongewijzigd.
PATCH/v1/integrations/{id}/events/{eventId}
Wijzig titel, start of einde. Gebruik RFC3339-tijdstempels met offset, of hele dagen als YYYY-MM-DD met een exclusief einde. Zwevende Apple-tijden mogen de offset alleen weglaten als een bestaande zwevende afspraak behouden blijft. Vereist bevestiging en een actuele revisie; geeft de geverifieerde afspraak terug.
Verzoek
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}'Antwoord
{
"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"
}
}Foutcodes
- 404
calendar_not_foundNoch de koppeling noch een agenda met dit id bestaat, of ze hoort niet bij uw account. - 400
calendar_unsupportedDe koppeling is geen agenda (maar facturatie), dus dit endpoint is er niet op van toepassing. - 409
calendar_read_onlyDe agenda is alleen-lezen gekoppeld (Apple CalDAV weigerde het schrijven) - controleer uw rechten op de gedeelde agenda in Apple Agenda. - 409
calendar_busyOp deze koppeling loopt op dit moment een andere bewerking (meestal een tokenvernieuwing) - probeer het zo meteen opnieuw. - 409
calendar_refresh_in_progressDe toegang tot het account wordt op de achtergrond vernieuwd - probeer het zo meteen opnieuw. - 409
calendar_unauthorizedDe koppeling heeft de toegang bij de provider verloren - koppel het account opnieuw in het portaal. - 409
calendar_oauth_failedInloggen bij het account van de provider is mislukt - koppel het account opnieuw in het portaal. - 409
calendar_credentials_unavailableDe opgeslagen inloggegevens van de koppeling konden niet worden ontsleuteld - koppel het account opnieuw in het portaal; neem contact op met support als het blijft gebeuren. - 409
calendar_invalid_credentialsDe provider heeft de opgeslagen inloggegevens van de koppeling geweigerd - koppel het account opnieuw in het portaal. - 503
calendar_not_configuredDe agendakoppeling is aan onze kant momenteel niet geconfigureerd - neem contact op met support. - 502
calendar_provider_errorEen fout aan de kant van de provider (Google, Apple) - probeer het zo meteen opnieuw. - 502
calendar_invalid_provider_responseDe provider gaf een onverwacht antwoord - probeer het opnieuw en neem contact op met support als het blijft gebeuren. - 400
validationOngeldige vorm van de body (calendarId, sourceRevision, patch, confirm) - de body is.strict(), een onbekend veld wordt geweigerd. - 400
calendar_invalid_calendarcalendarId komt niet overeen met een agenda uit GET .../calendars voor deze koppeling. - 400
calendar_invalid_queryOngeldige pageToken of Apple eventId. Gebruik externalId uit een verse afsprakenlijst en laat de paginafilters ongewijzigd. - 400
calendar_provider_mismatchDe koppeling is intussen van provider gewisseld, dus het voorstel uit de eerdere leesactie hoort er niet meer bij - lees de afspraak opnieuw en laat het nieuwe voorstel goedkeuren. - 400
calendar_invalid_patchpatch heeft een ongeldige vorm of een onbekend veld - alleen title, start en end zijn toegestaan; start/end hebben een offset nodig (of een hele dag als YYYY-MM-DD met een later einde). - 400
calendar_confirmation_requiredconfirm moet true zijn - zonder bevestiging van de gebruiker wordt de afspraak niet gewijzigd. - 409
calendar_stale_revisionsourceRevision komt niet overeen met de huidige revisie van de afspraak - iemand heeft hem intussen gewijzigd. Lees hem opnieuw, toon de gebruiker de nieuwe toestand en laat het nieuwe voorstel goedkeuren. Bij een Apple-afsprakenlijst betekent dezelfde code dat de lijst tussen twee pagina's is veranderd: laat de pageToken weg en lees de eerste pagina opnieuw. - 502
calendar_verification_failedHet schrijven is gelukt, maar het terugschrijven bij de provider kon niet worden geverifieerd - lees de afspraak opnieuw en controleer het resultaat.
POST/v1/integrations/{id}/events/{eventId}/propose
Bereidt een voorgestelde wijziging van titel, start of einde van een afspraak voor ZONDER bij de provider te schrijven (patch en sourceRevision zoals bij PATCH .../events/{eventId}, maar zonder confirm) - een zuivere functie over het opgeslagen koppelingstype, die de provider nooit aanroept. Toon het voorstel (proposal.patch) aan de gebruiker ter uitdrukkelijke goedkeuring en verstuur daarna PATCH .../events/{eventId} met dezelfde waarden en confirm: true.
Verzoek
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"}}'Antwoord
{"proposal":{"calendarId":"primary","externalId":"EVENT_ID","sourceRevision":"revision1","patch":{"title":"Updated meeting"},"provider":"google-calendar","proposedAt":"2026-09-08T09:00:00.000Z","requiresExplicitConfirmation":true}}Foutcodes
- 400
validationOngeldige vorm van de body -calendarId/sourceRevisionontbreekt, ofpatchbevat een ander veld dantitle/start/end. - 404
calendar_not_foundDe koppeling of agenda met dit ID bestaat niet, of hoort niet bij uw account. - 400
calendar_unsupportedDe koppeling is geen agenda (maar facturatie) - dit endpoint is er niet op van toepassing. - 400
calendar_invalid_patchpatchheeft een ongeldige vorm -start/endmoeten een tijdzone bevatten (of een hele dagYYYY-MM-DDmet een later einde zijn).
Facturen uit Fakturoid en ABRA Flexi
Koppel het account eerst bij Koppelingen. Fakturoid gebruikt OAuth in de browser; als meerdere bedrijven met facturering beschikbaar zijn, kiest u er expliciet één. ABRA Flexi vereist de HTTPS-server, de bedrijfsidentificatie en de inloggegevens van een API-gebruiker; de hostnaam van de server moet door de volai-beheerder zijn toegestaan. Inloggegevens blijven in het portaal. Deze endpoints lezen uitgegeven facturen en maken nooit facturen aan en markeren ze nooit als betaald.
Fouten van de factuurendpoints dragen het voorvoegsel integration_ en een cause en action, net als elke API-fout. Per oorzaak: account betekent dat u het account opnieuw moet koppelen bij Koppelingen, zonder dat heeft opnieuw proberen geen zin; busy (409) probeer het over een paar seconden opnieuw, uw gegevens zijn in orde; service (400, 502 of 503) ligt aan onze kant of aan die van de provider - probeer het later opnieuw en beschouw de factuurstatus ondertussen als ONBEKEND; request (400) corrigeer de zoekopdracht. Vervang een mislukte leesactie nooit door een eerdere of door de aanroeper aangeleverde betalingsclaim.
GET/v1/integrations/{id}/invoices
Een optionele zoekopdracht (maximaal 100 tekens) doorzoekt de provider. Geeft de eerste 40 resultaten van Fakturoid of de eerste 50 van ABRA Flexi terug. Verfijn de zoekopdracht om oudere facturen te vinden; dit antwoord heeft geen paginatoken.
Verzoek
curl "https://volai.cz/v1/integrations/int_CONNECTION/invoices?search=2026-0001" -H "Authorization: Bearer $VOLAI_API_KEY"Antwoord
{"invoices":[{"externalId":"123","label":"2026-0001","status":"unpaid","amountDueMinor":1210000,"currency":"CZK","dueDate":"2026-09-30","sourceRevision":"v3"}]}Foutcodes
- 400
validationsearch is langer dan 100 tekens, of de query bevat een onbekende parameter (zoals limit) - de agenda- en factuurendpoints weigeren onbekende parameters, de rest van v1 negeert ze. - 404
integration_not_foundEr bestaat geen koppeling met dit id op het account; bij het detail ook wanneer de provider geen factuur met dit invoiceId kent (bij de lijst komt een 404 van de provider aan als provider_error). - 400
integration_unsupportedDe koppeling is geen facturatiekoppeling (bijvoorbeeld een agenda) - alleen Fakturoid en ABRA Flexi leveren facturen. - 400
integration_invalid_providerDe ABRA Flexi-server die bij de koppeling is opgeslagen staat niet op de lijst met toegestane hosts van de beheerder. Neem contact op met support. - 409
integration_busyDe inloggegevens van de koppeling worden vernieuwd en het record is intussen gewijzigd - probeer het verzoek over een paar seconden opnieuw. - 409
integration_refresh_in_progressEen ander verzoek vernieuwt al het token van de koppeling - probeer het verzoek over een paar seconden opnieuw. - 409
integration_unauthorizedDe provider heeft de opgeslagen inloggegevens geweigerd (HTTP 401) - koppel het account opnieuw bij Koppelingen. - 409
integration_oauth_failedHet vernieuwen van het Fakturoid-token is mislukt - koppel het account opnieuw bij Koppelingen. - 409
integration_credentials_unavailableDe opgeslagen inloggegevens konden niet worden ontsleuteld - koppel het account opnieuw bij Koppelingen. - 409
integration_invalid_credentialsDe opgeslagen inloggegevens hebben niet de verwachte vorm (token, slug of API-gebruiker ontbreekt) - koppel het account opnieuw bij Koppelingen. - 503
integration_not_configuredDe beheerder heeft de sleutels van de provider niet ingesteld (bijvoorbeeld Fakturoid OAuth) - hiervoor wordt niets in rekening gebracht, neem contact op met support. - 502
integration_provider_errorDe provider antwoordde met een fout (ook HTTP 403) of antwoordde niet - probeer het verzoek later opnieuw; beschouw de factuurstatus ondertussen als onbekend. - 502
integration_invalid_provider_responseDe provider gaf een antwoord in een onverwachte vorm - probeer het verzoek later opnieuw; neem contact op met support als het blijft gebeuren. - 400
integration_invalid_queryDe parameter search bevat backslashes, stuurtekens of gemengde aanhalingstekens die de zoekfunctie van ABRA Flexi weigert - vereenvoudig de zoekopdracht.
GET/v1/integrations/{id}/invoices/{invoiceId}
Lees vers betalingsbewijs voordat u meldt of een factuur is betaald. amountDueMinor is een geheel getal in de kleinste eenheid van de valuta; null betekent dat de provider het openstaande saldo niet heeft vastgesteld. status kan unknown zijn. Noch unknown noch een ontbrekend bedrag bewijst een betaling. Het detail bevat provider, sourceUrl, fetchedAt en evidence: provider_response. Codeer invoiceId als URL.
Verzoek
curl "https://volai.cz/v1/integrations/int_CONNECTION/invoices/123" -H "Authorization: Bearer $VOLAI_API_KEY"Antwoord
{"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"}}Foutcodes
- 400
validationinvoiceId in het pad ontbreekt of is langer dan 500 tekens. - 404
integration_not_foundEr bestaat geen koppeling met dit id op het account; bij het detail ook wanneer de provider geen factuur met dit invoiceId kent (bij de lijst komt een 404 van de provider aan als provider_error). - 400
integration_unsupportedDe koppeling is geen facturatiekoppeling (bijvoorbeeld een agenda) - alleen Fakturoid en ABRA Flexi leveren facturen. - 400
integration_invalid_providerDe ABRA Flexi-server die bij de koppeling is opgeslagen staat niet op de lijst met toegestane hosts van de beheerder. Neem contact op met support. - 409
integration_busyDe inloggegevens van de koppeling worden vernieuwd en het record is intussen gewijzigd - probeer het verzoek over een paar seconden opnieuw. - 409
integration_refresh_in_progressEen ander verzoek vernieuwt al het token van de koppeling - probeer het verzoek over een paar seconden opnieuw. - 409
integration_unauthorizedDe provider heeft de opgeslagen inloggegevens geweigerd (HTTP 401) - koppel het account opnieuw bij Koppelingen. - 409
integration_oauth_failedHet vernieuwen van het Fakturoid-token is mislukt - koppel het account opnieuw bij Koppelingen. - 409
integration_credentials_unavailableDe opgeslagen inloggegevens konden niet worden ontsleuteld - koppel het account opnieuw bij Koppelingen. - 409
integration_invalid_credentialsDe opgeslagen inloggegevens hebben niet de verwachte vorm (token, slug of API-gebruiker ontbreekt) - koppel het account opnieuw bij Koppelingen. - 503
integration_not_configuredDe beheerder heeft de sleutels van de provider niet ingesteld (bijvoorbeeld Fakturoid OAuth) - hiervoor wordt niets in rekening gebracht, neem contact op met support. - 502
integration_provider_errorDe provider antwoordde met een fout (ook HTTP 403) of antwoordde niet - probeer het verzoek later opnieuw; beschouw de factuurstatus ondertussen als onbekend. - 502
integration_invalid_provider_responseDe provider gaf een antwoord in een onverwachte vorm - probeer het verzoek later opnieuw; neem contact op met support als het blijft gebeuren.
Setupbestellingen (Done for you)
Done for you stelt een werkende agent in op basis van een beschrijving in gewone taal, in plaats van de handmatige configuratie in het portaal. POST /v1/setup-orders voert een eenmalige gespreksbeurt uit: beschrijf de agent, en de adviseur geeft ofwel een offerte met prijs terug (quoted), vraagt u eigen regelitems boven een drempel te bevestigen (awaiting_confirmation), of weigert geautomatiseerde setup voor een toepassing waarvoor het niet geschikt is (declined). De prijs zelf wordt in code berekend uit een vaste catalogus, nooit door het model - de adviseur kiest alleen pakket, add-ons en eigen items. De offerte betalen (POST .../checkout, een Stripe Checkout-sessie die de setupkosten plus een eerste opwaardering van het tegoed dekt) start de bouw; volai bouwt de agent met de hand en verplaatst de bestelling via paid -> building -> ready. Zodra de status ready is, belt u het gekoppelde nummer om het uit te proberen, verstuurt u een bericht voor wijzigingen (revision), en daarna POST .../launch om live te gaan. GET .../{id} bevat altijd nextStep, een zin in gewoon Engels die beschrijft wat u bij de huidige status als volgende moet doen.
POST/v1/setup-orders
Eenmalig: beschrijf de agent die u nodig hebt in brief (minstens 20 tekens) en krijg een offerte terug of een verzoek om eigen items te bevestigen. locale stelt de taal in van het adviesgesprek en van alle e-mails over deze bestelling (standaard en); het wijzigt de taal van dit antwoord niet - REST spreekt altijd Engels. contactPhone wordt als notitie voor volai aan de brief toegevoegd, niet gevalideerd. Begrensd op 5 nieuwe setupbestellingen per account per dag. Mislukt de beurt van de adviseur (advisor_unavailable, advisor_no_quote), dan was de bestelling al aangemaakt en meegeteld voor die limiet, probeer het dus opnieuw door haar te vinden met GET /v1/setup-orders en de volgende poging te versturen via POST .../messages, niet door dit endpoint opnieuw aan te roepen. Geen Idempotency-Key: elke aanroep maakt een nieuwe bestelling aan, de limiet van 5 per dag is de enige rem - probeer een aanroep met een timeout eerst opnieuw met GET /v1/setup-orders.
Verzoek
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
{"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}]}}Foutcodes
- 400
validationDe body komt niet overeen met het schema (briefontbreekt of is korter dan 20 tekens,contactPhoneis te lang, oflocaleis nietcs/en). - 422
advisor_no_quoteDe adviseur besloot een vraag te moeten stellen, maar een eenmalige beurt vereist een offerte of een afwijzing - probeer het opnieuw, of formuleer de brief met meer detail om. - 503
advisor_unavailableDe adviseur is tijdelijk niet beschikbaar (geen API-sleutel geconfigureerd, een netwerkfout, of het model weigerde) - probeer het zo meteen opnieuw. - 503
setup_orders_disabledDone for you is tijdelijk voor het hele systeem uitgeschakeld; bestaande betaalde bestellingen gaan normaal verder.
GET/v1/setup-orders
De setupbestellingen van het account, zonder paginering.
Verzoek
curl "https://volai.cz/v1/setup-orders" -H "Authorization: Bearer $VOLAI_API_KEY"Antwoord
{"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 van de setupbestelling, inclusief elk bericht in het gesprek (ook antwoorden van de eigenaar van het volai-team) en nextStep.
Verzoek
curl "https://volai.cz/v1/setup-orders/so_AbCdEf123456" -H "Authorization: Bearer $VOLAI_API_KEY"Antwoord
{"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}]}}Foutcodes
- 404
setup_order_not_foundEr bestaat geen setupbestelling met ditidop het account.
POST/v1/setup-orders/{id}/messages
Verstuurt een bericht aan de setupadviseur. Zolang de bestelling quoted is, kan een nieuw bericht een nieuwe beurt van de adviseur starten die de offerte vervangt - tenzij er momenteel een checkout voor open staat (checkout_open), wat de offerte bevriest totdat die Checkout-sessie verloopt of wordt gebruikt. Bij andere statussen (ready, paid, building, revision) wordt het bericht gewoon voor het volai-team vastgelegd; het versturen ervan terwijl de status ready is, zet de bestelling op revision. Rondes die in het pakket zijn inbegrepen (order.quote.revisionsIncluded, al gebruikt order.revisionsUsed) zijn gratis; zijn ze opgebruikt, dan wordt de volgende ronde die de oprichter afrondt 490 CZK exclusief btw van het tegoed afgeschreven als een boeking setup_revision.
Verzoek
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?"}'Antwoord
{"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}]}}Foutcodes
- 400
validationmessageontbreekt, is leeg, of is langer dan 2000 tekens. - 404
setup_order_not_foundEr bestaat geen setupbestelling met ditidop het account. - 409
order_closedDe bestelling islaunchedofcancelled- ze accepteert geen berichten meer. - 409
turn_in_progressEen ander verzoek voor dezelfde bestelling is al onderweg - wacht tot het klaar is en probeer het opnieuw. - 409
checkout_openEr staat momenteel een Checkout-sessie open voor de offerte van deze bestelling - wacht tot die verloopt of wordt voltooid voordat u een bericht verstuurt dat de prijs kan wijzigen. - 422
advisor_no_quoteDe adviseur besloot een vraag te moeten stellen, maar een eenmalige beurt vereist een offerte of een afwijzing - probeer het opnieuw, of formuleer de brief met meer detail om. - 503
advisor_unavailableDe adviseur is tijdelijk niet beschikbaar (geen API-sleutel geconfigureerd, een netwerkfout, of het model weigerde) - probeer het zo meteen opnieuw.
POST/v1/setup-orders/{id}/checkout
Maakt een Stripe Checkout-sessie aan voor de geoffreerde setupkosten plus een eerste opwaardering van het tegoed (creditCzk, een van de ondersteunde opwaardeerbedragen van het account). Alleen vanuit de status quoted, en alleen door de eigenaar van de bestelling. De succes- en annuleerredirect wijzen beide terug naar de pagina van de bestelling in de taal van het gesprek (order.locale), niet noodzakelijk die van dit verzoek.
Verzoek
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}'Antwoord
{"url":"https://checkout.stripe.com/c/pay/cs_test_...","sessionId":"cs_test_a1B2c3D4"}Foutcodes
- 400
validationDe body komt niet overeen met het schema (creditCzkis geen geheel getal). - 400
invalid_amountcreditCzkis niet een van de ondersteunde opwaardeerbedragen van het account. - 400
billing_address_requiredHet account heeft nog geen compleet factuuradres opgeslagen - voeg er eerst een toe (PUT /v1/account/billing). - 404
setup_order_not_foundEr bestaat geen setupbestelling met ditidop het account. - 409
checkout_openEr staat momenteel een Checkout-sessie open voor de offerte van deze bestelling - wacht tot die verloopt of wordt voltooid voordat u een bericht verstuurt dat de prijs kan wijzigen. - 409
invalid_transitionDe bestelling is nietquoted, of haar offerte ontbreekt - de checkout opent alleen vanuit een geoffreerde bestelling. - 409
quote_pending_confirmationDe offerte moet nog door volai worden bevestigd voor eigen regelitems (awaiting_confirmation) voordat de checkout kan openen. - 409
turn_in_progressEen ander verzoek voor dezelfde bestelling is al onderweg - wacht tot het klaar is en probeer het opnieuw. - 503
setup_orders_disabledDone for you is tijdelijk voor het hele systeem uitgeschakeld; bestaande betaalde bestellingen gaan normaal verder.
POST/v1/setup-orders/{id}/launch
Lanceert de voltooide agent (ready -> launched) - de enige overgang die de klant zelf uitvoert; elke andere overgang (start van de bouw, ready, annulering) is aan het volai-team.
Verzoek
curl -X POST https://volai.cz/v1/setup-orders/so_AbCdEf123456/launch -H "Authorization: Bearer $VOLAI_API_KEY"Antwoord
{"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":[]}}Foutcodes
- 404
setup_order_not_foundEr bestaat geen setupbestelling met ditidop het account. - 409
invalid_transitionDe bestelling is nietready- er is nog niets klaar om te lanceren.
POST/v1/setup-orders/{id}/cancel
Annuleert een onbetaalde setupbestelling (draft, quoted, awaiting_confirmation of declined -> cancelled). Een betaalde bestelling kan niet via dit endpoint worden geannuleerd - neem contact op met volai.
Verzoek
curl -X POST https://volai.cz/v1/setup-orders/so_AbCdEf123456/cancel -H "Authorization: Bearer $VOLAI_API_KEY"Antwoord
{"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":[]}}Foutcodes
- 404
setup_order_not_foundEr bestaat geen setupbestelling met ditidop het account. - 409
invalid_transitionDe bestelling kan vanuit haar huidige status niet worden geannuleerd (ze is al betaald, of al gesloten).
Changelog
Een machineleesbare changelog voor de openbare interface - zowel REST v1 als MCP. Eén document op één URL (geen queryparameters - filter de teruggegeven array entries zelf), dus het is veilig om het openbaar te cachen. Dezelfde gegevens als /en/changelog in het portaal en de MCP-tool get_changelog. De canonieke bron van waarheid is één bestand (content/changelog.json) - elke weergave wordt ervan gegenereerd.
GET/v1/changelog
Geeft de huidige interfaceversie en ALLE changelogitems (nieuwste eerst) terug, in het Engels ongeacht de taal van het account. version/technical zijn null wanneer een item er geen had (oude items zonder versie, of zonder technisch detail). url is het canonieke Engelse pad met een anker naar id.
Verzoek
curl https://volai.cz/v1/changelogAntwoord
{
"version": "1.58.0",
"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"
}
]
}Foutcodes
- 429
rate_limitedU hebt de limiet van 30 verzoeken per minuut vanaf één IP-adres bereikt (zonder API-sleutel geldt de limiet per IP, niet per account).
De header x-volai-version
Absoluut ELK v1-antwoord (succes of fout, inclusief 401/429, binaire routes zoals een gespreksopname, en GET /openapi.json) bevat een header x-volai-version met de huidige interfaceversie. Wijkt die af van de laatste versie die u zag, roep dan GET /v1/changelog (of MCP get_changelog) aan om te zien wat er nieuw is - de MCP-client krijgt dezelfde aanbeveling rechtstreeks in de SERVER_INSTRUCTIONS van de server.
x-volai-version: 1.58.0De MCP-tegenhanger is de tool get_changelog (READ_ONLY) - dezelfde gegevens, plus het kan op de server zelf filteren op since (een datum of versie - alleen nieuwere items), area, en het aantal begrenzen met limit.
Gerelateerd
MCP-server
Koppel Claude Code, Codex CLI, Codex desktop, Cursor en andere AI-editors.
Spraakagent
Systeemprompt, tools, overdracht aan een mens, opnames en gestructureerde gespreksgegevens.
Webhooks
Gebeurtenissen, gestructureerde gegevens in de body, verificatie van handtekeningen, nieuwe pogingen bij mislukking.
SIP
Uw eigen softphone, SIP-gegevens, uitgaande gesprekken via een SIP-client.
Eigen agent meebrengen
Koppel een platform van een derde partij (ElevenLabs, Asterisk...), zonder toeslag voor de agent.