---
title: "Read a version link (public)"
method: GET
path: "/v1/public/diagrams/{ref}"
tags: ["Published diagrams"]
---

# Read a version link (public)

`GET /v1/public/diagrams/{ref}`

## Request

- Base URL: `https://api.zindex.ai`
- URL: `https://api.zindex.ai/v1/public/diagrams/{ref}`
- Auth: none required

What the page at https://zindex.ai/d/{ref} shows, and with .svg or .png the image itself. No key needed. {ref} is a published diagram's ID (never its key) or a share link's token, with @{revision} for one version; without it, the latest. A published diagram's answer has its title, description, madePublicAt, scene and kept image; a share link's has only id, access, the version shown and its date, latest, and every version's number and date, never anything from the scene. Images are the ones kept when each version was saved. An exact version's image is served with Cache-Control: public, max-age=31536000, immutable; one that follows the latest, and every JSON answer, with public, max-age=60. A share link's answers carry private instead of public: the visitor's own browser may keep them, but no shared cache may, so a revoked link stops for new viewers. Images carry an ETag of the version's chain hash (its content hash while it has none); If-None-Match with it answers 304. A private, deleted or unknown diagram, and an unknown or revoked token, answer the same 404, so the answer doesn't reveal which exist. Making a diagram private stops its links by ID at once, revoking a share link stops that link, and deleting the diagram stops both; copies already fetched stay with whoever has them.

## Path parameters

- `ref` string, required

## Headers

- `If-None-Match` string

## Response `200`

The page answer, or the image

- object
  - `id` string, required
  - `access` 'public' | 'link', required: public: a published diagram by ID; link: a share link
  - `revision` integer, required: The version shown
  - `latest` integer, required: The diagram's latest version
  - `at` string, date-time, nullable, required: When the version shown was saved
  - `versions` object[], required: Every version's number and date, newest first
    - `revision` integer, required
    - `at` string, date-time, nullable, required
  - `title` string, nullable: Published diagrams only
  - `description` string, nullable: Published diagrams only
  - `madePublicAt` string, date-time, nullable: Published diagrams only
  - `scene` SceneDocument: A DSP scene document containing the scene definition, elements, and optional styles, constraints, palette, layoutStrategy, diagramFamily, and extensions.
    - `schemaVersion` '0.1', required
    - `scene` object, required: Scene metadata and canvas configuration. Canvas dimensions are a minimum per axis: when content exceeds the declared dimensions, the engine auto-extends the canvas to fit. The engine never silently clips. When the engine lays out every node (a new diagram whose nodes have no positions, autoLayout without elements, or a new layoutStrategy or diagramFamily, unless members of a pool, lane or fragment with its own layout, or of a frame in an org chart or network, keep their positions), it also auto-spreads the nodes around their centroid to fill a declared canvas larger than node spacing needs, and caps an axis declared more than 30% larger than the comfortable centred-content size at content x 1.4. This is the only path where the engine reduces the canvas below a declared dimension, and the size it picks is saved with the diagram. Supply absolute coordinates on any node of a new diagram to keep the declared size. After the first save nodes keep their positions, so a larger canvas set with updateScene adds room on the right and at the bottom; send autoLayout to spread the nodes into it. Neither growing nor tightening comes back as a warning.
      - `id` string
      - `title` string
      - `description` string
      - `brief` string: Context kept with the diagram for the next agent: what it shows, conventions to keep, decisions made. Versioned with the diagram.
      - `units` 'px'
      - `canvas` object: Canvas dimensions. Both width and height are a minimum: the engine grows past them to fit content. The engine may also tighten either axis BELOW declared when it lays out every node (a new diagram whose nodes have no positions, autoLayout without elements, or a new layoutStrategy or diagramFamily, unless members of a pool, lane or fragment with its own layout, or of a frame in an org chart or network, keep their positions) AND the declared dimension on that axis exceeds the comfortable centred-content size by more than 30% - to avoid rendering with excessive whitespace. The size it picks is saved with the diagram. Neither change comes back as a warning. width/height are optional - the canonical scene.schema.json marks them so, and the runtime computes content-derived dimensions when they are absent.
        - `width` number
        - `height` number
        - `background` string: CSS color value (hex, rgb, or rgba). When set to a dark color and no render `theme` is passed, the renderer auto-selects the dark theme so strokes / labels remain legible. Use `palette.background` to override the painted rect color without affecting theme selection.
    - `elements` Element[], required: Array of scene elements (nodes, edges, groups, frames, text, images, guides)
      - `id` string, required: Unique element identifier
      - `kind` 'node' | 'edge' | 'group' | 'frame' | 'text' | 'image' | 'guide', required: Element type
      - `nodeType` 'generic' | 'service' | 'database' | 'queue' | 'actor' | 'note' | 'decision' | 'document' | 'process' | 'container' | 'externalSystem' | 'storage' | 'objectStorage' | 'event' | 'iconNode' | 'workflow.start' | 'workflow.end' | 'workflow.task' | 'workflow.decision' | 'workflow.gateway' | 'workflow.gatewayParallel' | 'workflow.gatewayInclusive' | 'workflow.gatewayEvent' | 'workflow.subprocess' | 'workflow.timer' | 'workflow.error' | 'workflow.intermediateTimer' | 'workflow.intermediateMessage' | 'workflow.intermediateError' | 'workflow.intermediateThrowMessage' | 'workflow.terminate' | 'workflow.messageStart' | 'workflow.messageEnd' | 'er.entity' | 'er.weakEntity' | 'er.attribute' | 'er.relationship' | 'sequence.lifeline' | 'sequence.activation' | 'sequence.actor' | 'sequence.note' | 'network.server' | 'network.router' | 'network.switch' | 'network.firewall' | 'network.cloud' | 'network.client' | 'org.person' | 'org.role' | 'org.department' | 'uiflow.screen' | 'uiflow.modal' | 'uiflow.entry' | 'uiflow.exit': Semantic node type. Architecture-family types: generic, service, database, queue, actor, note, decision, document, process, container, externalSystem, storage, objectStorage, event, iconNode. Family-namespaced types (workflow.*, er.*, sequence.*, network.*, org.*, uiflow.*) require the corresponding diagramFamily on the scene. The same values as https://zindex.ai/schemas/scene.schema.json#/$defs/nodeType.
      - `edgeType` 'default' | 'workflow.sequenceFlow' | 'workflow.messageFlow' | 'er.relationship' | 'er.identifying' | 'sequence.message' | 'sequence.reply' | 'sequence.create' | 'sequence.async' | 'sequence.destroy' | 'network.dataFlow' | 'network.controlFlow' | 'org.directReport' | 'org.dottedLine' | 'uiflow.navigate' | 'uiflow.back' | 'uiflow.action': Family-specific edge type for typed connection semantics. The same values as edgeType in https://zindex.ai/schemas/scene.schema.json.
      - `shape` 'rect' | 'roundedRect' | 'ellipse' | 'diamond' | 'cylinder' | 'bucket' | 'pill' | 'hexagon' | 'parallelogram' | 'cloud' | 'box3d' | 'textBox' | 'iconBox': Visual shape for nodes
      - `label` string: Display label
      - `visible` boolean: false hides the element: it keeps its space but isn't drawn, nor any edge with an end at it. A hidden frame hides only its box and title, a hidden group only its title.
      - `layout` union: Position and size, as in scene.schema.json: mode absolute with x, y, width and height, or mode relative with a place clause such as { "rightOf": "api", "gap": 40 }. Leave it out and the engine places the node next to its neighbours, around the nodes that have a layout, without moving them; layoutStrategy on the scene picks how. After the first save the engine records the position here and keeps it, unless a node that grows needs the room and pushes this one, an edit adds or deletes a frame round it and keeping it would leave frames overlapping or a frame holding a node that isn't its member, or an added order constraint finds its two nodes the wrong way round; the autoLayout op, and a new or removed layoutStrategy or diagramFamily, place nodes again. Sequence diagrams are laid out from their messages, so their nodes stay at x 0, y 0. A layout at exactly x 0, y 0 counts as not placed.
        - AbsoluteLayout
          - `mode` 'absolute', required
          - `x` number, required
          - `y` number, required
          - `width` number, required
          - `height` number, required
          - `rotation` number: Kept with the layout. The reference engine doesn't rotate anything.
          - `autoSize` 'content' | 'none': 'content' sizes a node whose label wraps (textStyle.wrap 'word' or 'character') to its text: it keeps this width and gets at least the height its wrapped label and icon need, the height a TEXT_OVERFLOW warning gives, growing down from its top edge. The height here is the minimum. Nodes it grows into move down to make room, keeping the node spacing from it. No effect on a label that doesn't wrap (the engine already grows that node to fit it) or on nodes that lay out their own text: org chart people and roles, UI flow screens and modals, ER tables with columns, sequence nodes, BPMN events and gateways, UI flow entry and exit points.
        - RelativeLayout
          - `mode` 'relative', required
          - `place` union, required: Where to put the element: exactly one of rightOf, leftOf, above, below or inside, naming another element's ID.
            - object
              - …
            - object
              - …
            - object
              - …
            - object
              - …
            - object
              - …
          - `preferredSize` object
            - `width` number, required
            - `height` number, required
          - `rotation` number: Kept with the layout. The reference engine doesn't rotate anything.
      - `from` object: Edge source (for kind=edge). elementId may reference any element kind - typically a node, but frame and group IDs are also valid targets. An edge to a frame terminates at the frame's border using cardinal anchors; useful for abstract group dependencies (e.g. an edge to a 'Regional Services' sidebar rather than to a specific service inside). On entityRelationship diagrams, set `column` to anchor the edge at a specific column row inside the entity. Set `cardinality` for an explicit Crow's Foot glyph; when omitted, the engine infers it from the resolved column's pk/fk/unique/nullable metadata.
        - `elementId` string, required
        - `portId` string
        - `anchor` object: Optional explicit anchor point on the element's face. Mutually exclusive with portId.
        - `column` string: Optional column-row anchor for ER entities. Must match the `name` of a column declared in the endpoint entity's `extensions.columns` array. Mismatched names emit an EDGE_COLUMN_NOT_FOUND warning.
        - `cardinality` 'one' | 'many' | 'zero-or-one' | 'zero-or-many' | 'one-or-many': ER-style cardinality marker for this endpoint. When not set, inferred from the column the end anchors at each time the diagram is drawn, and not stored.
      - `to` object: Edge target (for kind=edge). elementId may reference any element kind - typically a node, but frame and group IDs are also valid targets. An edge to a frame terminates at the frame's border using cardinal anchors; useful for abstract group dependencies (e.g. an edge to a 'Regional Services' sidebar rather than to a specific service inside). On entityRelationship diagrams, set `column` to anchor the edge at a specific column row inside the entity. Set `cardinality` for an explicit Crow's Foot glyph; when omitted, the engine infers it from the resolved column's pk/fk/unique/nullable metadata.
        - `elementId` string, required
        - `portId` string
        - `anchor` object: Optional explicit anchor point on the element's face. Mutually exclusive with portId.
        - `column` string: Optional column-row anchor for ER entities. Must match the `name` of a column declared in the endpoint entity's `extensions.columns` array. Mismatched names emit an EDGE_COLUMN_NOT_FOUND warning.
        - `cardinality` 'one' | 'many' | 'zero-or-one' | 'zero-or-many' | 'one-or-many': ER-style cardinality marker for this endpoint. When not set, inferred from the column the end anchors at each time the diagram is drawn, and not stored.
      - `router` 'straight' | 'orthogonal' | 'curved' | 'polyline': Edge routing algorithm
      - `children` string[]: Child element IDs (for kind=group or kind=frame)
      - `title` string: A frame's or group's title
      - `containerType` 'generic' | 'pool' | 'lane' | 'subprocess' | 'partition' | 'package' | 'boundary' | 'fragment': Frames: what the frame stands for; generic when absent
      - `laneDirection` 'vertical' | 'horizontal': Pools: how the lanes are arranged; vertical (stacked top to bottom) when absent
      - `style` union: Element style: either a named-style string key (resolved against scene.styles) or an inline StyleValue object. textColor: when omitted, the rendering engine auto-computes a contrast-appropriate color based on the element's resolved fill (light text on dark fills, dark text on light fills). Edge labels, and labels drawn outside their shape (BPMN events and gateways), are checked against what is under them, and are made lighter or darker when they wouldn't read there, even when textColor is set.
        - string: Reference to a named style in scene.styles
        - object: Inline style overrides (fill, stroke, strokeWidth, dash, fontSize, fontFamily, fontWeight, opacity, textColor or its other name color, labelBackground, cornerRadius, accentColor, forceLabel). On ER edges, forceLabel: true draws a label that matches an endpoint column, which is otherwise left out; the edge still anchors at that column.
      - `icon` string: A node's icon: a family:id key such as lucide:database or aws:lambda, or an image URL. The same field as https://zindex.ai/schemas/scene.schema.json describes.
      - `textStyle` object: Text layout for a node's label or a text element: wrap (none, word, character), align, verticalAlign. The same fields as https://zindex.ai/schemas/scene.schema.json describes.
      - `extensions` object: Protocol extensions on an element. ER tables (er.entity, er.weakEntity) take extensions.columns, each column as https://zindex.ai/schemas/scene.schema.json#/$defs/erColumn describes: name, and optionally type, pk, fk, unique, nullable, key and comment.
    - `styles` object: Named style definitions that elements can reference
    - `constraints` object[]: Layout constraints. 10 types: align, distribute, sameSize, stack, centerIn, contain, grid, minGap, noOverlap, order. The order constraint enforces hard spatial relationships when the engine places nodes (e.g. source leftOf target, source above target, sameRank); one added with addConstraint places its two nodes again when they're out of order. The other types apply on every layout. A constraint's id is optional and unique when given.
    - `diagramFamily` 'architecture' | 'workflow' | 'entityRelationship' | 'sequence' | 'network' | 'orgchart' | 'uiflow': Diagram family declaration. Technically optional in the schema but effectively load-bearing: gates family-specific validation, layout, and rendering behaviour. Without it the diagram is drawn as a generic flowchart, and nothing warns you. Always declare on every scene.
    - `layoutStrategy` object: Optional: how the engine places nodes that have no layout. The engine picks its method from diagramFamily; the layered layout (architecture, workflow, entityRelationship, uiflow, and no family) reads these fields, and org charts (top to bottom), network and sequence diagrams don't. Changing it with updateScene places every node again. nodeSpacing and rankSpacing are the spacing the engine aims for, not minimums: laying out every node, it also tries tighter and wider spacings and keeps the layout that scores best.
      - `direction` 'TB' | 'BT' | 'LR' | 'RL': Layout direction
      - `nodeSpacing` number: Pixels the engine aims for between nodes in the same rank. Not a minimum: when it lays out every node it also tries tighter and wider spacings and keeps the layout that scores best, and it widens gaps to fill a larger canvas.
      - `rankSpacing` number: Pixels the engine aims for between adjacent ranks. Not a minimum: when it lays out every node it also tries tighter and wider spacings and keeps the layout that scores best, and it widens gaps to fill a larger canvas.
    - `palette` object: Scene-level color palette that overrides theme defaults for unstyled elements. Keys: background, foreground, nodeFill, nodeStroke, edgeStroke, containerFill, containerStroke, accentPrimary, accentSecondary, mutedForeground. Element explicit styles still win. Note: palette.background does NOT trigger the canvas-aware default-theme auto-selection; only scene.canvas.background does. Set canvas.background if you want a dark canvas to also flip the default theme to dark.
      - `background` string
      - `foreground` string
      - `nodeFill` string
      - `nodeStroke` string
      - `edgeStroke` string
      - `containerFill` string
      - `containerStroke` string
      - `accentPrimary` string: Accent color of nodes whose style sets no accentColor: ER table headers and PK badges, org chart card stripes, UI flow screen and modal title bars.
      - `accentSecondary` string: Nothing draws a second accent yet, so this has no effect.
      - `mutedForeground` string
    - `extensions` object: Reserved extension point for protocol additions. Recognised keys include `showLifelineFeet` (sequence diagrams only) and `autoGroup` (architecture diagrams only).
      - `showLifelineFeet` boolean: Sequence-diagram only. When true (default), the renderer draws a foot bar at the bottom of each lifeline mirroring the head (UML / PlantUML / Mermaid convention). Set to false to suppress foot bars - useful when the author has their own bottom row of shapes.
      - `autoGroup` 'byNodeType': Architecture-family only. When set, the engine synthesises subtle background bands grouping nodes by their `nodeType` (e.g. one band for all `service` nodes, one for `database` nodes, one for `externalSystem` nodes). Each band is labelled with the pluralised type name ("Services", "Databases", "External Systems"). Skipped when the scene already authors frames containing the candidate nodes (no double-wrap), when all nodes share a single `nodeType` (trivial), or when the nodes of a type are scattered (a node of another type sits between two of them in a column). Opt-in only; omit the field to render with no banding (existing default).
  - `svg` string: Published diagrams only: the version's kept image, with its address and date in the bottom-right corner (older versions: {id} | Rev N - {date}) and data-zindex-id, data-zindex-revision and data-zindex-hash on the root

## Other responses

- `304`: Images: the copy named by If-None-Match is current
- `404`: NOT_FOUND: no public diagram or share link with that ID
- `422`: DRAW_TIMEOUT: drawing a version that had no kept image, or its PNG, took longer than the drawing budget (5 s), or needed more memory than it allows
- `429`: RATE_LIMITED: over the request rate limit (see Rate Limiting). Wait retryAfter seconds (also the Retry-After header), then try again.
- `503`: BUSY: every drawing worker is busy and the queue is full. Wait retryAfter seconds (also the Retry-After header): half the budget of the drawing refused. Then try again.

---

[API](https://skmtc.dev/zindex/apis/zindex-api.md) · [All operations](https://skmtc.dev/zindex/apis/zindex-api/llms.txt) · [OpenAPI document](https://skmtc.dev/zindex/apis/zindex-api/revisions/1d21bd5bbf8e?raw)
