Agent Workflow Examples
Each example is a sequence of calls to the Diagrams API, with the response each call returns, errors and recovery included. The MCP tools run the same code, so an agent on MCP takes the same steps with diagram_create, diagram_edit, diagram_validate and diagram_render.
Every call sends an API key as Authorization: Bearer $ZINDEX_API_KEY, except validate and preview, which need none. Examples 2 and 5 continue the diagram from Example 1. Your IDs, hashes and times will differ from the ones shown.
Building an agent integration? The Zindex skill covers the loop an agent follows: create, read, edit with
baseRevision, retry safely, re-plan after a conflict and render.
Example 1: Create a diagram, add elements, render
The basic path for a new diagram.
HTTP
Save an empty diagram under the key arch. Zindex mints its ID and returns revision 1:
curl -s https://api.zindex.ai/v1/diagrams \
-H "Authorization: Bearer $ZINDEX_API_KEY" -H "Content-Type: application/json" \
-d '{
"key": "arch",
"message": "First version",
"scene": {
"schemaVersion": "0.1",
"diagramFamily": "architecture",
"scene": { "title": "Architecture", "canvas": { "width": 800, "height": 400 } },
"elements": []
}
}'
{"id":"dgm_pH5ZSZpDr6CQ","key":"arch","revision":1,"hash":"sha256:cd289a…","warnings":[],"created":true}
Add two nodes. baseRevision names the revision you planned the edit against:
curl -s https://api.zindex.ai/v1/diagrams/arch/revisions \
-H "Authorization: Bearer $ZINDEX_API_KEY" -H "Content-Type: application/json" \
-d '{
"baseRevision": 1,
"message": "Add the gateway and the database",
"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 } }
]
}'
{"id":"dgm_pH5ZSZpDr6CQ","revision":2,"hash":"sha256:9bbd44…","warnings":[],"changed":{"added":["api","db"],"updated":[],"removed":[]}}
Connect them, planned against revision 2:
curl -s https://api.zindex.ai/v1/diagrams/arch/revisions \
-H "Authorization: Bearer $ZINDEX_API_KEY" -H "Content-Type: application/json" \
-d '{
"baseRevision": 2,
"message": "Connect the gateway to the database",
"ops": [
{ "op": "createEdge", "id": "e1", "from": { "elementId": "api" }, "to": { "elementId": "db" },
"router": "orthogonal", "label": "queries" }
]
}'
{"id":"dgm_pH5ZSZpDr6CQ","revision":3,"hash":"sha256:231332…","warnings":[],"changed":{"added":["e1"],"updated":[],"removed":[]}}
Ask for links to the latest version’s images:
curl -s https://api.zindex.ai/v1/diagrams/arch/render \
-H "Authorization: Bearer $ZINDEX_API_KEY"
{
"id": "dgm_pH5ZSZpDr6CQ",
"revision": 3,
"hash": "sha256:231332…",
"engine": "3.1.0+build.1220",
"svg": "https://api.zindex.ai/v1/images/[email protected]?e=1791849625&s=AkwA637RDSUzJG4jEerAsgfErY706lGd",
"png": "https://api.zindex.ai/v1/images/[email protected]?e=1791849625&s=6CxFaaWmxNlDFPy9ArhRqxcaYhOmAXgt",
"expires": "2026-10-13T00:00:25.000Z"
}
The links work without a key for 7 days, so you can hand them to a person. With your key, GET /v1/images/[email protected] returns the image itself.
MCP
diagram_create → key "arch", no elements (revision 1)
diagram_edit → baseRevision 1: createNode "api", createNode "db" (revision 2)
diagram_edit → baseRevision 2: createEdge "e1" from api to db (revision 3)
diagram_render → SVG and PNG links for revision 3
Key pattern: Nodes first, edges second, render last. Each edit names the revision you planned it against and makes the next revision.
Example 2: Edit an existing diagram
Read the latest state, plan a minimal change, apply it.
GET /v1/diagrams/arch returns the outline, whose first line has the current revision:
dgm_pH5ZSZpDr6CQ "Architecture" r3 2026-10-06T00:00Z sha256:231332a8fa3a
key: arch
recent: r3 curl/8.7.1 "Connect the gateway to the database" | r2 curl/8.7.1 "Add the gateway and the database" | r1 curl/8.7.1 "First version"
outline: 2 nodes, 1 edges
api service "API Gateway"
db database "PostgreSQL"
e1: api -> db "queries"
Plan the edit against revision 3:
curl -s https://api.zindex.ai/v1/diagrams/arch/revisions \
-H "Authorization: Bearer $ZINDEX_API_KEY" -H "Content-Type: application/json" \
-d '{
"baseRevision": 3,
"message": "Rename the gateway and move the database right",
"ops": [
{ "op": "updateNode", "id": "api", "label": "API v2" },
{ "op": "moveElement", "id": "db", "delta": { "dx": 100, "dy": 0 } }
]
}'
{"id":"dgm_pH5ZSZpDr6CQ","revision":4,"hash":"sha256:20d6a1…","warnings":[],"changed":{"added":[],"updated":["api","db"],"removed":[]}}
Download the new version’s image:
curl -s -o arch.svg "https://api.zindex.ai/v1/images/[email protected]" \
-H "Authorization: Bearer $ZINDEX_API_KEY"
Key pattern: Read the latest revision before editing and send it as baseRevision. Change elements in place with updateNode and moveElement; don’t delete and recreate them. On later reads, ?knownRevision=4 returns { "unchanged": true } or only what changed since revision 4.
Example 3: Draft without saving, then save
Validate and preview a draft without storing anything. Neither call needs a key.
curl -s https://api.zindex.ai/v1/diagrams/validate \
-H "Content-Type: application/json" \
-d '{
"scene": {
"schemaVersion": "0.1",
"diagramFamily": "architecture",
"layoutStrategy": { "algorithm": "hierarchical", "direction": "LR" },
"scene": { "title": "Checkout" },
"elements": [
{ "id": "web", "kind": "node", "nodeType": "service", "label": "Web" },
{ "id": "pay", "kind": "node", "nodeType": "service", "label": "Payments" },
{ "id": "e1", "kind": "edge", "from": { "elementId": "web" }, "to": { "elementId": "pay" }, "label": "charges" }
]
}
}'
{"ok":true,"errors":[],"warnings":[]}
No errors, and drawing the scene noticed nothing. Preview it next. The answer is the SVG itself; add "format": "png" for a PNG, or a theme such as "dark":
curl -s -o draft.svg https://api.zindex.ai/v1/diagrams/preview \
-H "Content-Type: application/json" \
-d '{
"scene": {
"schemaVersion": "0.1",
"diagramFamily": "architecture",
"layoutStrategy": { "algorithm": "hierarchical", "direction": "LR" },
"scene": { "title": "Checkout" },
"elements": [
{ "id": "web", "kind": "node", "nodeType": "service", "label": "Web" },
{ "id": "pay", "kind": "node", "nodeType": "service", "label": "Payments" },
{ "id": "e1", "kind": "edge", "from": { "elementId": "web" }, "to": { "elementId": "pay" }, "label": "charges" }
]
}
}'
When the draft looks right, save it:
curl -s https://api.zindex.ai/v1/diagrams \
-H "Authorization: Bearer $ZINDEX_API_KEY" -H "Content-Type: application/json" \
-d '{
"key": "checkout",
"message": "First version",
"scene": {
"schemaVersion": "0.1",
"diagramFamily": "architecture",
"layoutStrategy": { "algorithm": "hierarchical", "direction": "LR" },
"scene": { "title": "Checkout" },
"elements": [
{ "id": "web", "kind": "node", "nodeType": "service", "label": "Web" },
{ "id": "pay", "kind": "node", "nodeType": "service", "label": "Payments" },
{ "id": "e1", "kind": "edge", "from": { "elementId": "web" }, "to": { "elementId": "pay" }, "label": "charges" }
]
}
}'
{"id":"dgm_H4efQvsxAxCw","key":"checkout","revision":1,"hash":"sha256:b80ca4…","warnings":[],"created":true}
Key pattern: Iterate with validate and preview, which store nothing, and save once. Saving checks the scene again and returns the same warnings.
Example 4: Validation failure and repair
A scene with errors gets a 422, VALIDATION_FAILED, and Zindex saves nothing:
curl -s https://api.zindex.ai/v1/diagrams \
-H "Authorization: Bearer $ZINDEX_API_KEY" -H "Content-Type: application/json" \
-d '{
"key": "inventory",
"scene": {
"schemaVersion": "0.1",
"diagramFamily": "architecture",
"layoutStrategy": { "algorithm": "hierarchical", "direction": "LR" },
"scene": { "title": "Inventory" },
"elements": [
{ "id": "stock", "kind": "node", "nodeType": "service", "label": "Stock" },
{ "id": "stock", "kind": "node", "nodeType": "database", "label": "Stock DB" },
{ "id": "e1", "kind": "edge", "from": { "elementId": "stock" }, "to": { "elementId": "stock-db" } }
]
}
}'
{
"error": "The scene has errors; nothing was saved.",
"code": "VALIDATION_FAILED",
"errors": [
{ "code": "ELEMENT_ID_DUPLICATE", "fix": "Duplicate element ID: 'stock'." },
{ "code": "EDGE_ENDPOINT_ELEMENT_NOT_FOUND", "element": "e1", "fix": "Edge 'e1' to-endpoint references element 'stock-db' which does not exist." }
]
}
The second node reuses the ID stock, and the edge points at stock-db, which doesn’t exist. Giving the second node the ID stock-db fixes both:
curl -s https://api.zindex.ai/v1/diagrams \
-H "Authorization: Bearer $ZINDEX_API_KEY" -H "Content-Type: application/json" \
-d '{
"key": "inventory",
"scene": {
"schemaVersion": "0.1",
"diagramFamily": "architecture",
"layoutStrategy": { "algorithm": "hierarchical", "direction": "LR" },
"scene": { "title": "Inventory" },
"elements": [
{ "id": "stock", "kind": "node", "nodeType": "service", "label": "Stock" },
{ "id": "stock-db", "kind": "node", "nodeType": "database", "label": "Stock DB" },
{ "id": "e1", "kind": "edge", "from": { "elementId": "stock" }, "to": { "elementId": "stock-db" } }
]
}
}'
{"id":"dgm_b9aZaoEN9vuw","key":"inventory","revision":1,"hash":"sha256:742a0d…","warnings":[],"created":true}
Key pattern: Each error has a code, a one-line fix and, when the problem belongs to one element, its ID as element. Change only what the errors name; the same body sent again fails the same way. Validation rules lists every code, and POST /v1/diagrams/validate returns the same errors without saving.
Example 5: Revision conflict recovery
Handle a 409 when another writer changed the diagram after you read it.
You read arch at revision 4 and plan an edit. Before you send it, another agent saves revision 5:
curl -s https://api.zindex.ai/v1/diagrams/arch/revisions \
-H "Authorization: Bearer $ZINDEX_API_KEY" -H "Content-Type: application/json" \
-d '{
"baseRevision": 4,
"message": "Add a read replica",
"ops": [
{ "op": "createNode", "id": "replica", "nodeType": "database", "shape": "cylinder", "label": "Replica",
"layout": { "mode": "absolute", "x": 650, "y": 90, "width": 120, "height": 90 } },
{ "op": "createEdge", "id": "e3", "from": { "elementId": "db" }, "to": { "elementId": "replica" }, "label": "replicates" }
]
}'
{"id":"dgm_pH5ZSZpDr6CQ","revision":5,"hash":"sha256:1cc6ea…","warnings":[],"changed":{"added":["replica","e3"],"updated":[],"removed":[]}}
Your edit, planned against revision 4, gets a 409, REVISION_CONFLICT, and changes nothing:
curl -s https://api.zindex.ai/v1/diagrams/arch/revisions \
-H "Authorization: Bearer $ZINDEX_API_KEY" -H "Content-Type: application/json" \
-d '{
"baseRevision": 4,
"message": "Add the job queue",
"ops": [
{ "op": "createNode", "id": "queue", "nodeType": "queue", "label": "Jobs",
"layout": { "mode": "absolute", "x": 50, "y": 260, "width": 180, "height": 60 } },
{ "op": "createEdge", "id": "e2", "from": { "elementId": "api" }, "to": { "elementId": "queue" }, "label": "enqueues" }
]
}'
{
"error": "The diagram is at revision 5, not 4. Re-plan the edit against revision 5.",
"code": "REVISION_CONFLICT",
"revision": 5,
"changes": "dgm_pH5ZSZpDr6CQ \"Architecture\" r5 2026-10-06T00:00Z sha256:1cc6ea6d7840\nkey: arch\nsince r4: r5 curl/8.7.1 \"Add a read replica\"\n+ replica database \"Replica\"\n+ e3: db -> replica \"replicates\""
}
changes lists what changed since your base, one line per element. The other agent added replica and the edge e3, and neither touches what you’re changing, so send the edit again against revision 5:
curl -s https://api.zindex.ai/v1/diagrams/arch/revisions \
-H "Authorization: Bearer $ZINDEX_API_KEY" -H "Content-Type: application/json" \
-d '{
"baseRevision": 5,
"message": "Add the job queue",
"ops": [
{ "op": "createNode", "id": "queue", "nodeType": "queue", "label": "Jobs",
"layout": { "mode": "absolute", "x": 50, "y": 260, "width": 180, "height": 60 } },
{ "op": "createEdge", "id": "e2", "from": { "elementId": "api" }, "to": { "elementId": "queue" }, "label": "enqueues" }
]
}'
{"id":"dgm_pH5ZSZpDr6CQ","revision":6,"hash":"sha256:a51ab8…","warnings":[],"changed":{"added":["queue","e2"],"updated":[],"removed":[]}}
Key pattern: On a 409, read changes and check your edit against them. If they touch the elements you’re changing, read the diagram again and re-plan. Then send the edit with the new revision as baseRevision. Don’t resend the same ops without looking.
Example 6: Auto-layout without coordinates
Set layoutStrategy at the document root and leave layout off the nodes:
curl -s https://api.zindex.ai/v1/diagrams \
-H "Authorization: Bearer $ZINDEX_API_KEY" -H "Content-Type: application/json" \
-d '{
"key": "auto",
"scene": {
"schemaVersion": "0.1",
"diagramFamily": "architecture",
"layoutStrategy": { "algorithm": "hierarchical", "direction": "TB" },
"scene": { "title": "Auto layout" },
"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" } }
]
}
}'
{"id":"dgm_J3xfBsMhetHF","key":"auto","revision":1,"hash":"sha256:ad1932…","warnings":[],"created":true}
The image has the nodes top to bottom:
curl -s -o auto.svg "https://api.zindex.ai/v1/images/[email protected]" \
-H "Authorization: Bearer $ZINDEX_API_KEY"
Key pattern: No x, y, width or height on nodes. The engine places the nodes, routes the edges, places the labels and sizes frames around their children. It doesn’t group nodes into regions by meaning, such as frontend on the left and backend on the right; use order constraints or explicit coordinates for that. See Layout Engine.
Example 7: Workflow diagram with BPMN rendering
Create a workflow with family-specific node types for BPMN-style rendering.
curl -s https://api.zindex.ai/v1/diagrams \
-H "Authorization: Bearer $ZINDEX_API_KEY" -H "Content-Type: application/json" \
-d '{
"key": "approval",
"scene": {
"schemaVersion": "0.1",
"diagramFamily": "workflow",
"layoutStrategy": { "algorithm": "hierarchical", "direction": "LR" },
"scene": { "title": "Approval" },
"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" } }
]
}
}'
{"id":"dgm_Asb3LDAksRAA","key":"approval","revision":1,"hash":"sha256:6905fa…","warnings":[],"created":true}
The image has BPMN shapes, such as a filled start circle and a gateway diamond with an × marker:
curl -s -o approval.svg "https://api.zindex.ai/v1/images/[email protected]" \
-H "Authorization: Bearer $ZINDEX_API_KEY"
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.
curl -s https://api.zindex.ai/v1/diagrams \
-H "Authorization: Bearer $ZINDEX_API_KEY" -H "Content-Type: application/json" \
-d '{
"key": "blog-er",
"scene": {
"schemaVersion": "0.1",
"diagramFamily": "entityRelationship",
"layoutStrategy": { "algorithm": "hierarchical", "direction": "LR" },
"scene": { "title": "Blog" },
"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" } }
]
}
}'
{"id":"dgm_iUvbWwCmrX2B","key":"blog-er","revision":1,"hash":"sha256:2cce12…","warnings":[],"created":true}
The scene sends no cardinality: the engine infers it from the column flags, so the image has a many glyph at post and a one glyph at user, and there’s no warning:
curl -s -o blog-er.svg "https://api.zindex.ai/v1/images/[email protected]" \
-H "Authorization: Bearer $ZINDEX_API_KEY"
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. FAMILY_ER_MISSING_CARDINALITY names an end with neither a cardinality nor a pk or fk column to infer it from.
Example 9: Sequence diagram with lifelines
Create a UML sequence diagram with time-ordered messages.
curl -s https://api.zindex.ai/v1/diagrams \
-H "Authorization: Bearer $ZINDEX_API_KEY" -H "Content-Type: application/json" \
-d '{
"key": "auth-seq",
"scene": {
"schemaVersion": "0.1",
"diagramFamily": "sequence",
"scene": { "title": "Login", "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" }
]
}
}'
{"id":"dgm_nagsHwVr6U2Z","key":"auth-seq","revision":1,"hash":"sha256:6e6c48…","warnings":[],"created":true}
The image has lifeline headers with dashed tails, the messages in order, and the “cache miss” note between m2 and m3, centred over the DB lifeline:
curl -s -o auth-seq.svg "https://api.zindex.ai/v1/images/[email protected]" \
-H "Authorization: Bearer $ZINDEX_API_KEY"
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.