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.