Agent Workflow Examples
Concrete interaction sequences for agents using zindex. Each example shows the exact call order, including error recovery.
For the full behavioral guide, see How AI Agents Should Use Zindex.
Example 1: Create scene, add elements, render
The basic happy path for creating a new diagram.
HTTP
POST /v1/scenes
→ { "schemaVersion": "0.1", "scene": { "id": "arch", "units": "px",
"canvas": { "width": 800, "height": 400, "background": "#ffffff" } },
"elements": [] }
← { "sceneId": "arch", "revision": 1, "diagnostics": [] }
POST /v1/scenes/arch/applyOps
→ { "schemaVersion": "0.1", "sceneId": "arch", "baseRevision": 1,
"ops": [
{ "op": "createNode", "id": "api", "nodeType": "service",
"shape": "roundedRect", "label": "API Gateway",
"layout": { "mode": "absolute", "x": 50, "y": 100, "width": 180, "height": 70 } },
{ "op": "createNode", "id": "db", "nodeType": "database",
"shape": "cylinder", "label": "PostgreSQL",
"layout": { "mode": "absolute", "x": 350, "y": 90, "width": 160, "height": 90 } }
] }
← { "applied": true, "sceneId": "arch", "revision": 2, "diagnostics": [] }
POST /v1/scenes/arch/applyOps
→ { "schemaVersion": "0.1", "sceneId": "arch", "baseRevision": 2,
"ops": [
{ "op": "createEdge", "id": "e1", "from": { "elementId": "api" },
"to": { "elementId": "db" }, "router": "orthogonal", "label": "queries" }
] }
← { "applied": true, "sceneId": "arch", "revision": 3, "diagnostics": [] }
POST /v1/scenes/arch/render
→ { "format": "svg" }
← { "output": { "mimeType": "image/svg+xml", "content": "<svg>...</svg>" } }
MCP
dsp_create_scene → scene with id "arch", empty elements
dsp_apply_ops → createNode "api", createNode "db"
dsp_apply_ops → createEdge "e1" from api to db
dsp_render_scene → format "svg"
Key pattern: Nodes first, edges second, render last.
Example 2: Edit an existing scene
Read the latest state, plan a minimal patch, apply it.
HTTP
GET /v1/scenes/arch
← { "sceneId": "arch", "revision": 3, "scene": { ... } }
POST /v1/scenes/arch/applyOps
→ { "schemaVersion": "0.1", "sceneId": "arch", "baseRevision": 3,
"ops": [
{ "op": "updateNode", "id": "api", "label": "API v2" },
{ "op": "moveElement", "id": "db", "delta": { "dx": 100, "dy": 0 } }
] }
← { "applied": true, "sceneId": "arch", "revision": 4, "diagnostics": [] }
POST /v1/scenes/arch/render
→ { "format": "svg" }
Key pattern: Always GET the latest revision before editing. Use updateNode and moveElement - don’t delete and recreate.
Example 3: Draft statelessly, then persist
Validate and preview without creating stored state.
HTTP
POST /v1/scenes/validate
→ { "schemaVersion": "0.1", "scene": { ... }, "elements": [ ... ] }
← { "ok": true, "diagnostics": [] }
POST /v1/scenes/normalize
→ { "schemaVersion": "0.1", "scene": { ... }, "elements": [ ... ] }
← { "scene": { ... }, "computed": { ... }, "diagnostics": [] }
POST /v1/scenes/render
→ { "scene": { ... }, "format": "svg" }
← { "output": { "mimeType": "image/svg+xml", "content": "<svg>...</svg>" } }
# Happy with the draft - now persist it
POST /v1/scenes
→ (the validated scene document)
← { "sceneId": "...", "revision": 1, "diagnostics": [] }
Key pattern: Use stateless endpoints for iteration. Persist only when ready.
Example 4: Validation failure and repair
Handle a 422 response by reading diagnostics and fixing the specific problem.
HTTP
POST /v1/scenes
→ { "schemaVersion": "0.1", "scene": { "id": "broken", ... },
"elements": [
{ "id": "dup", "kind": "node", ... },
{ "id": "dup", "kind": "node", ... }
] }
← 422 { "error": "Scene validation failed",
"diagnostics": [
{ "severity": "error", "code": "ELEMENT_ID_DUPLICATE",
"message": "Duplicate element ID: dup" }
] }
# Fix: change the second element's ID
POST /v1/scenes
→ { ... "elements": [
{ "id": "dup", "kind": "node", ... },
{ "id": "dup-2", "kind": "node", ... }
] }
← 200 { "sceneId": "broken", "revision": 1, "diagnostics": [] }
Key pattern: Read the diagnostic code and message. Fix only the failing element. Do not retry the same payload.
Example 5: Revision conflict recovery
Handle a 409 when another writer updated the scene.
HTTP
GET /v1/scenes/shared-diagram
← { "sceneId": "shared-diagram", "revision": 5, "scene": { ... } }
# Plan a patch based on revision 5
POST /v1/scenes/shared-diagram/applyOps
→ { "baseRevision": 5, "ops": [ ... ] }
← 409 { "error": "Revision conflict", ... }
# Re-fetch the latest
GET /v1/scenes/shared-diagram
← { "sceneId": "shared-diagram", "revision": 7, "scene": { ... } }
# Re-plan the patch against revision 7
POST /v1/scenes/shared-diagram/applyOps
→ { "baseRevision": 7, "ops": [ ... ] }
← 200 { "applied": true, "revision": 8, "diagnostics": [] }
Key pattern: On 409, re-fetch the scene, compare your intended change to the current state, build a fresh minimal patch, and retry with the new baseRevision. Do not blindly replay the same ops.
Example 6: Auto-layout (no manual positioning)
Create a scene with layoutStrategy and nodes without layout coordinates:
HTTP
POST /v1/scenes
→ { "schemaVersion": "0.1",
"layoutStrategy": { "algorithm": "hierarchical", "direction": "TB" },
"scene": { "id": "auto", "units": "px", "canvas": { "width": 800, "height": 600 } },
"elements": [
{ "id": "a", "kind": "node", "nodeType": "service", "shape": "roundedRect", "label": "API" },
{ "id": "b", "kind": "node", "nodeType": "database", "shape": "cylinder", "label": "DB" },
{ "id": "e1", "kind": "edge", "from": { "elementId": "a" }, "to": { "elementId": "b" } }
] }
← 200
POST /v1/scenes/auto/render
→ { "format": "svg" }
← SVG with nodes automatically positioned top-to-bottom
Key pattern: No x/y/width/height on nodes. The engine computes everything from the graph structure.
Example 7: Workflow diagram with BPMN rendering
Create a workflow scene with family-specific node types for BPMN-style rendering.
HTTP
POST /v1/scenes
→ { "schemaVersion": "0.1",
"diagramFamily": "workflow",
"layoutStrategy": { "algorithm": "hierarchical", "direction": "LR" },
"scene": { "id": "approval", "units": "px", "canvas": { "width": 800, "height": 400 } },
"elements": [
{ "id": "start", "kind": "node", "nodeType": "workflow.start", "shape": "ellipse", "label": "Start" },
{ "id": "review", "kind": "node", "nodeType": "workflow.task", "shape": "roundedRect", "label": "Review" },
{ "id": "decide", "kind": "node", "nodeType": "workflow.gateway", "shape": "diamond", "label": "Approved?" },
{ "id": "approve", "kind": "node", "nodeType": "workflow.task", "shape": "roundedRect", "label": "Approve" },
{ "id": "reject", "kind": "node", "nodeType": "workflow.task", "shape": "roundedRect", "label": "Reject" },
{ "id": "end", "kind": "node", "nodeType": "workflow.end", "shape": "ellipse", "label": "End" },
{ "id": "e1", "kind": "edge", "edgeType": "workflow.sequenceFlow", "from": { "elementId": "start" }, "to": { "elementId": "review" } },
{ "id": "e2", "kind": "edge", "edgeType": "workflow.sequenceFlow", "from": { "elementId": "review" }, "to": { "elementId": "decide" } },
{ "id": "e3", "kind": "edge", "edgeType": "workflow.sequenceFlow", "from": { "elementId": "decide" }, "to": { "elementId": "approve" }, "label": "yes" },
{ "id": "e4", "kind": "edge", "edgeType": "workflow.sequenceFlow", "from": { "elementId": "decide" }, "to": { "elementId": "reject" }, "label": "no" },
{ "id": "e5", "kind": "edge", "edgeType": "workflow.sequenceFlow", "from": { "elementId": "approve" }, "to": { "elementId": "end" } },
{ "id": "e6", "kind": "edge", "edgeType": "workflow.sequenceFlow", "from": { "elementId": "reject" }, "to": { "elementId": "end" } }
] }
← 200
POST /v1/scenes/approval/render
→ { "format": "svg", "theme": "dark" }
← SVG with BPMN-style shapes (filled start circle, gateway diamond with × marker)
Key pattern: Declare diagramFamily: "workflow" and use workflow.* node types. The renderer produces BPMN-style shapes automatically. Combine with layoutStrategy for zero manual positioning.
Decision diamond vs gateway: the example above uses workflow.gateway (BPMN exclusive gateway, renders with a x marker inside the diamond and the label outside). For a plain decision diamond without a BPMN marker, use workflow.decision instead - the label renders INSIDE the diamond per the broader flowchart convention (Mermaid, draw.io, Lucidchart, yEd, PlantUML). Pick workflow.decision when you want a generic “is X true?” diamond and the BPMN gateway semantics aren’t relevant; pick a workflow.gateway* type when they are. Both render at 64-128 px depending on label length - the engine sizes the diamond to fit the label inside its inscribed rectangle.
Edge type: every edge in this example uses edgeType: "workflow.sequenceFlow". That gets you the BPMN-correct filled-triangle arrowhead automatically, so a reader can follow flow direction at a glance. Plain edgeType: "default" renders an undirected line - fine for non-flow connections, wrong for sequential workflow steps.
Example 8: ER diagram with crow’s-foot notation
Create an entity-relationship diagram with cardinality markers.
HTTP
POST /v1/scenes
→ { "schemaVersion": "0.1",
"diagramFamily": "entityRelationship",
"layoutStrategy": { "algorithm": "hierarchical", "direction": "LR" },
"scene": { "id": "blog-er", "units": "px", "canvas": { "width": 600, "height": 300 } },
"elements": [
{ "id": "user", "kind": "node", "nodeType": "er.entity", "shape": "rect", "label": "User",
"extensions": { "columns": [
{ "name": "id", "type": "uuid", "pk": true },
{ "name": "email", "type": "text" } ] } },
{ "id": "post", "kind": "node", "nodeType": "er.entity", "shape": "rect", "label": "Post",
"extensions": { "columns": [
{ "name": "id", "type": "uuid", "pk": true },
{ "name": "user_id", "type": "uuid", "fk": true },
{ "name": "body", "type": "text" } ] } },
{ "id": "e1", "kind": "edge", "edgeType": "er.relationship",
"from": { "elementId": "post", "column": "user_id" },
"to": { "elementId": "user" } }
] }
← 200
POST /v1/scenes/blog-er/render
→ { "format": "svg" }
← SVG with crow's-foot cardinality markers (many on the FK side, one on the PK side) auto-inferred from column metadata
Key pattern: Set extensions.columns with pk / fk flags and let auto-layout + auto-inference do the rest. endpoint.column anchors the edge at the FK column row inside Post; the PK side omits column and falls back to the entity’s sole PK row automatically. Crow’s Foot glyphs (many for FK, one for PK) are inferred from the column metadata. To override the inferred glyphs, set cardinality explicitly on an endpoint - author-set values are always preserved.
Example 9: Sequence diagram with lifelines
Create a UML sequence diagram with time-ordered messages.
HTTP
POST /v1/scenes
→ { "schemaVersion": "0.1",
"diagramFamily": "sequence",
"scene": { "id": "auth-seq", "units": "px", "canvas": { "width": 600, "height": 400 } },
"elements": [
{ "id": "client", "kind": "node", "nodeType": "sequence.actor", "shape": "rect", "label": "Client",
"layout": { "mode": "absolute", "x": 50, "y": 30, "width": 100, "height": 350 } },
{ "id": "server", "kind": "node", "nodeType": "sequence.lifeline", "shape": "rect", "label": "Server",
"layout": { "mode": "absolute", "x": 250, "y": 30, "width": 100, "height": 350 } },
{ "id": "db", "kind": "node", "nodeType": "sequence.lifeline", "shape": "rect", "label": "DB",
"layout": { "mode": "absolute", "x": 450, "y": 30, "width": 100, "height": 350 } },
{ "id": "m1", "kind": "edge", "edgeType": "sequence.message", "from": { "elementId": "client" }, "to": { "elementId": "server" }, "label": "login()" },
{ "id": "m2", "kind": "edge", "edgeType": "sequence.message", "from": { "elementId": "server" }, "to": { "elementId": "db" }, "label": "query" },
{ "id": "n1", "kind": "node", "nodeType": "sequence.note", "shape": "rect",
"label": "cache miss",
"extensions": { "anchor": "db" } },
{ "id": "m3", "kind": "edge", "edgeType": "sequence.reply", "from": { "elementId": "db" }, "to": { "elementId": "server" }, "label": "result" },
{ "id": "m4", "kind": "edge", "edgeType": "sequence.reply", "from": { "elementId": "server" }, "to": { "elementId": "client" }, "label": "token" }
] }
← 200
POST /v1/scenes/auth-seq/render
→ { "format": "svg", "theme": "dark" }
← SVG with lifeline headers + dashed tails, time-ordered messages, the "cache miss" note rendered between m2 and m3 centred over the DB lifeline
Key pattern: Declare diagramFamily: "sequence". Messages are ordered vertically by their position in the elements array (first = topmost). Self-messages (from === to) render as U-shaped loops. Add sequence.note nodes between messages for UML annotations - extensions.anchor: "<id>" centres the note OVER that lifeline (PlantUML / Mermaid / UML 2.5 §17.4 convention), or extensions.anchor: ["<a>", "<b>"] spans across the named lifelines (also centred-over semantics); the anchor id can be any sequence.lifeline or sequence.actor.