Skip to main content
Documentation menu

Developer platform

Diagram DSL

The content model the API, CLI, and MCP all speak. Describe a diagram as a node and edge graph, and let Excaliwow lay it out for you.

There are two shapes. A DiagramSpec is a node and edge graph that auto-lays-out, used when you create a diagram. An EditFragment is a small additive patch, used when you change an existing one.

  • Create with a DiagramSpec: the API POST /diagrams with a spec field, the CLI --spec flag, or the MCP generate_diagram tool.
  • Edit with an EditFragment: the API POST /:id/edit, the CLI --fragment flag, or the MCP edit_diagram tool.

See the REST API, the CLI, and the MCP server for how each surface accepts these payloads.

DiagramSpec

A DiagramSpec is a list of nodes, an optional list of edges, and an optional layout. Excaliwow positions everything for you.

DiagramSpec
{
  "version": "1",
  "nodes": [ /* required, at least 1 */ ],
  "edges": [ /* optional */ ],
  "layout": { "direction": "TB" }
}
  • version is optional. Only "1" is accepted. Anything else returns UNSUPPORTED_VERSION.
  • nodes is required and must contain at least one node.
  • edges is optional. It accepts the object form or the shorthand string form described below.
  • layout is optional and tunes direction and spacing.

Nodes

Each node needs a unique id. Everything else is optional.

NodeSpec
{
  "id": "client",
  "label": "Client",
  "shape": "rectangle",
  "width": 140,
  "height": 60,
  "strokeColor": "#1e1e1e",
  "backgroundColor": "transparent",
  "fillStyle": "solid"
}
FieldTypeDescription
id*stringUnique within the diagram. Avoid ids ending in -label or containing __, which collide with generated ids and raise ID_COLLISION.
labelstring?Defaults to id. An empty string suppresses the label entirely.
shapeenum?rectangle, ellipse, diamond, or text. Defaults to rectangle.
width / heightnumber?Positive. Treated as a minimum, so a shape grows to fit its label.
strokeColorstring?Any CSS color.
backgroundColorstring?Any CSS color, or transparent.
fillStyleenum?hachure, cross-hatch, solid, or zigzag.

When you omit width and height, default sizes are a rectangle at 140x60, an ellipse at 140x80, and a diamond at 160x90. A text node is sized to its text.

Edges

An edge connects two nodes by id. There are two ways to write one.

Object form

Edge
{ "from": "client", "to": "api", "label": "request", "arrowhead": "arrow" }
FieldTypeDescription
from / to*stringEach must name an existing node id, otherwise the edge is rejected with DANGLING_EDGE.
labelstring?Optional text rendered on the edge.
arrowheadenum?arrow, triangle, or null for no head. Defaults to arrow.

Shorthand string

DiagramSpec edges also accept a compact string. Everything after the first colon becomes the label. This form is not available in an EditFragment.

Shorthand
"client -> api"
"client -> api: request"

A missing or empty endpoint, or anything other than a single ->, returns MALFORMED_SHORTHAND.

Layout

Layout sets the flow direction and the spacing between nodes.

Layout
{ "direction": "TB", "nodeSep": 50, "rankSep": 80 }
  • direction is one of TB (top to bottom), BT, LR, or RL. Defaults to TB.
  • nodeSep (default 50) and rankSep (default 80) are optional positive numbers controlling node spacing.

EditFragment

An edit additively merges a fragment into an existing diagram. Existing elements are never moved, resized, or deleted, so an edit can only add new content or restyle nodes you already have.

EditFragment
{
  "addNodes":    [ /* NodeSpec, plus optional absolute x and y */ ],
  "addEdges":    [ /* Edge, object form only, no shorthand */ ],
  "updateNodes": [ /* { "id": "..." } plus restyle fields only */ ]
}
  • addNodes takes the same fields as a NodeSpec. New ids must not collide with any existing element id, otherwise the edit returns ID_COLLISION. You may set an explicit x and y (both or neither). Otherwise the node is auto-placed below the existing diagram.
  • addEdges accepts the object form only. from and to resolve against existing nodes plus any you add in the same fragment.
  • updateNodes patches existing nodes by id. Only the restyle fields can change: strokeColor, backgroundColor, fillStyle, and label.

Geometry fields are rejected on update

Any field that would move or resize a node, such as x, y, width, height, or shape, is rejected with INVALID_SPEC in updateNodes. This keeps an already-placed node from drifting into a neighbor.

Worked example

A small but realistic DiagramSpec: a client calling an API server that reads from a database and a cache.

DiagramSpec
{
  "version": "1",
  "nodes": [
    { "id": "client", "label": "Client", "shape": "rectangle" },
    { "id": "api", "label": "API Server", "shape": "rectangle" },
    { "id": "db", "label": "Database", "shape": "diamond" },
    { "id": "cache", "label": "Cache", "shape": "ellipse" }
  ],
  "edges": [
    { "from": "client", "to": "api", "label": "request" },
    { "from": "api", "to": "db" },
    { "from": "api", "to": "cache" }
  ],
  "layout": { "direction": "TB" }
}

Caps

A spec that exceeds a cap is rejected before it renders.

LimitMaximumDescription
nodes1000Nodes per diagram.
edges2000Edges per diagram.
label length2000Characters per label.
total scene elements5000Elements in the rendered scene (nodes, edges, and labels combined).

Error codes

When a spec or fragment is invalid, the response carries one of these codes, surfaced as code: message.

  • INVALID_SPEC
  • UNSUPPORTED_VERSION
  • MISSING_NODE_ID
  • DUPLICATE_NODE_ID
  • UNKNOWN_SHAPE
  • DANGLING_EDGE
  • MALFORMED_SHORTHAND
  • MALFORMED_EDGE
  • ID_COLLISION
  • SPEC_TOO_LARGE
  • SCENE_TOO_LARGE
  • PROTO_KEY

For how these codes arrive in an HTTP response, see the REST API reference.