Intent
The intent layer turns a plain-language task into a network. Decompose a task plans a network specification from a description without saving or running it, so a caller can review or edit the plan first; the streaming variant reports the planner's progress as it works.
Render a template and run it is the one-call path for automation: it renders a chosen template into a fresh network and starts a run in a single request. That shape is deliberate — a caller that fires repeatedly (for example a trigger answering inbound mail) must know whether a run started, because a two-step plan-then-run flow leaves a failure between the steps ambiguous.
Authenticate with your access token. Your token scopes which private templates and workspace context participate in planning.
- POST /intent/decompose Decompose a task
- POST /intent/decompose/stream Stream a decomposition
- POST /intent-templates/{template_id}/execute Render a template and run it
Paths are relative to /networks-api
Decompose a task
POST /networks-api/intent/decompose
Plans a runnable network from a task description and returns it, without saving or executing anything. The response carries the generated network_spec, a canvas view (nodes and connections), the resolved agents, and planning metadata.
Provide the task as task_description (or user_request). Optionally pin the plan to a specific template with template_id, scope agents and templates to a workspace with workspace_slug (or the X-Workspace-Slug header), and attach document text with document_text. When planning cannot produce a detailed plan it falls back to a simpler one and reports that in validation_errors rather than failing.
Save the returned specification with Create or update a network, then run it with Execute a network.
- Authentication
- Bearer token How it works
Headers
-
X-Workspace-SlugstringWorkspace to scope agent and template resolution to.
-
X-Client-TimezonestringIANA timezone used to resolve relative dates in the plan.
Body parameters application/json
-
task_descriptionstringThe task to plan for.
user_requestis accepted as an alias. When absent, the response issuccess: falsewith a validation error rather than an error status. -
workspace_slugstringWorkspace to scope agents and templates to.
-
template_idstringPlan against this template explicitly instead of routing by intent rules. Ignored if it does not resolve for the caller.
-
domain_idstringOverride the inferred domain.
-
document_textstringAttachment or document text to plan against.
document_contentanddocumentare accepted as aliases. -
plan_depthstringPreferred planning depth. An unrecognised value is ignored rather than rejected.
-
client_timezonestringIANA timezone used to resolve relative dates in the plan.
Returns
Returns the planned network specification, a canvas view, resolved agents, and planning metadata.
Errors
- 400 The supplied
domain_idnames a domain that is no longer supported.
curl -X POST "$VDF_BASE_URL/networks-api/intent/decompose" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"task_description": "Classify an inbound support ticket and draft a reply."
}' const response = await fetch(`${process.env.VDF_BASE_URL}/networks-api/intent/decompose`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
task_description: 'Classify an inbound support ticket and draft a reply.',
}),
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.post(
f"{os.environ['VDF_BASE_URL']}/networks-api/intent/decompose",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
json={
"task_description": "Classify an inbound support ticket and draft a reply.",
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"message": "Decomposition completed",
"task_description": "Classify an inbound support ticket and draft a reply.",
"network_spec": {
"network_id": "classify-an-inbound-support-ticket-20260901-093000",
"version": "1.0",
"name": "Classify an inbound support ticket and draft a reply",
"domain_id": "customer-success",
"mode": "execute",
"created_by": "intent-layer",
"nodes": [
{
"node_id": "classify",
"type": "LLMAgent",
"label": "Classify the ticket",
"source": "manual"
},
{
"node_id": "draft_reply",
"type": "LLMAgent",
"label": "Draft a reply",
"source": "manual"
}
],
"edges": [
{
"from": "classify",
"to": "draft_reply",
"type": "sequential"
}
],
"tools": []
},
"intent_metadata": {
"task_class": "generic"
},
"agent_selection": {
"counts": {
"resolved": 2
},
"mode_effective": "hybrid"
},
"validation_errors": [],
"nodes": [
{
"id": "classify",
"label": "Classify the ticket"
},
{
"id": "draft_reply",
"label": "Draft a reply"
}
],
"connections": [
{
"from": "classify",
"to": "draft_reply"
}
]
} Stream a decomposition
POST /networks-api/intent/decompose/stream
Streams planning progress, then the planned network.
The streaming form of Decompose a task. Returns a Server-Sent Events stream: a sequence of progress events reporting each planning stage, then a final result event carrying the same payload the non-streaming endpoint returns. The request body is identical to Decompose a task.
If the client disconnects mid-plan, planning is cancelled.
- Authentication
- Bearer token How it works
Headers
-
X-Workspace-SlugstringWorkspace to scope agent and template resolution to.
-
X-Client-TimezonestringIANA timezone used to resolve relative dates in the plan.
Body parameters application/json
-
task_descriptionstringThe task to plan for.
user_requestis accepted as an alias. -
workspace_slugstringWorkspace to scope agents and templates to.
-
template_idstringPlan against this template explicitly instead of routing by intent rules.
-
domain_idstringOverride the inferred domain.
-
document_textstringAttachment or document text to plan against.
-
plan_depthstringPreferred planning depth.
-
client_timezonestringIANA timezone used to resolve relative dates in the plan.
Returns
Streams progress events, then a final result event with the planned network.
curl -N -X POST "$VDF_BASE_URL/networks-api/intent/decompose/stream" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" \
-H "Accept: text/event-stream" \
-H "Content-Type: application/json" \
-d '{
"task_description": "Classify an inbound support ticket and draft a reply."
}' const response = await fetch(`${process.env.VDF_BASE_URL}/networks-api/intent/decompose/stream`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
Accept: 'text/event-stream',
'Content-Type': 'application/json',
},
body: JSON.stringify({
task_description: 'Classify an inbound support ticket and draft a reply.',
}),
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
for (;;) {
const { value, done } = await reader.read();
if (done) break;
process.stdout.write(value); // Server-sent events: "event:" and "data:" lines
} import os
import requests
response = requests.post(
f"{os.environ['VDF_BASE_URL']}/networks-api/intent/decompose/stream",
headers={
"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}",
"Accept": "text/event-stream",
},
json={
"task_description": "Classify an inbound support ticket and draft a reply.",
},
stream=True,
timeout=30,
)
response.raise_for_status()
for line in response.iter_lines(decode_unicode=True):
if line:
print(line) event: progress
data: {"stage":"domain_inferred","message":"Domain inferred: customer-success","kind":"decision","domain_id":"customer-success"}
event: progress
data: {"stage":"llm_call_started","message":"Generating network spec...","kind":"progress"}
event: progress
data: {"stage":"llm_call_done","message":"Network spec generated.","kind":"success"}
event: result
data: {"success":true,"message":"Decomposition completed","network_spec":{"network_id":"classify-an-inbound-support-ticket-20260901-093000","name":"Classify an inbound support ticket and draft a reply","domain_id":"customer-success","nodes":[{"node_id":"classify","type":"LLMAgent"}],"edges":[]},"validation_errors":[],"nodes":[],"connections":[]} Render a template and run it
POST /networks-api/intent-templates/{template_id}/execute
Renders a template into a network and starts a run in one call.
Renders an intent template into a fresh network and starts executing it, returning the new run's identifier. input is the task text.
Pass network_id to name the network the template is saved under. This is the endpoint's retry-safety mechanism: a caller that fires repeatedly should pass a stable network_id so each run re-renders and overwrites the same network rather than leaving one behind per run. network_name sets that network's display name. Any other body fields are forwarded to the intent layer and then become the run's input.
The template must resolve for the caller and must actually render for this input; if the intent layer would fall back to a different plan, the request fails with 422 instead of quietly running something else. Follow the run's progress by reading its status and node outputs with Retrieve a run.
- Authentication
- Bearer token How it works
Path parameters
-
template_idstring RequiredThe template to render and run.
Headers
-
X-Client-TimezonestringIANA timezone used to resolve relative dates in the run.
Body parameters application/json
-
inputstring RequiredThe task text.
task_descriptionis accepted as an alias. An empty value is rejected with422. -
network_idstringIdentifier the rendered network is saved under. Pass a stable value from a repeating caller so retries target one network.
-
network_namestringDisplay name for the saved network.
Returns
Returns the new run's identifier and status, plus the network it was saved under and the template used.
Errors
- 404 No template with this identifier is available to the caller.
- 422
inputis missing, or the template could not be rendered for this input.
curl -X POST "$VDF_BASE_URL/networks-api/intent-templates/support-triage-template/execute" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"input": "Draft a reply to the escalation in ticket 4471.",
"network_id": "support-autoreply",
"network_name": "Support auto-reply"
}' const response = await fetch(`${process.env.VDF_BASE_URL}/networks-api/intent-templates/support-triage-template/execute`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
input: 'Draft a reply to the escalation in ticket 4471.',
network_id: 'support-autoreply',
network_name: 'Support auto-reply',
}),
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.post(
f"{os.environ['VDF_BASE_URL']}/networks-api/intent-templates/support-triage-template/execute",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
json={
"input": "Draft a reply to the escalation in ticket 4471.",
"network_id": "support-autoreply",
"network_name": "Support auto-reply",
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"run_id": "run-20260901-093500-b2c3d4",
"status": "started",
"network_id": "support-autoreply",
"network_version": "1.0",
"template_id": "support-triage-template"
}