Guides
Invoices
The agent can read an issued invoice from a connected Fakturoid or ABRA Flexi account and use it as the basis for a reminder call - it never issues an invoice or marks one paid.
What it can and cannot do
- Search issued invoices by number or company name (
list_invoices). - Read one invoice's freshest status straight from the provider (
get_invoice). - Create a reminder Task (
taskType: "invoice") that references the invoice.
Cannot
- It does not issue, edit or delete any invoice.
- It does not mark an invoice paid - only Fakturoid or ABRA themselves can.
- It does not connect Fakturoid with credentials - only the browser OAuth flow.
Connecting an account
Both connect in the same place as calendars - the portal's Connections section (/en/connections), or through list_integrations/GET /v1/integrations. The full agent flow (list_integrations -> connectUrl -> a browser step) is on the Connections.
Fakturoid
Connects only through browser OAuth - POST /v1/integrations with provider: "fakturoid" is rejected by REST with integration_unsupported; credentials cannot be sent this way at all. When the account has more than one invoicing-enabled company, the browser is redirected back to volai's own Connections page (/en/connections, or /propojeni in Czech) after sign-in, and the company picker appears there - the integrations entry is only created after that choice. ready for fakturoid in GET /v1/integrations only reports that volai has Fakturoid OAuth credentials configured (FAKTUROID_CLIENT_ID and friends) - NOT that a particular account is actually connected. Whether the connection is done shows up as an entry in the integrations array.
ABRA Flexi
Connects with credentials, the same way as Apple Calendar: POST /v1/integrations with {provider: "abra-flexi", baseUrl, company, username, password}. The server address (baseUrl) must match a host the operator allowlisted in advance (ABRA_FLEXI_ALLOWED_HOSTS) - anything else is rejected.
Pass a password only when the user already has it stored somewhere safe (an env var, a file they named) - never ask for one in conversation, and never echo it back.
Reading invoices
A two-step flow: search first, then a fresh read of the one invoice you care about.
list_invoices({integrationId, search?})-search(up to 100 characters) is a provider-side search by invoice number or company name; it returns the first 40 Fakturoid or 50 ABRA rows, no pagination. Narrowsearchwhen there are too many results.get_invoice({integrationId, invoiceId})-invoiceIdis theexternalIdcopied fromlist_invoices. Returns the freshest status straight from the provider, not whatever volai remembers.
REST equivalents: GET /v1/integrations/{id}/invoices?search=... and GET /v1/integrations/{id}/invoices/{invoiceId}.
status is paid, unpaid, overdue or unknown; amountDueMinor is an integer in the INVOICE'S OWN CURRENCY minor units (not necessarily CZK), and null means the balance is unverified. The detail also carries provider, fetchedAt and evidence: "provider_response".
status: "unknown" and a missing amount are NEVER evidence of payment - and a caller's own claim is not evidence either. The agent only treats a fresh get_invoice response as evidence.
Reminding through Tasks
Bulk reminder calling about unpaid invoices goes through Tasks.
A task with taskType: "invoice" is created with source: "integration", and each recipient (or the whole task, when every recipient shares the same invoice) carries externalRefs.invoice = {connectionId, externalId, provider?} - connectionId is the connection id from list_integrations, externalId the invoice id from list_invoices. Without that reference, create_task/POST /v1/tasks fails with a validation error whose message names recipients.externalRefs, and start_task refuses to run with the same error; issues with the details only shows up later, from get_task/GET /v1/tasks/{id} on a task that got created anyway.
Each recipient also carries a paymentStatus field (not_applicable, unknown, unpaid, paid, disputed) - system-managed and read-only, derived from the freshest invoice read at the provider right before dialing. It is not_applicable with no invoice reference, unknown right after creation or when the status or amount cannot be verified, and otherwise follows the Fakturoid/ABRA response - a paid invoice makes the task skip that item (skipped) and set paid. The system itself never sets disputed today - only the portal can display it - it is part of the public enum (TaskItem in /openapi.json) for a future manual dispute flag.
Errors
integration_not_found- No connection with thisintegrationIdexists, or it does not belong to this account.integration_unsupported- The connection is not an invoicing one (a calendar, say), or this is an attempt to connect Fakturoid with credentials instead of OAuth.integration_unauthorized- Fakturoid or ABRA revoked access - reconnect in the portal.integration_provider_error- The provider is not responding right now or returned an error - retry later and treat the invoice's state as unknown meanwhile.integration_invalid_query- ABRA rejectedsearchfor backslashes or mixed quotation marks - simplify the query.
The full table with every code is in the invoices section of the REST API reference and in MCP errors.
Security
REST API and MCP never return access tokens or passwords for Fakturoid or ABRA Flexi, and we store them encrypted. Disconnecting an integration is only in the portal or over REST (DELETE /v1/integrations/{id}) - MCP has no tool for it on purpose, since it is irreversible.
Related
MCP server
Connect Claude Code, Cursor and other AI editors.
REST API
Complete reference for every endpoint: numbers, calls, SMS, agents and their drafts, tools, recordings, webhooks, do-not-call list, relay, Google Calendar.
Connections
Google Calendar: connect an account, read and confirm event updates.
Tasks
Outbound campaigns: creation, CSV recipient import, start/pause/reconcile, budget and calling window.