API reference

Graph agents

Validate a graph agent's nodes and edges before saving it, and test it with scripted callers.

A graph agent is an agent whose conversation is a set of nodes joined by edges, rather than one prompt. You create and update one through the agent endpoints, with llm_agent.agent_type set to graph_agent and the graph under llm_agent.llm_config. Saving an agent with a broken graph is refused. This page has the extra endpoints: one checks a graph and lists every problem without saving anything, and the workflow tests run scripted callers through a saved graph. The graph agents guide describes node and edge types.

OperationMethod and pathMinimum role
Validate a graphPOST /graph-agent/validatemember
List scenariosGET /graph-agent/{agent_id}/tests/scenariosviewer
Create a scenarioPOST /graph-agent/{agent_id}/tests/scenariosmember
Get a scenarioGET /graph-agent/{agent_id}/tests/scenarios/{scenario_id}viewer
Replace a scenarioPUT /graph-agent/{agent_id}/tests/scenarios/{scenario_id}member
Delete a scenarioDELETE /graph-agent/{agent_id}/tests/scenarios/{scenario_id}member
Suggest scenariosPOST /graph-agent/{agent_id}/tests/suggestmember
Start a test runPOST /graph-agent/{agent_id}/tests/runsmember
List test runsGET /graph-agent/{agent_id}/tests/runsviewer
Get a test runGET /graph-agent/{agent_id}/tests/runs/{run_id}viewer

Validate a graph

POST /graph-agent/validate

Returns the errors and warnings for a graph. Errors would block saving the agent; warnings would not.

Auth: API key or session token. Role: member and up.

Send one of these shapes:

ParameterInTypeRequiredDescription
agent_configbodyobjectone ofA whole agent configuration, as you would send it when creating an agent. Its tools are read from the configuration.
llm_configbodyobjectone ofOnly the graph: nodes, current_node_id and the rest of the graph settings, such as global_edges (the safety transitions from every node).
api_toolsbodyobjectnoWith llm_config: {"tools": [{"name": "..."}]}, the tool names that nodes may reference.

Request

bash
curl -X POST https://api.sevennadi.si/graph-agent/validate \
  -H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef" \
  -H "Content-Type: application/json" \
  -d '{
    "llm_config": {
      "current_node_id": "welcome",
      "nodes": [
        {
          "id": "welcome",
          "prompt": "Greet the caller and find out what they need.",
          "edges": [{"to_node_id": "ghost", "condition": "Caller wants order status"}]
        },
        {"id": "closing", "prompt": "Thank the caller and say goodbye.", "edges": []}
      ]
    }
  }'

Response 200 OK

json
{
  "valid": false,
  "errors": [
    {
      "severity": "error",
      "code": "missing_transition_target",
      "message": "Node 'welcome': edge 0 points to unknown node 'ghost'",
      "node_id": "welcome",
      "path": "nodes.0.edges.0"
    }
  ],
  "warnings": [
    {
      "severity": "warning",
      "code": "unreachable_node",
      "message": "Node 'closing' has no transitions pointing to it",
      "node_id": "closing"
    }
  ]
}

A graph with no problems returns {"valid": true, "errors": [], "warnings": []}. valid is true whenever errors is empty, even if there are warnings.

Each issue has severity, code and message, and where they apply node_id and path (a dotted path into the graph, such as nodes.1.edges.0). Codes you may see include:

CodeSeverityMeaning
no_nodeserrorThe graph has no nodes.
invalid_nodeerrorA node is not an object.
missing_node_iderrorA node has no id.
duplicate_node_iderrorTwo nodes share an id.
no_start_nodeerrorcurrent_node_id is missing or names no node.
invalid_node_typeerrorA node's node_type is not one the platform knows.
invalid_edgeserrorA node's edges is not a list of objects.
missing_transition_targeterrorAn edge, or a global edge, points to a node that does not exist.
invalid_condition_typeerrorAn edge's condition_type is not one the platform knows.
missing_event_nameerrorAn event edge has no event_name.
invalid_dtmf_eventerrorA keypad event name is malformed.
unreachable_toolerrorA node's function_call names a tool that is not in api_tools.
knowledge_base_no_vectorserrorA rag_config has no knowledge base ids.
invalid_collect_dtmferrorA node's keypad-entry settings are invalid.
router_speakserrorA router node sets a prompt or a static message.
router_no_catch_allerrorA router node has no unconditional edge.
router_event_edgeerrorA router node has an event edge.
router_collect_dtmferrorA router node tries to collect keypad digits.
router_cycleerrorRouters form a loop that never reaches a speaking node.
invalid_variable_typeerrorvariable_types has an unknown type.
invalid_agent_informationerroragent_information is not a string.
invalid_global_edgeserrorglobal_edges is not a list of objects.
invalid_global_edgeerrorA global edge has a condition_type (only intent edges can be global) or no condition. Its path is global_edges.<n>.
empty_promptwarningAn LLM node has no prompt.
static_without_messagewarningA static node has no static_message.
empty_intent_conditionwarningAn intent edge on a node with several edges has no condition.
duplicate_prioritieswarningTwo rule edges of a node share a priority.
unreachable_nodewarningNo edge leads to the node.
no_global_safetywarningA node has an intent edge about danger, safety, an emergency or fraud, but the graph has no global_edges: a caller on any other node cannot take it.

Expression and edge-parameter problems have further codes of their own. Treat any code you do not recognise by its severity.

Errors

StatusMessage
400agent_config is not a graph agent: set llm_agent.agent_type to "graph_agent"
400Send agent_config, or llm_config (with nodes)

Workflow tests

A workflow test plays a scripted caller through a saved graph agent and checks where the conversation goes. Each caller line is routed the way the dashboard's test chat routes it: the agent's model picks the transition. When the agent has no usable model, a keyword rule decides instead and the run says so (routing is keyword). Nothing is dialled, no tool is called and no audio is involved, so a scenario that passes here can still behave differently on a real call.

Scenarios

GET /graph-agent/{agent_id}/tests/scenarios lists them. POST /graph-agent/{agent_id}/tests/scenarios creates one (201); GET, PUT and DELETE /graph-agent/{agent_id}/tests/scenarios/{scenario_id} read, replace and delete one.

ParameterInTypeRequiredDescription
namebodystringyesUp to 200 characters.
linesbodyarray of stringsyesWhat the caller says, in order (1 to 30 lines). event:<name> injects an event instead of a line.
variablesbodyobjectnoThe values the call starts with.
expectbodyobjectyesAt least one of final_node (node id), visits (node ids that must be on the path), avoids (node ids that must not be) and values (extracted values by name).
bash
curl -X POST https://api.sevennadi.si/graph-agent/3c90c3cc-0d44-4b50-8822-8dd25736052a/tests/scenarios \
  -H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Order status, happy path",
    "lines": ["Where is my order ORD-3?", "Thanks, that is all"],
    "expect": {"final_node": "closing", "visits": ["collect_order"], "values": {"order_id": "ORD-3"}}
  }'

Suggest scenarios

POST /graph-agent/{agent_id}/tests/suggest

Proposes one scenario for each path from the start node to a closing node (at most 12). The lines come from the edge conditions, reworded by the agent's model when it is available (phrased is llm, otherwise conditions). Send {"save": true} to store the proposals whose name is not taken, and {"phrase": false} to skip the model. The response has suggestions and saved. Read suggested lines before relying on them.

Start a test run

POST /graph-agent/{agent_id}/tests/runs

Runs in the background and answers 202 with the run. One run per agent at a time (409 otherwise).

ParameterInTypeRequiredDescription
repeatsbodyintegernoHow many times each scenario is played: 1 to 10, default 3.
scenario_idsbodyarraynoOnly these scenarios. Default: all.
safety_probebodybooleannoDefault true: see below.
emergency_linebodystringnoThe caller line the safety probe says.
agent_repliesbodybooleannoDefault false. When true the model also speaks for the agent after each turn, so routing reads both sides. Slower.

Read a test run

GET /graph-agent/{agent_id}/tests/runs lists the last 20 runs without detail. GET /graph-agent/{agent_id}/tests/runs/{run_id} returns one in full; poll it until status is done (or error).

FieldMeaning
routing, modelllm and the model that routed, or keyword when there was none.
summaryCounts: scenarios passed, failed and flaky, conversations passed, nodes and edges never reached, safety probes failed.
results[]Per scenario: status (passed, failed, or flaky when only some repeats passed), passes, repeats, pass_rate, and runs[] with the path, the extracted values, each failed expectation in failures[], and per turn the routing decisions[] (from, to, by: llm, keyword or rule, reason, and global: the position of the global edge when one was taken).
coveragenodes.unreached and edges.unreached: what no scenario reached.
safetyThe safety probe. mode says how the safety transitions were found. global: the graph has global_edges; an emergency line is said on every node a caller speaks on, and the conversation must reach a global edge's target in that turn (probes[], with ok; a node is probed once per kind of safety edge). wording: there are none, so intent edges whose condition is about danger, safety, an emergency or fraud count; only the nodes that can take one within a single caller turn are probed, later_only lists nodes that reach one only in a later turn, and unreachable lists nodes from which none can ever be reached: treat it as a warning and add global_edges. none: nothing to probe. In safety_edges[], a global edge has from * and type global.