Le descrizioni degli endpoint e dei campi restano in inglese, esattamente come le pubblica l'API. È una scelta voluta: qui legge ciò che vedrà anche nelle risposte.
GET /v1/ops-agents#
List operations agents.
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl "https://api.voiceland.ai/v1/ops-agents" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"items": [
{
"created_at": "2026-09-19T08:00:00Z",
"definition": {
"edges": [
{
"from": "trigger",
"to": "download"
},
{
"from": "download",
"to": "transcribe"
},
{
"from": "transcribe",
"to": "report"
},
{
"from": "report",
"to": "mail"
},
{
"from": "report",
"to": "hook"
}
],
"nodes": [
{
"id": "trigger",
"label": "Call ended",
"position": {
"x": 80,
"y": 160
},
"type": "trigger.pbx_call"
},
{
"config": {
"url": "{{trigger.recording_url}}"
},
"id": "download",
"label": "Download recording",
"position": {
"x": 380,
"y": 160
},
"type": "recording.download"
},
{
"config": {
"file": "{{download.file}}",
"translate_to": [
"en"
]
},
"id": "transcribe",
"label": "Transcribe",
"position": {
"x": 680,
"y": 160
},
"type": "transcribe"
},
{
"config": {
"questions": [
{
"text": "Did the customer place an order?"
},
{
"text": "Was a complaint raised?"
}
],
"transcript": "{{transcribe.text}}"
},
"id": "report",
"label": "Report",
"position": {
"x": 980,
"y": 160
},
"type": "report.questions"
},
{
"config": {
"to": [
"ops@example.com"
]
},
"id": "mail",
"label": "Email the report",
"position": {
"x": 1280,
"y": 100
},
"type": "email.report"
},
{
"config": {
"secret": "…",
"url": "https://example.com/hooks/calls"
},
"id": "hook",
"label": "Notify",
"position": {
"x": 1280,
"y": 240
},
"type": "webhook.post"
}
]
},
"description": "Transcribe every call and mail the report",
"enabled": true,
"hook_url": "https://api.voiceland.ai/v1/ops/hooks/k3JqTz9wY1n8Qb2vXm5LpR0e",
"kind": "operations",
"last_run_at": "2026-09-19T09:45:31Z",
"last_status": "ok",
"name": "after-call-report",
"runs": 12,
"updated_at": "2026-09-19T08:30:00Z"
}
]
}
POST /v1/ops-agents#
Create an operations agent. The name is shared with voice and chat agents and must be free. Without a definition the board starts with one manual trigger. definition is {nodes, edges}: a node has id, type (a module), label, config and position; an edge from, to and, leaving a condition, when: "true"|"false". Validated: known modules, required settings, at least one trigger, no cycle, a schedule trigger's cron and timezone (at most every 5 minutes). A board with a trigger.schedule node answers with schedule (cron, timezone, policies, runs_per_month) and next_run_at; it fires only while the agent is enabled.
Corpo della richiesta
application/json Schema: OpsAgentRequest
| Campo |
Tipo |
Obbligatorio |
Descrizione |
definition |
string (byte) |
Sì |
|
description |
string |
Sì |
|
enabled |
boolean |
|
|
name |
string |
Sì |
|
note |
string |
|
|
source |
string |
|
|
Risposte
| Codice |
Descrizione |
201 |
Created. |
400 |
invalid_definition (the message names the node and the rule), invalid_agent_name. |
401 |
Missing or invalid API key. |
409 |
name_taken, already_exists. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl -X POST "https://api.voiceland.ai/v1/ops-agents" \
-H "Authorization: Bearer VL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"definition": {
"edges": [
{
"from": "trigger",
"to": "download"
},
{
"from": "download",
"to": "transcribe"
},
{
"from": "transcribe",
"to": "report"
},
{
"from": "report",
"to": "mail"
},
{
"from": "report",
"to": "hook"
}
],
"nodes": [
{
"id": "trigger",
"label": "Call ended",
"position": {
"x": 80,
"y": 160
},
"type": "trigger.pbx_call"
},
{
"config": {
"url": "{{trigger.recording_url}}"
},
"id": "download",
"label": "Download recording",
"position": {
"x": 380,
"y": 160
},
"type": "recording.download"
},
{
"config": {
"file": "{{download.file}}",
"translate_to": [
"en"
]
},
"id": "transcribe",
"label": "Transcribe",
"position": {
"x": 680,
"y": 160
},
"type": "transcribe"
},
{
"config": {
"questions": [
{
"text": "Did the customer place an order?"
},
{
"text": "Was a complaint raised?"
}
],
"transcript": "{{transcribe.text}}"
},
"id": "report",
"label": "Report",
"position": {
"x": 980,
"y": 160
},
"type": "report.questions"
},
{
"config": {
"to": [
"ops@example.com"
]
},
"id": "mail",
"label": "Email the report",
"position": {
"x": 1280,
"y": 100
},
"type": "email.report"
},
{
"config": {
"secret": "…",
"url": "https://example.com/hooks/calls"
},
"id": "hook",
"label": "Notify",
"position": {
"x": 1280,
"y": 240
},
"type": "webhook.post"
}
]
},
"description": "Transcribe every call and mail the report",
"enabled": true,
"name": "after-call-report"
}'
Esempio di risposta
{
"created_at": "2026-09-19T08:00:00Z",
"definition": {
"edges": [
{
"from": "trigger",
"to": "download"
},
{
"from": "download",
"to": "transcribe"
},
{
"from": "transcribe",
"to": "report"
},
{
"from": "report",
"to": "mail"
},
{
"from": "report",
"to": "hook"
}
],
"nodes": [
{
"id": "trigger",
"label": "Call ended",
"position": {
"x": 80,
"y": 160
},
"type": "trigger.pbx_call"
},
{
"config": {
"url": "{{trigger.recording_url}}"
},
"id": "download",
"label": "Download recording",
"position": {
"x": 380,
"y": 160
},
"type": "recording.download"
},
{
"config": {
"file": "{{download.file}}",
"translate_to": [
"en"
]
},
"id": "transcribe",
"label": "Transcribe",
"position": {
"x": 680,
"y": 160
},
"type": "transcribe"
},
{
"config": {
"questions": [
{
"text": "Did the customer place an order?"
},
{
"text": "Was a complaint raised?"
}
],
"transcript": "{{transcribe.text}}"
},
"id": "report",
"label": "Report",
"position": {
"x": 980,
"y": 160
},
"type": "report.questions"
},
{
"config": {
"to": [
"ops@example.com"
]
},
"id": "mail",
"label": "Email the report",
"position": {
"x": 1280,
"y": 100
},
"type": "email.report"
},
{
"config": {
"secret": "…",
"url": "https://example.com/hooks/calls"
},
"id": "hook",
"label": "Notify",
"position": {
"x": 1280,
"y": 240
},
"type": "webhook.post"
}
]
},
"description": "Transcribe every call and mail the report",
"enabled": true,
"hook_url": "https://api.voiceland.ai/v1/ops/hooks/k3JqTz9wY1n8Qb2vXm5LpR0e",
"kind": "operations",
"last_run_at": "2026-09-19T09:45:31Z",
"last_status": "ok",
"name": "after-call-report",
"runs": 12,
"updated_at": "2026-09-19T08:30:00Z"
}
POST /v1/ops-agents/design#
Design a board with AI. Say what the workflow should do. The designer is given the module catalog, the connection schema, the rules and the template library, and answers with a board that has been parsed, validated and linted; a first draft with findings goes back to the model once with those findings. ok: true means the board is clean and ready to put on a canvas (it is NOT saved; save it with source: ai). ok: false carries the issues that remained. Pass current to ask for a change to an existing board.
Corpo della richiesta
application/json
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
502 |
designer_unavailable: the model could not be reached. |
5XX |
Server or upstream error. |
Esempio di richiesta
curl -X POST "https://api.voiceland.ai/v1/ops-agents/design" \
-H "Authorization: Bearer VL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"language": "en",
"request": "When a call ends, transcribe it and email a summary to sales@example.com"
}'
Esempio di risposta
{
"assumptions": [
"sales@example.com is the recipient"
],
"attempts": 1,
"definition": {
"edges": [
{
"from": "trigger",
"to": "download"
},
{
"from": "download",
"to": "transcribe"
},
{
"from": "transcribe",
"to": "report"
},
{
"from": "report",
"to": "mail"
},
{
"from": "report",
"to": "hook"
}
],
"nodes": [
{
"id": "trigger",
"label": "Call ended",
"position": {
"x": 80,
"y": 160
},
"type": "trigger.pbx_call"
},
{
"config": {
"url": "{{trigger.recording_url}}"
},
"id": "download",
"label": "Download recording",
"position": {
"x": 380,
"y": 160
},
"type": "recording.download"
},
{
"config": {
"file": "{{download.file}}",
"translate_to": [
"en"
]
},
"id": "transcribe",
"label": "Transcribe",
"position": {
"x": 680,
"y": 160
},
"type": "transcribe"
},
{
"config": {
"questions": [
{
"text": "Did the customer place an order?"
},
{
"text": "Was a complaint raised?"
}
],
"transcript": "{{transcribe.text}}"
},
"id": "report",
"label": "Report",
"position": {
"x": 980,
"y": 160
},
"type": "report.questions"
},
{
"config": {
"to": [
"ops@example.com"
]
},
"id": "mail",
"label": "Email the report",
"position": {
"x": 1280,
"y": 100
},
"type": "email.report"
},
{
"config": {
"secret": "…",
"url": "https://example.com/hooks/calls"
},
"id": "hook",
"label": "Notify",
"position": {
"x": 1280,
"y": 240
},
"type": "webhook.post"
}
]
},
"explanation": "Downloads the recording, transcribes it, summarises it and emails the summary.",
"issues": [],
"ok": true
}
GET /v1/ops-agents/runs#
Run history across agents. Every agent's runs of the project, newest first, with the same window, totals and truncated flag as the per-agent list.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
from |
query |
string |
|
Window start by run start time: RFC3339 or YYYY-MM-DD (00:00Z). Open when omitted. |
to |
query |
string |
|
Window end: RFC3339 or YYYY-MM-DD (that day's last millisecond). Open when omitted. |
limit |
query |
integer |
|
Max rows (default 50, max 500). |
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl "https://api.voiceland.ai/v1/ops-agents/runs" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"from": "2026-09-01T00:00:00Z",
"items": [
{
"active_ms": 39100,
"agent": "after-call-report",
"billed_seconds": 60,
"cost_cents": 5,
"data": {
"download": {
"bytes": 812345,
"filename": "download-recording.wav"
},
"summarize": {
"text": "The caller ordered two souvlaki for pickup."
},
"transcribe": {
"language": "el",
"text": "Καλημέρα, θα ήθελα δύο σουβλάκια…"
},
"trigger": {
"caller_number": "+306972605774",
"direction": "inbound"
}
},
"duration_ms": 41200,
"finished_at": "2026-09-19T09:46:12Z",
"has_xlsx": true,
"id": "opr-c3e5e22e41b010c0",
"report": {
"answers": [
{
"answer": "Yes, two souvlaki for pickup.",
"matched": true,
"question": "Did the customer place an order?"
}
],
"title": "Call report +306972605774"
},
"started_at": "2026-09-19T09:45:31Z",
"status": "ok",
"steps": [
{
"attempt": 1,
"duration_ms": 1830,
"finished_at": "2026-09-19T09:45:33Z",
"input_bytes": 96,
"input_preview": "{\"max_mb\":200,\"url\":\"https://example.com/api/v1.0/crm/recording?secret=…\"}",
"label": "Download recording",
"max_attempts": 24,
"node_id": "download",
"output_bytes": 118,
"output_preview": "{\"bytes\":812345,\"content_type\":\"audio/wav\",\"file\":\"(file download-recording.wav)\",\"filename\":\"download-recording.wav\"}",
"retry_interval_min": 60,
"started_at": "2026-09-19T09:45:31Z",
"status": "ok",
"type": "recording.download"
},
{
"attempt": 1,
"attempts": [
{
"at": "2026-09-19T09:46:11Z",
"attempt": 1,
"duration_ms": 412,
"error": "webhook: the endpoint answered 503: upstream maintenance",
"remote_status": 503
}
],
"error": "webhook: the endpoint answered 503: upstream maintenance",
"label": "Notify",
"max_attempts": 24,
"next_attempt_at": "2026-09-19T10:46:12Z",
"node_id": "hook",
"remote_body": "upstream maintenance",
"remote_status": 503,
"retry_interval_min": 60,
"status": "retrying",
"type": "webhook.post"
}
],
"trigger_summary": "inbound call +306972605774 → 401",
"trigger_type": "trigger.pbx_call",
"updated_at": "2026-09-19T09:46:12Z"
}
],
"to": "2026-09-30T23:59:59Z",
"totals": {
"billed_seconds": 60,
"charged_cents": 0,
"cost_cents": 5,
"failed": 0,
"ok": 1,
"pool_seconds": 15,
"runs": 1
},
"truncated": false
}
GET /v1/ops-agents/runs/export.xlsx#
Export runs across agents as a workbook. The runs of the window (up to 5000, newest first) as an .xlsx: a Runs sheet (started, agent, run, status, trigger, steps, active/automation/plan seconds, list value and charged EUR, source, error) and a Totals sheet. Content-Disposition: attachment; filename="ops-runs-all-<from>_<to>.xlsx".
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
from |
query |
string |
|
Window start by run start time: RFC3339 or YYYY-MM-DD (00:00Z). Open when omitted. |
to |
query |
string |
|
Window end: RFC3339 or YYYY-MM-DD (that day's last millisecond). Open when omitted. |
Risposte
| Codice |
Descrizione |
200 |
An .xlsx workbook. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl "https://api.voiceland.ai/v1/ops-agents/runs/export.xlsx" \
-H "Authorization: Bearer VL_API_KEY"
GET /v1/ops-agents/stats#
Run statistics. The window (default: the start of the current UTC month → now) folded per UTC day, zero-filled for every day of [from, to), and per agent (sorted by runs, with last_run_at), plus totals. Up to 5000 runs are counted; truncated says the window held more. Seconds and cents are the runs' own settled values.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
from |
query |
string |
|
Window start by run start time: RFC3339 or YYYY-MM-DD (00:00Z). Open when omitted. |
to |
query |
string |
|
Window end: RFC3339 or YYYY-MM-DD (that day's last millisecond). Open when omitted. |
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl "https://api.voiceland.ai/v1/ops-agents/stats" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"agents": [
{
"agent": "after-call-report",
"billed_seconds": 540,
"charged_cents": 0,
"cost_cents": 45,
"failed": 1,
"last_run_at": "2026-09-22T16:02:11Z",
"ok": 8,
"pool_seconds": 135,
"runs": 9
}
],
"days": [
{
"billed_seconds": 0,
"charged_cents": 0,
"cost_cents": 0,
"date": "2026-09-01",
"failed": 0,
"ok": 0,
"pool_seconds": 0,
"runs": 0
},
{
"billed_seconds": 540,
"charged_cents": 0,
"cost_cents": 45,
"date": "2026-09-22",
"failed": 1,
"ok": 8,
"pool_seconds": 135,
"runs": 9
}
],
"from": "2026-09-01T00:00:00Z",
"to": "2026-09-23T10:00:00Z",
"total_runs": 14,
"totals": {
"billed_seconds": 900,
"charged_cents": 0,
"cost_cents": 75,
"failed": 2,
"ok": 12,
"pool_seconds": 225,
"runs": 14
},
"truncated": false
}
GET /v1/ops-agents/usage#
Monthly usage. Runs, billed seconds and cents for a month (default: the current one), plus the rate.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
month |
query |
string |
|
YYYY-MM. |
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl "https://api.voiceland.ai/v1/ops-agents/usage" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"billed_seconds": 900,
"charged_cents": 0,
"cost_cents": 75,
"month": "2026-09",
"pool_seconds": 225,
"questions": 8,
"rate": {
"cents_per_minute": 5,
"currency": "EUR",
"in_regime": true,
"min_seconds": 30,
"overage_cents_per_min": 20,
"package_id": "growth",
"percent": 25,
"questions_included": 5,
"seconds_per_extra_question": 60
},
"runs": 12
}
DELETE /v1/ops-agents/{name}#
Delete an operations agent. Runs are kept for their 90-day retention.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
Risposte
| Codice |
Descrizione |
204 |
Deleted. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl -X DELETE "https://api.voiceland.ai/v1/ops-agents/{name}" \
-H "Authorization: Bearer VL_API_KEY"
GET /v1/ops-agents/{name}#
Get an operations agent.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl "https://api.voiceland.ai/v1/ops-agents/{name}" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"created_at": "2026-09-19T08:00:00Z",
"definition": {
"edges": [
{
"from": "trigger",
"to": "download"
},
{
"from": "download",
"to": "transcribe"
},
{
"from": "transcribe",
"to": "report"
},
{
"from": "report",
"to": "mail"
},
{
"from": "report",
"to": "hook"
}
],
"nodes": [
{
"id": "trigger",
"label": "Call ended",
"position": {
"x": 80,
"y": 160
},
"type": "trigger.pbx_call"
},
{
"config": {
"url": "{{trigger.recording_url}}"
},
"id": "download",
"label": "Download recording",
"position": {
"x": 380,
"y": 160
},
"type": "recording.download"
},
{
"config": {
"file": "{{download.file}}",
"translate_to": [
"en"
]
},
"id": "transcribe",
"label": "Transcribe",
"position": {
"x": 680,
"y": 160
},
"type": "transcribe"
},
{
"config": {
"questions": [
{
"text": "Did the customer place an order?"
},
{
"text": "Was a complaint raised?"
}
],
"transcript": "{{transcribe.text}}"
},
"id": "report",
"label": "Report",
"position": {
"x": 980,
"y": 160
},
"type": "report.questions"
},
{
"config": {
"to": [
"ops@example.com"
]
},
"id": "mail",
"label": "Email the report",
"position": {
"x": 1280,
"y": 100
},
"type": "email.report"
},
{
"config": {
"secret": "…",
"url": "https://example.com/hooks/calls"
},
"id": "hook",
"label": "Notify",
"position": {
"x": 1280,
"y": 240
},
"type": "webhook.post"
}
]
},
"description": "Transcribe every call and mail the report",
"enabled": true,
"hook_url": "https://api.voiceland.ai/v1/ops/hooks/k3JqTz9wY1n8Qb2vXm5LpR0e",
"kind": "operations",
"last_run_at": "2026-09-19T09:45:31Z",
"last_status": "ok",
"name": "after-call-report",
"runs": 12,
"updated_at": "2026-09-19T08:30:00Z"
}
PUT /v1/ops-agents/{name}#
Save the board, description or enabled state. Fields absent from the body are kept. Disabling stops triggers from starting runs.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
Corpo della richiesta
application/json Schema: OpsAgentRequest
| Campo |
Tipo |
Obbligatorio |
Descrizione |
definition |
string (byte) |
Sì |
|
description |
string |
Sì |
|
enabled |
boolean |
|
|
name |
string |
Sì |
|
note |
string |
|
|
source |
string |
|
|
Risposte
| Codice |
Descrizione |
200 |
Success. |
400 |
invalid_definition. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl -X PUT "https://api.voiceland.ai/v1/ops-agents/{name}" \
-H "Authorization: Bearer VL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"definition": {
"edges": [
{
"from": "trigger",
"to": "download"
},
{
"from": "download",
"to": "transcribe"
},
{
"from": "transcribe",
"to": "report"
},
{
"from": "report",
"to": "mail"
},
{
"from": "report",
"to": "hook"
}
],
"nodes": [
{
"id": "trigger",
"label": "Call ended",
"position": {
"x": 80,
"y": 160
},
"type": "trigger.pbx_call"
},
{
"config": {
"url": "{{trigger.recording_url}}"
},
"id": "download",
"label": "Download recording",
"position": {
"x": 380,
"y": 160
},
"type": "recording.download"
},
{
"config": {
"file": "{{download.file}}",
"translate_to": [
"en"
]
},
"id": "transcribe",
"label": "Transcribe",
"position": {
"x": 680,
"y": 160
},
"type": "transcribe"
},
{
"config": {
"questions": [
{
"text": "Did the customer place an order?"
},
{
"text": "Was a complaint raised?"
}
],
"transcript": "{{transcribe.text}}"
},
"id": "report",
"label": "Report",
"position": {
"x": 980,
"y": 160
},
"type": "report.questions"
},
{
"config": {
"to": [
"ops@example.com"
]
},
"id": "mail",
"label": "Email the report",
"position": {
"x": 1280,
"y": 100
},
"type": "email.report"
},
{
"config": {
"secret": "…",
"url": "https://example.com/hooks/calls"
},
"id": "hook",
"label": "Notify",
"position": {
"x": 1280,
"y": 240
},
"type": "webhook.post"
}
]
},
"description": "Transcribe every call and mail the report",
"enabled": true,
"name": "after-call-report"
}'
Esempio di risposta
{
"created_at": "2026-09-19T08:00:00Z",
"definition": {
"edges": [
{
"from": "trigger",
"to": "download"
},
{
"from": "download",
"to": "transcribe"
},
{
"from": "transcribe",
"to": "report"
},
{
"from": "report",
"to": "mail"
},
{
"from": "report",
"to": "hook"
}
],
"nodes": [
{
"id": "trigger",
"label": "Call ended",
"position": {
"x": 80,
"y": 160
},
"type": "trigger.pbx_call"
},
{
"config": {
"url": "{{trigger.recording_url}}"
},
"id": "download",
"label": "Download recording",
"position": {
"x": 380,
"y": 160
},
"type": "recording.download"
},
{
"config": {
"file": "{{download.file}}",
"translate_to": [
"en"
]
},
"id": "transcribe",
"label": "Transcribe",
"position": {
"x": 680,
"y": 160
},
"type": "transcribe"
},
{
"config": {
"questions": [
{
"text": "Did the customer place an order?"
},
{
"text": "Was a complaint raised?"
}
],
"transcript": "{{transcribe.text}}"
},
"id": "report",
"label": "Report",
"position": {
"x": 980,
"y": 160
},
"type": "report.questions"
},
{
"config": {
"to": [
"ops@example.com"
]
},
"id": "mail",
"label": "Email the report",
"position": {
"x": 1280,
"y": 100
},
"type": "email.report"
},
{
"config": {
"secret": "…",
"url": "https://example.com/hooks/calls"
},
"id": "hook",
"label": "Notify",
"position": {
"x": 1280,
"y": 240
},
"type": "webhook.post"
}
]
},
"description": "Transcribe every call and mail the report",
"enabled": true,
"hook_url": "https://api.voiceland.ai/v1/ops/hooks/k3JqTz9wY1n8Qb2vXm5LpR0e",
"kind": "operations",
"last_run_at": "2026-09-19T09:45:31Z",
"last_status": "ok",
"name": "after-call-report",
"runs": 12,
"updated_at": "2026-09-19T08:30:00Z"
}
POST /v1/ops-agents/{name}/apply-template#
Replace the board with a template. Templates are ready-made boards, grouped by integration, every one validated and run end to end in our tests. The previous board is kept as a revision. The answer carries the template's sample_payload, which a test run through its trigger exercises.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
Corpo della richiesta
application/json
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl -X POST "https://api.voiceland.ai/v1/ops-agents/{name}/apply-template" \
-H "Authorization: Bearer VL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"template": "yeastar-summary-email"
}'
Esempio di risposta
{
"agent": {
"created_at": "2026-09-19T08:00:00Z",
"definition": {
"edges": [
{
"from": "trigger",
"to": "download"
},
{
"from": "download",
"to": "transcribe"
},
{
"from": "transcribe",
"to": "report"
},
{
"from": "report",
"to": "mail"
},
{
"from": "report",
"to": "hook"
}
],
"nodes": [
{
"id": "trigger",
"label": "Call ended",
"position": {
"x": 80,
"y": 160
},
"type": "trigger.pbx_call"
},
{
"config": {
"url": "{{trigger.recording_url}}"
},
"id": "download",
"label": "Download recording",
"position": {
"x": 380,
"y": 160
},
"type": "recording.download"
},
{
"config": {
"file": "{{download.file}}",
"translate_to": [
"en"
]
},
"id": "transcribe",
"label": "Transcribe",
"position": {
"x": 680,
"y": 160
},
"type": "transcribe"
},
{
"config": {
"questions": [
{
"text": "Did the customer place an order?"
},
{
"text": "Was a complaint raised?"
}
],
"transcript": "{{transcribe.text}}"
},
"id": "report",
"label": "Report",
"position": {
"x": 980,
"y": 160
},
"type": "report.questions"
},
{
"config": {
"to": [
"ops@example.com"
]
},
"id": "mail",
"label": "Email the report",
"position": {
"x": 1280,
"y": 100
},
"type": "email.report"
},
{
"config": {
"secret": "…",
"url": "https://example.com/hooks/calls"
},
"id": "hook",
"label": "Notify",
"position": {
"x": 1280,
"y": 240
},
"type": "webhook.post"
}
]
},
"description": "Transcribe every call and mail the report",
"enabled": true,
"hook_url": "https://api.voiceland.ai/v1/ops/hooks/k3JqTz9wY1n8Qb2vXm5LpR0e",
"kind": "operations",
"last_run_at": "2026-09-19T09:45:31Z",
"last_status": "ok",
"name": "after-call-report",
"runs": 12,
"updated_at": "2026-09-19T08:30:00Z"
},
"revision": {
"number": 2,
"source": "template"
},
"template": {
"id": "yeastar-summary-email",
"integration": "yeastar",
"sample_payload": {
"caller_number": "+306972605774",
"direction": "inbound"
},
"trigger": "trigger.pbx_call"
}
}
GET /v1/ops-agents/{name}/export#
Export the board as JSON. An envelope (format: voiceland.ops-agent/v1) with the board, sent as a download. Import accepts exactly this.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl "https://api.voiceland.ai/v1/ops-agents/{name}/export" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"agent": "after-call-report",
"definition": {
"edges": [
{
"from": "trigger",
"to": "download"
},
{
"from": "download",
"to": "transcribe"
},
{
"from": "transcribe",
"to": "report"
},
{
"from": "report",
"to": "mail"
},
{
"from": "report",
"to": "hook"
}
],
"nodes": [
{
"id": "trigger",
"label": "Call ended",
"position": {
"x": 80,
"y": 160
},
"type": "trigger.pbx_call"
},
{
"config": {
"url": "{{trigger.recording_url}}"
},
"id": "download",
"label": "Download recording",
"position": {
"x": 380,
"y": 160
},
"type": "recording.download"
},
{
"config": {
"file": "{{download.file}}",
"translate_to": [
"en"
]
},
"id": "transcribe",
"label": "Transcribe",
"position": {
"x": 680,
"y": 160
},
"type": "transcribe"
},
{
"config": {
"questions": [
{
"text": "Did the customer place an order?"
},
{
"text": "Was a complaint raised?"
}
],
"transcript": "{{transcribe.text}}"
},
"id": "report",
"label": "Report",
"position": {
"x": 980,
"y": 160
},
"type": "report.questions"
},
{
"config": {
"to": [
"ops@example.com"
]
},
"id": "mail",
"label": "Email the report",
"position": {
"x": 1280,
"y": 100
},
"type": "email.report"
},
{
"config": {
"secret": "…",
"url": "https://example.com/hooks/calls"
},
"id": "hook",
"label": "Notify",
"position": {
"x": 1280,
"y": 240
},
"type": "webhook.post"
}
]
},
"exported_at": "2026-09-22T10:15:00Z",
"format": "voiceland.ops-agent/v1"
}
DELETE /v1/ops-agents/{name}/hook-test#
Stop listening. Closes the window without capturing; the last sample, if any, stays readable.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl -X DELETE "https://api.voiceland.ai/v1/ops-agents/{name}/hook-test" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"armed": false,
"hook_url": "https://api.voiceland.ai/v1/ops/hooks/k3JqTz9wY1n8Qb2vXm5LpR0e"
}
GET /v1/ops-agents/{name}/hook-test#
Read the listening window. Whether the window is armed, and the captured request once one has arrived. Poll it after arming.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl "https://api.voiceland.ai/v1/ops-agents/{name}/hook-test" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"armed": false,
"hook_url": "https://api.voiceland.ai/v1/ops/hooks/k3JqTz9wY1n8Qb2vXm5LpR0e",
"sample": {
"body": {
"call_id": "c-77",
"direction": "inbound"
},
"headers": {
"Content-Type": "application/json",
"X-Event": "call.ended"
},
"method": "POST",
"path": "/v1/ops/hooks/k3JqTz9wY1n8Qb2vXm5LpR0e",
"query": {
"source": "pbx"
},
"received_at": "2026-09-22T10:07:41Z"
}
}
POST /v1/ops-agents/{name}/hook-test#
Listen for a test webhook. Arms a ten-minute listening window on the agent's inbound webhook (hook_url). While it is open, the NEXT request to that address is captured and returned by GET instead of starting a run, so you can fire the real event from the other system and read exactly what it sent (method, path, query, a small set of safe headers, and the body as JSON when it parses). It works whether or not the agent is enabled, one request per press (the window closes on capture, so a second event cannot overwrite the one you are reading), the body is cut at 64 KB (truncated), and Authorization and cookies are never kept. Arming again clears the previous sample.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl -X POST "https://api.voiceland.ai/v1/ops-agents/{name}/hook-test" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"armed": true,
"armed_until": "2026-09-22T10:15:00Z",
"hook_url": "https://api.voiceland.ai/v1/ops/hooks/k3JqTz9wY1n8Qb2vXm5LpR0e"
}
POST /v1/ops-agents/{name}/import#
Import a board from JSON. Send an exported envelope. The file is validated BEFORE anything changes: the format, the JSON, every module type, every edge, the cycle check, and the connection lint (a step no trigger reaches, a reference to a step that is not upstream, a URL fed to a file input). The answer says valid, lists errors by problem, and carries current, the normalised definition and the diff between them, so you can show the two side by side. With dry_run: true nothing is replaced; without it, a valid file becomes the board and a revision (source: import) records the previous one.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
Corpo della richiesta
application/json
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl -X POST "https://api.voiceland.ai/v1/ops-agents/{name}/import" \
-H "Authorization: Bearer VL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent": "after-call-report",
"definition": {
"edges": [
{
"from": "trigger",
"to": "download"
},
{
"from": "download",
"to": "transcribe"
},
{
"from": "transcribe",
"to": "report"
},
{
"from": "report",
"to": "mail"
},
{
"from": "report",
"to": "hook"
}
],
"nodes": [
{
"id": "trigger",
"label": "Call ended",
"position": {
"x": 80,
"y": 160
},
"type": "trigger.pbx_call"
},
{
"config": {
"url": "{{trigger.recording_url}}"
},
"id": "download",
"label": "Download recording",
"position": {
"x": 380,
"y": 160
},
"type": "recording.download"
},
{
"config": {
"file": "{{download.file}}",
"translate_to": [
"en"
]
},
"id": "transcribe",
"label": "Transcribe",
"position": {
"x": 680,
"y": 160
},
"type": "transcribe"
},
{
"config": {
"questions": [
{
"text": "Did the customer place an order?"
},
{
"text": "Was a complaint raised?"
}
],
"transcript": "{{transcribe.text}}"
},
"id": "report",
"label": "Report",
"position": {
"x": 980,
"y": 160
},
"type": "report.questions"
},
{
"config": {
"to": [
"ops@example.com"
]
},
"id": "mail",
"label": "Email the report",
"position": {
"x": 1280,
"y": 100
},
"type": "email.report"
},
{
"config": {
"secret": "…",
"url": "https://example.com/hooks/calls"
},
"id": "hook",
"label": "Notify",
"position": {
"x": 1280,
"y": 240
},
"type": "webhook.post"
}
]
},
"dry_run": true,
"format": "voiceland.ops-agent/v1"
}'
Esempio di risposta
{
"definition": {
"edges": [
{
"from": "trigger",
"to": "download"
},
{
"from": "download",
"to": "transcribe"
},
{
"from": "transcribe",
"to": "report"
},
{
"from": "report",
"to": "mail"
},
{
"from": "report",
"to": "hook"
}
],
"nodes": [
{
"id": "trigger",
"label": "Call ended",
"position": {
"x": 80,
"y": 160
},
"type": "trigger.pbx_call"
},
{
"config": {
"url": "{{trigger.recording_url}}"
},
"id": "download",
"label": "Download recording",
"position": {
"x": 380,
"y": 160
},
"type": "recording.download"
},
{
"config": {
"file": "{{download.file}}",
"translate_to": [
"en"
]
},
"id": "transcribe",
"label": "Transcribe",
"position": {
"x": 680,
"y": 160
},
"type": "transcribe"
},
{
"config": {
"questions": [
{
"text": "Did the customer place an order?"
},
{
"text": "Was a complaint raised?"
}
],
"transcript": "{{transcribe.text}}"
},
"id": "report",
"label": "Report",
"position": {
"x": 980,
"y": 160
},
"type": "report.questions"
},
{
"config": {
"to": [
"ops@example.com"
]
},
"id": "mail",
"label": "Email the report",
"position": {
"x": 1280,
"y": 100
},
"type": "email.report"
},
{
"config": {
"secret": "…",
"url": "https://example.com/hooks/calls"
},
"id": "hook",
"label": "Notify",
"position": {
"x": 1280,
"y": 240
},
"type": "webhook.post"
}
]
},
"diff": {
"same": false,
"summary": "+1 step, +1 connection"
},
"errors": [],
"valid": true
}
DELETE /v1/ops-agents/{name}/listen/{trigger}#
Stop listening on a trigger. Closes the window without capturing; the last sample, if any, stays readable.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
trigger |
path |
string |
Sì |
Trigger type. |
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl -X DELETE "https://api.voiceland.ai/v1/ops-agents/{name}/listen/{trigger}" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"armed": false,
"trigger": "trigger.pbx_call"
}
GET /v1/ops-agents/{name}/listen/{trigger}#
Read a trigger's listening state and sample. Whether the window is open and the last captured sample: when it arrived, a summary, where it came from and the payload the run would have started with.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
trigger |
path |
string |
Sì |
Trigger type. |
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl "https://api.voiceland.ai/v1/ops-agents/{name}/listen/{trigger}" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"armed": false,
"sample": {
"payload": {
"callee_number": "401",
"caller_number": "+302101234567",
"direction": "inbound",
"duration_sec": 42
},
"received_at": "2026-05-09T10:31:12Z",
"source": {
"event_id": "evt_01",
"integration": "yeastar_crm"
},
"summary": "inbound call +302101234567 → 401",
"trigger": "trigger.pbx_call"
},
"trigger": "trigger.pbx_call"
}
POST /v1/ops-agents/{name}/listen/{trigger}#
Listen for a test event on a trigger. Opens a ten-minute window on one trigger: trigger.webhook, trigger.pbx_call (a finished PBX call) or trigger.agent (a call from another operations agent). The next such event is captured as a sample instead of starting a run, whether or not the agent is enabled. The window closes on the capture; read the sample with GET.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
trigger |
path |
string |
Sì |
Trigger type. |
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl -X POST "https://api.voiceland.ai/v1/ops-agents/{name}/listen/{trigger}" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"armed": true,
"armed_until": "2026-05-09T10:40:00Z",
"trigger": "trigger.pbx_call"
}
GET /v1/ops-agents/{name}/revisions#
Board history. Every save that changed the board, newest first, without the boards themselves: number, when, who, source (create, save, rollback, import, ai, template), a one-line summary of what changed against the previous revision, and the note the actor left. Kept a year.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl "https://api.voiceland.ai/v1/ops-agents/{name}/revisions" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"items": [
{
"actor": "maria@example.com",
"created_at": "2026-09-22T10:15:00Z",
"id": "00001790080000000000",
"number": 3,
"source": "save",
"summary": "+1 step, +1 connection"
}
]
}
GET /v1/ops-agents/{name}/revisions/{rev}#
One revision, with its board.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
rev |
path |
string |
Sì |
Revision id from the history. |
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl "https://api.voiceland.ai/v1/ops-agents/{name}/revisions/{rev}" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"definition": {
"edges": [
{
"from": "trigger",
"to": "download"
},
{
"from": "download",
"to": "transcribe"
},
{
"from": "transcribe",
"to": "report"
},
{
"from": "report",
"to": "mail"
},
{
"from": "report",
"to": "hook"
}
],
"nodes": [
{
"id": "trigger",
"label": "Call ended",
"position": {
"x": 80,
"y": 160
},
"type": "trigger.pbx_call"
},
{
"config": {
"url": "{{trigger.recording_url}}"
},
"id": "download",
"label": "Download recording",
"position": {
"x": 380,
"y": 160
},
"type": "recording.download"
},
{
"config": {
"file": "{{download.file}}",
"translate_to": [
"en"
]
},
"id": "transcribe",
"label": "Transcribe",
"position": {
"x": 680,
"y": 160
},
"type": "transcribe"
},
{
"config": {
"questions": [
{
"text": "Did the customer place an order?"
},
{
"text": "Was a complaint raised?"
}
],
"transcript": "{{transcribe.text}}"
},
"id": "report",
"label": "Report",
"position": {
"x": 980,
"y": 160
},
"type": "report.questions"
},
{
"config": {
"to": [
"ops@example.com"
]
},
"id": "mail",
"label": "Email the report",
"position": {
"x": 1280,
"y": 100
},
"type": "email.report"
},
{
"config": {
"secret": "…",
"url": "https://example.com/hooks/calls"
},
"id": "hook",
"label": "Notify",
"position": {
"x": 1280,
"y": 240
},
"type": "webhook.post"
}
]
},
"id": "00001790080000000000",
"number": 3,
"source": "save"
}
GET /v1/ops-agents/{name}/revisions/{rev}/diff/{other}#
Compare two revisions. Both boards whole, beside a structural diff: steps added, removed and changed (with each setting's before and after), connections added and removed, and steps merely moved. other may be current for the board as it is now.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
rev |
path |
string |
Sì |
The older revision. |
other |
path |
string |
Sì |
The newer revision, or current. |
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl "https://api.voiceland.ai/v1/ops-agents/{name}/revisions/{rev}/diff/{other}" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"diff": {
"edges_added": [],
"edges_removed": [],
"nodes_added": [],
"nodes_changed": [
{
"fields": [
{
"after": "bullets",
"before": "brief",
"key": "style"
}
],
"id": "summarize",
"type": "summarize"
}
],
"nodes_removed": [],
"same": false,
"summary": "1 changed"
},
"from": {
"id": "00001790070000000000",
"label": "#2"
},
"to": {
"id": "current",
"label": "current"
}
}
POST /v1/ops-agents/{name}/revisions/{rev}/rollback#
Restore a revision. The board becomes that revision again, and a NEW revision (source: rollback, note restored #n) records it, so the history only ever grows. 409 revision_invalid when the old board no longer validates against today's modules.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
rev |
path |
string |
Sì |
Revision id. |
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl -X POST "https://api.voiceland.ai/v1/ops-agents/{name}/revisions/{rev}/rollback" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"agent": {
"created_at": "2026-09-19T08:00:00Z",
"definition": {
"edges": [
{
"from": "trigger",
"to": "download"
},
{
"from": "download",
"to": "transcribe"
},
{
"from": "transcribe",
"to": "report"
},
{
"from": "report",
"to": "mail"
},
{
"from": "report",
"to": "hook"
}
],
"nodes": [
{
"id": "trigger",
"label": "Call ended",
"position": {
"x": 80,
"y": 160
},
"type": "trigger.pbx_call"
},
{
"config": {
"url": "{{trigger.recording_url}}"
},
"id": "download",
"label": "Download recording",
"position": {
"x": 380,
"y": 160
},
"type": "recording.download"
},
{
"config": {
"file": "{{download.file}}",
"translate_to": [
"en"
]
},
"id": "transcribe",
"label": "Transcribe",
"position": {
"x": 680,
"y": 160
},
"type": "transcribe"
},
{
"config": {
"questions": [
{
"text": "Did the customer place an order?"
},
{
"text": "Was a complaint raised?"
}
],
"transcript": "{{transcribe.text}}"
},
"id": "report",
"label": "Report",
"position": {
"x": 980,
"y": 160
},
"type": "report.questions"
},
{
"config": {
"to": [
"ops@example.com"
]
},
"id": "mail",
"label": "Email the report",
"position": {
"x": 1280,
"y": 100
},
"type": "email.report"
},
{
"config": {
"secret": "…",
"url": "https://example.com/hooks/calls"
},
"id": "hook",
"label": "Notify",
"position": {
"x": 1280,
"y": 240
},
"type": "webhook.post"
}
]
},
"description": "Transcribe every call and mail the report",
"enabled": true,
"hook_url": "https://api.voiceland.ai/v1/ops/hooks/k3JqTz9wY1n8Qb2vXm5LpR0e",
"kind": "operations",
"last_run_at": "2026-09-19T09:45:31Z",
"last_status": "ok",
"name": "after-call-report",
"runs": 12,
"updated_at": "2026-09-19T08:30:00Z"
},
"restored": 2,
"revision": {
"note": "restored #2",
"number": 5,
"source": "rollback"
}
}
POST /v1/ops-agents/{name}/run#
Start a run. Runs are asynchronous: the answer is the run row in state queued (or running on a single-node deployment); poll the detail. Any node of the service executes it under a lease; a node that stops hands the run to another one at its last checkpoint. payload is the trigger data; trigger (default trigger.manual) lets a test enter through trigger.pbx_call or trigger.webhook with sample data.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
Corpo della richiesta
application/json
Risposte
| Codice |
Descrizione |
202 |
Started. |
400 |
cannot_run (for example a board without that trigger), invalid_trigger. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl -X POST "https://api.voiceland.ai/v1/ops-agents/{name}/run" \
-H "Authorization: Bearer VL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"payload": {
"callee_number": "401",
"caller_number": "+306972605774",
"direction": "inbound",
"duration_sec": 42,
"recording_url": "https://example.com/api/v1.0/crm/recording?secret=…"
},
"trigger": "trigger.pbx_call"
}'
Esempio di risposta
{
"active_ms": 39100,
"agent": "after-call-report",
"billed_seconds": 60,
"cost_cents": 5,
"data": {
"download": {
"bytes": 812345,
"filename": "download-recording.wav"
},
"summarize": {
"text": "The caller ordered two souvlaki for pickup."
},
"transcribe": {
"language": "el",
"text": "Καλημέρα, θα ήθελα δύο σουβλάκια…"
},
"trigger": {
"caller_number": "+306972605774",
"direction": "inbound"
}
},
"duration_ms": 41200,
"finished_at": "2026-09-19T09:46:12Z",
"has_xlsx": true,
"id": "opr-c3e5e22e41b010c0",
"report": {
"answers": [
{
"answer": "Yes, two souvlaki for pickup.",
"matched": true,
"question": "Did the customer place an order?"
}
],
"title": "Call report +306972605774"
},
"started_at": "2026-09-19T09:45:31Z",
"status": "ok",
"steps": [
{
"attempt": 1,
"duration_ms": 1830,
"finished_at": "2026-09-19T09:45:33Z",
"input_bytes": 96,
"input_preview": "{\"max_mb\":200,\"url\":\"https://example.com/api/v1.0/crm/recording?secret=…\"}",
"label": "Download recording",
"max_attempts": 24,
"node_id": "download",
"output_bytes": 118,
"output_preview": "{\"bytes\":812345,\"content_type\":\"audio/wav\",\"file\":\"(file download-recording.wav)\",\"filename\":\"download-recording.wav\"}",
"retry_interval_min": 60,
"started_at": "2026-09-19T09:45:31Z",
"status": "ok",
"type": "recording.download"
},
{
"attempt": 1,
"attempts": [
{
"at": "2026-09-19T09:46:11Z",
"attempt": 1,
"duration_ms": 412,
"error": "webhook: the endpoint answered 503: upstream maintenance",
"remote_status": 503
}
],
"error": "webhook: the endpoint answered 503: upstream maintenance",
"label": "Notify",
"max_attempts": 24,
"next_attempt_at": "2026-09-19T10:46:12Z",
"node_id": "hook",
"remote_body": "upstream maintenance",
"remote_status": 503,
"retry_interval_min": 60,
"status": "retrying",
"type": "webhook.post"
}
],
"trigger_summary": "inbound call +306972605774 → 401",
"trigger_type": "trigger.pbx_call",
"updated_at": "2026-09-19T09:46:12Z"
}
GET /v1/ops-agents/{name}/runs#
Run history. Newest first, without step previews (the detail has them). from/to narrow the window by start time (RFC3339, or a plain YYYY-MM-DD day: from is that day's 00:00Z, to its last millisecond; either bound may be left open); 400 invalid_from, invalid_to, invalid_window (from must be before to). totals sums the rows returned (ok = finished clean, failed = error or partial); truncated says the window holds more rows than limit.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
from |
query |
string |
|
Window start by run start time: RFC3339 or YYYY-MM-DD (00:00Z). Open when omitted. |
to |
query |
string |
|
Window end: RFC3339 or YYYY-MM-DD (that day's last millisecond). Open when omitted. |
limit |
query |
integer |
|
Max rows (default 50, max 500). |
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl "https://api.voiceland.ai/v1/ops-agents/{name}/runs" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"from": "2026-09-01T00:00:00Z",
"items": [
{
"active_ms": 39100,
"agent": "after-call-report",
"billed_seconds": 60,
"cost_cents": 5,
"data": {
"download": {
"bytes": 812345,
"filename": "download-recording.wav"
},
"summarize": {
"text": "The caller ordered two souvlaki for pickup."
},
"transcribe": {
"language": "el",
"text": "Καλημέρα, θα ήθελα δύο σουβλάκια…"
},
"trigger": {
"caller_number": "+306972605774",
"direction": "inbound"
}
},
"duration_ms": 41200,
"finished_at": "2026-09-19T09:46:12Z",
"has_xlsx": true,
"id": "opr-c3e5e22e41b010c0",
"report": {
"answers": [
{
"answer": "Yes, two souvlaki for pickup.",
"matched": true,
"question": "Did the customer place an order?"
}
],
"title": "Call report +306972605774"
},
"started_at": "2026-09-19T09:45:31Z",
"status": "ok",
"steps": [
{
"attempt": 1,
"duration_ms": 1830,
"finished_at": "2026-09-19T09:45:33Z",
"input_bytes": 96,
"input_preview": "{\"max_mb\":200,\"url\":\"https://example.com/api/v1.0/crm/recording?secret=…\"}",
"label": "Download recording",
"max_attempts": 24,
"node_id": "download",
"output_bytes": 118,
"output_preview": "{\"bytes\":812345,\"content_type\":\"audio/wav\",\"file\":\"(file download-recording.wav)\",\"filename\":\"download-recording.wav\"}",
"retry_interval_min": 60,
"started_at": "2026-09-19T09:45:31Z",
"status": "ok",
"type": "recording.download"
},
{
"attempt": 1,
"attempts": [
{
"at": "2026-09-19T09:46:11Z",
"attempt": 1,
"duration_ms": 412,
"error": "webhook: the endpoint answered 503: upstream maintenance",
"remote_status": 503
}
],
"error": "webhook: the endpoint answered 503: upstream maintenance",
"label": "Notify",
"max_attempts": 24,
"next_attempt_at": "2026-09-19T10:46:12Z",
"node_id": "hook",
"remote_body": "upstream maintenance",
"remote_status": 503,
"retry_interval_min": 60,
"status": "retrying",
"type": "webhook.post"
}
],
"trigger_summary": "inbound call +306972605774 → 401",
"trigger_type": "trigger.pbx_call",
"updated_at": "2026-09-19T09:46:12Z"
}
],
"to": "2026-09-30T23:59:59Z",
"totals": {
"billed_seconds": 60,
"charged_cents": 0,
"cost_cents": 5,
"failed": 0,
"ok": 1,
"pool_seconds": 15,
"runs": 1
},
"truncated": false
}
GET /v1/ops-agents/{name}/runs/export.xlsx#
Export one agent's runs as a workbook. As the cross-agent export, for one agent: filename="ops-runs-<agent>-<from>_<to>.xlsx".
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
from |
query |
string |
|
Window start by run start time: RFC3339 or YYYY-MM-DD (00:00Z). Open when omitted. |
to |
query |
string |
|
Window end: RFC3339 or YYYY-MM-DD (that day's last millisecond). Open when omitted. |
Risposte
| Codice |
Descrizione |
200 |
An .xlsx workbook. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl "https://api.voiceland.ai/v1/ops-agents/{name}/runs/export.xlsx" \
-H "Authorization: Bearer VL_API_KEY"
GET /v1/ops-agents/{name}/runs/{id}#
Run detail. Every step with its start, duration, bytes received and produced, a redacted preview of both, its outcome (ok, error, skipped with the reason, retrying with next_attempt_at, pending), the attempt taken against the policy (attempt of max_attempts, every retry_interval_min), what a remote endpoint answered on failure (remote_status, remote_body) and the history of failed attempts. data is the full data map every step received (each node's output under its id), output the same capped, report the question report and has_xlsx whether a workbook exists. A run is queued while it waits for a worker, retrying while a step waits for its next attempt; active_ms is the worked time the bill is based on; deliveries counts the workers that picked it up (a run interrupted ten times is given up on). Webhook and dispatcher calls carry X-Voiceland-Idempotency-Key, the same value on every delivery of one attempt of one step.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
id |
path |
string |
Sì |
Run id (opr-…). |
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl "https://api.voiceland.ai/v1/ops-agents/{name}/runs/{id}" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"active_ms": 39100,
"agent": "after-call-report",
"billed_seconds": 60,
"cost_cents": 5,
"data": {
"download": {
"bytes": 812345,
"filename": "download-recording.wav"
},
"summarize": {
"text": "The caller ordered two souvlaki for pickup."
},
"transcribe": {
"language": "el",
"text": "Καλημέρα, θα ήθελα δύο σουβλάκια…"
},
"trigger": {
"caller_number": "+306972605774",
"direction": "inbound"
}
},
"duration_ms": 41200,
"finished_at": "2026-09-19T09:46:12Z",
"has_xlsx": true,
"id": "opr-c3e5e22e41b010c0",
"report": {
"answers": [
{
"answer": "Yes, two souvlaki for pickup.",
"matched": true,
"question": "Did the customer place an order?"
}
],
"title": "Call report +306972605774"
},
"started_at": "2026-09-19T09:45:31Z",
"status": "ok",
"steps": [
{
"attempt": 1,
"duration_ms": 1830,
"finished_at": "2026-09-19T09:45:33Z",
"input_bytes": 96,
"input_preview": "{\"max_mb\":200,\"url\":\"https://example.com/api/v1.0/crm/recording?secret=…\"}",
"label": "Download recording",
"max_attempts": 24,
"node_id": "download",
"output_bytes": 118,
"output_preview": "{\"bytes\":812345,\"content_type\":\"audio/wav\",\"file\":\"(file download-recording.wav)\",\"filename\":\"download-recording.wav\"}",
"retry_interval_min": 60,
"started_at": "2026-09-19T09:45:31Z",
"status": "ok",
"type": "recording.download"
},
{
"attempt": 1,
"attempts": [
{
"at": "2026-09-19T09:46:11Z",
"attempt": 1,
"duration_ms": 412,
"error": "webhook: the endpoint answered 503: upstream maintenance",
"remote_status": 503
}
],
"error": "webhook: the endpoint answered 503: upstream maintenance",
"label": "Notify",
"max_attempts": 24,
"next_attempt_at": "2026-09-19T10:46:12Z",
"node_id": "hook",
"remote_body": "upstream maintenance",
"remote_status": 503,
"retry_interval_min": 60,
"status": "retrying",
"type": "webhook.post"
}
],
"trigger_summary": "inbound call +306972605774 → 401",
"trigger_type": "trigger.pbx_call",
"updated_at": "2026-09-19T09:46:12Z"
}
GET /v1/ops-agents/{name}/runs/{id}/report.xlsx#
Download the run's Excel report.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
id |
path |
string |
Sì |
Run id. |
Risposte
| Codice |
Descrizione |
200 |
The workbook. |
401 |
Missing or invalid API key. |
404 |
no_report: the run produced none. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl "https://api.voiceland.ai/v1/ops-agents/{name}/runs/{id}/report.xlsx" \
-H "Authorization: Bearer VL_API_KEY"
POST /v1/ops-agents/{name}/runs/{id}/steps/{node}/retry#
Retry a failed step now. Runs one error or retrying step again at once, then everything after it; the run is answered in state retrying and polled. A person's retry runs even when the policy's attempts are used up. 409 cannot_retry for a step that did not fail or a run still in progress.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
id |
path |
string |
Sì |
Run id. |
node |
path |
string |
Sì |
The step's node id. |
Risposte
| Codice |
Descrizione |
202 |
Retrying. |
401 |
Missing or invalid API key. |
409 |
cannot_retry. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl -X POST "https://api.voiceland.ai/v1/ops-agents/{name}/runs/{id}/steps/{node}/retry" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"active_ms": 39100,
"agent": "after-call-report",
"billed_seconds": 60,
"cost_cents": 5,
"data": {
"download": {
"bytes": 812345,
"filename": "download-recording.wav"
},
"summarize": {
"text": "The caller ordered two souvlaki for pickup."
},
"transcribe": {
"language": "el",
"text": "Καλημέρα, θα ήθελα δύο σουβλάκια…"
},
"trigger": {
"caller_number": "+306972605774",
"direction": "inbound"
}
},
"duration_ms": 41200,
"finished_at": "2026-09-19T09:46:12Z",
"has_xlsx": true,
"id": "opr-c3e5e22e41b010c0",
"report": {
"answers": [
{
"answer": "Yes, two souvlaki for pickup.",
"matched": true,
"question": "Did the customer place an order?"
}
],
"title": "Call report +306972605774"
},
"started_at": "2026-09-19T09:45:31Z",
"status": "ok",
"steps": [
{
"attempt": 1,
"duration_ms": 1830,
"finished_at": "2026-09-19T09:45:33Z",
"input_bytes": 96,
"input_preview": "{\"max_mb\":200,\"url\":\"https://example.com/api/v1.0/crm/recording?secret=…\"}",
"label": "Download recording",
"max_attempts": 24,
"node_id": "download",
"output_bytes": 118,
"output_preview": "{\"bytes\":812345,\"content_type\":\"audio/wav\",\"file\":\"(file download-recording.wav)\",\"filename\":\"download-recording.wav\"}",
"retry_interval_min": 60,
"started_at": "2026-09-19T09:45:31Z",
"status": "ok",
"type": "recording.download"
},
{
"attempt": 1,
"attempts": [
{
"at": "2026-09-19T09:46:11Z",
"attempt": 1,
"duration_ms": 412,
"error": "webhook: the endpoint answered 503: upstream maintenance",
"remote_status": 503
}
],
"error": "webhook: the endpoint answered 503: upstream maintenance",
"label": "Notify",
"max_attempts": 24,
"next_attempt_at": "2026-09-19T10:46:12Z",
"node_id": "hook",
"remote_body": "upstream maintenance",
"remote_status": 503,
"retry_interval_min": 60,
"status": "retrying",
"type": "webhook.post"
}
],
"trigger_summary": "inbound call +306972605774 → 401",
"trigger_type": "trigger.pbx_call",
"updated_at": "2026-09-19T09:46:12Z"
}
POST /v1/ops-agents/{name}/steps/test#
Preview or send one deliver step. Renders a send-email or webhook step against real data and shows exactly what would go out (method, URL, headers with credentials masked, body; or the message envelope). With send=true it is sent and the other side's answer is returned. Data comes from a past run (run_id: its steps' recorded outputs) and/or a sample trigger payload; definition may carry the unsaved board. Nothing is billed and no run is recorded.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
name |
path |
string |
Sì |
Agent name. |
Corpo della richiesta
application/json
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl -X POST "https://api.voiceland.ai/v1/ops-agents/{name}/steps/test" \
-H "Authorization: Bearer VL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"node_id": "notify",
"payload": {
"caller_number": "+302101234567",
"direction": "inbound"
},
"run_id": "run_01J9",
"send": false
}'
Esempio di risposta
{
"data_from": {
"payload": true,
"run_id": "run_01J9"
},
"node_id": "notify",
"preview": {
"body": {
"caller": "+302101234567",
"event": "ops.step"
},
"bytes": 52,
"headers": {
"Authorization": "•••",
"Content-Type": "application/json",
"X-Voiceland-Run": "test-3f9a1c2b"
},
"method": "POST",
"timeout_s": 15,
"url": "https://example.com/hooks/calls?source=voiceland"
},
"sent": false,
"type": "webhook.post"
}
GET /v1/ops-modules#
Module catalog. Every module a board may use, with its settings (fields: type, default, options), what it produces (outputs), whether it is a trigger, its default retry policy (retry: attempts and interval_min; a node overrides it with a retry object in its config), and the run rate. Modules: the triggers (trigger.pbx_call, trigger.webhook, trigger.manual, trigger.agent, trigger.schedule with a preset or cron expression, timezone, overlap and catch-up policies, at most every 5 minutes), recording.download, transcribe, summarize, report.questions, email.report, webhook.post, dispatch (start several agents and/or post to several URLs at once), condition, agent.call.
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl "https://api.voiceland.ai/v1/ops-modules" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"items": [
{
"category": "media",
"fields": [
{
"default": "{{trigger.recording_url}}",
"key": "url",
"label": "Recording URL",
"required": true,
"type": "string"
}
],
"icon": "mdi-download-circle-outline",
"id": "recording.download",
"name": "Download recording",
"outputs": [
{
"description": "Temporary file (internal)",
"key": "file"
}
],
"trigger": false
}
],
"rate": {
"cents_per_minute": 5,
"currency": "EUR",
"in_regime": true,
"min_seconds": 30,
"overage_cents_per_min": 20,
"package_id": "growth",
"percent": 25,
"questions_included": 5,
"seconds_per_extra_question": 60
}
}
GET /v1/ops-modules/schema#
The connection schema. What each module produces (with the kind of each output), what each data-bearing setting accepts and from where, which modules must be upstream of which, and the graph rules in words. Derived from the module catalog; this is what the AI designer and the import check are held to.
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl "https://api.voiceland.ai/v1/ops-modules/schema" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"modules": [
{
"consumes": [
{
"field": "file",
"from": [
"recording.download.file"
],
"kinds": [
"file"
],
"required": true
}
],
"id": "transcribe",
"needs": [
"recording.download"
],
"produces": [
{
"key": "text",
"kind": "text"
}
]
}
],
"rules": [
"Every step must be reachable from a trigger through edges, or it never runs."
]
}
GET /v1/ops-modules/templates#
The template library. Ready-made boards by integration (?integration=yeastar|hubspot|generic), each with its trigger, a sample payload and the placeholders to fill in.
Parametri
| Nome |
Posizione |
Tipo |
Obbligatorio |
Descrizione |
integration |
query |
string |
|
yeastar, hubspot or generic. |
Risposte
| Codice |
Descrizione |
200 |
Success. |
401 |
Missing or invalid API key. |
4XX |
Request error (validation, not-found, etc.). |
5XX |
Server or upstream error. |
Esempio di richiesta
curl "https://api.voiceland.ai/v1/ops-modules/templates" \
-H "Authorization: Bearer VL_API_KEY"
Esempio di risposta
{
"integrations": [
"yeastar",
"hubspot",
"generic"
],
"items": [
{
"id": "yeastar-summary-email",
"integration": "yeastar",
"name": "Email a summary of every call",
"placeholders": [
"mail.to"
],
"trigger": "trigger.pbx_call"
}
]
}
Schemi#
Error#
Error envelope returned for non-2xx responses.
| Campo |
Tipo |
Obbligatorio |
Descrizione |
error |
object |
Sì |
|
OpsAgentRequest#
| Campo |
Tipo |
Obbligatorio |
Descrizione |
definition |
string (byte) |
Sì |
|
description |
string |
Sì |
|
enabled |
boolean |
|
|
name |
string |
Sì |
|
note |
string |
|
|
source |
string |
|
|