changelog

New features, improvements, and fixes to the Zindex platform.

new

Self-serve billing through Stripe

The /settings/billing page now drives the full subscription lifecycle through Stripe: pick a paid plan to launch Checkout, return through the Stripe customer portal to update payment methods or download invoices, and cancel through a cancel-at-period-end flow with a one-click /v1/billing/resume to undo before the period ends. Stale Stripe references (after a test-to-live cutover or operator-side delete) now recover automatically by clearing the link and returning 409 with a re-checkout prompt instead of a server error.

new

Plan-aware rate limits and per-workspace usage

Authenticated requests are now rate-limited per plan rather than per global default, and /v1/billing/me returns the workspace's monthly diagram-update and render counts alongside plan limits. The dashboard surfaces both as "of N included" / "unlimited" caps so agents and humans can see headroom without inspecting headers.

new

Account deletion closes the loop end-to-end

Delete account from /settings/account: typed-name modal, email confirmation, double-opt-in, and on confirm the flow cancels the Stripe subscription, deletes the Stripe customer record, revokes every API key, invalidates every active session, hard-deletes the user and workspace rows along with every scene and revision, and purges every analytics table that referenced the user (render_events, request_logs, plan_changes). The email address is released for re-signup immediately on confirm.

new

Scene soft-delete with 24-hour recovery

DELETE /v1/scenes/:id soft-deletes a scene with an explicit confirm: true gate; POST /v1/scenes/:id/undelete restores it within a 24-hour grace window. New MCP tools dsp_delete_scene, dsp_undelete_scene, and dsp_list_recently_deleted cover the agent path, and the new /dashboard/recently-deleted page shows deletion time, expiry countdown, and a per-row Restore button for the human path.

new

MCP server runs in anonymous mode without an API key

The @zindex-ai/mcp package no longer hard-fails when ZINDEX_API_KEY is missing. It boots in anonymous mode and registers the public-endpoint tools (dsp_validate_scene, dsp_normalize_scene, dsp_render_scene) so agents can demo the engine or render one-off diagrams before the user creates a key. dsp_render_scene is now polymorphic: pass scene for inline rendering in either mode, or sceneId for persisted rendering in authenticated mode. Agents call tools/list at connect time to know which mode they are in.

new

Email verification gates write API access

New signups land on /verify-email with a 6-digit OTP. Unverified accounts can read but not write: POST /v1/scenes, applyOps, render against a persisted scene, DELETE, and undelete all return 403 email_not_verified with a verifyUrl the agent can surface to the user. Public endpoints (/validate, /render inline, /normalize) and every read path are never gated. Unverified accounts older than 7 days are purged automatically so the email can be reused.

new

Privacy policy commits to no AI subprocessors

New /privacy and /terms pages publish four headline commitments up front: scene content is never used to train models, the subprocessor list names zero LLM providers, no Zindex employee reads scene content unless explicitly authorised, and customer data is removed within 30 days of account deletion. Signup now requires an explicit consent checkbox to satisfy the GDPR "freely given and unambiguous" standard, and the footer carries persistent Privacy and Terms links on every public page.

new

Diagnostics catch contradictory layout settings and off-centre content

Three new normalizer diagnostics help agents self-correct. LAYOUT_STRATEGY_OVERRIDDEN fires when a scene declares layoutStrategy but every node also pins layout.mode: "absolute", signalling the strategy is dead code. LAYOUT_ABSOLUTE_AT_ORIGIN now reports layoutStrategyPresent so the agent knows which authoring pattern to drop. CANVAS_CONTENT_OFFSET fires when the content's bounding-box centre deviates more than 10% from the canvas centre on either axis, naming the offset and three recovery options.

new

Per-route agent payloads for signup, login, and pricing

Agents requesting /signup, /login, or /pricing with Accept: text/markdown now receive route-specific guidance that leads with the anonymous-mode HTTP recipe before any signup framing. /signup explains that anonymous mode needs no signup and reframes signup as the upgrade path to persistence; /login tells agents to ask the user for an existing key rather than POSTing synthetic credentials; /pricing names anonymous mode as the free, no-signup path.

improved

Edges keep a 16-pixel clearance from unrelated nodes

The orthogonal router now enforces a unified 16 px clearance against third-party obstacles at every stage (visibility graph, L-path gate, channel router, funnel spreads, final audit pass) and emits a new EDGE_OBSTACLE_GRAZED info diagnostic when a route still grazes a neighbour after the full fallback cascade. Edges that previously ran a hairline from an unrelated shape's outline now pick a clean alternative path; when no clean route exists, the agent receives a code and recovery options instead of a silent quality regression. Matches the 16 px convention used by mainstream orthogonal routers.

improved

Parallel-CRUD edges keep their distinct labels

Fan-in label suppression used to drop N-1 labels whenever multiple edges shared the same target and label string, even when the source nodes were genuinely independent flows (three POST handlers writing INSERT to one database). A new source-unity heuristic only collapses labels when the sources share a common parent, a single predecessor, or the same node; otherwise the geometry still merges into one trunk but every label and arrowhead renders, with the new EDGE_LABEL_FANIN_BUNDLED info diagnostic surfacing the bundle-only path.

improved

Transactional emails ship from one sender with one template

Verification codes, account-deletion confirmations, internal support notices, and contact-form acknowledgements now all render from a single shared HTML template and ship from Zindex <support@zindex.ai>. The shared composeList helper renders bullet lists with explicit per-li typography so Gmail and Outlook render lists in the same font as the surrounding paragraphs. A new sender-address resolver guarantees the Zindex display name appears on every From header in every recipient's inbox.

improved

Authenticated pages persist navigation and load without a full-page gate

The dashboard, settings, and admin surfaces no longer collapse to a centred "Loading..." text on every navigation. The top nav persists across View Transitions, auth state caches once per session and dispatches a zindex:auth-resolved event for late-mounting pages, and dynamic fields use pixel-sized skeleton placeholders that swap into real content without layout shift. The /settings/* surface splits user configuration (account, billing) from the dashboard's operational view (overview, API keys, recently deleted).

fixed

Monthly usage display recovers from a silent driver error

The dashboard and billing usage panels were rendering zeros because the Postgres driver rejected JS Date values passed to a raw SQL template and the billing router caught the throw silently. The query now passes the ISO strings directly and casts them on the SQL side; a new regression test exercises the round-trip through the real driver. Historical usage rendered correctly on the next request after deploy. No data was lost.

fixed

Recently-deleted endpoint resolves under the literal path

GET /v1/scenes/_recently-deleted was being captured by the parameterised GET /v1/scenes/:id route and returning a 404 "Scene not found" instead of the recoverable-scenes list. The literal path now registers ahead of the parameterised one, and a regression test pins the route order so the precedence cannot drift.

fixed

MCP server starts on macOS through a symlinked entry point

npx @zindex-ai/mcp on macOS was launching the binary through /tmp's symlink to /private/tmp, causing the entry-point detection to compare the raw and resolved paths, conclude the file wasn't the main module, and exit without booting the server. The detection now resolves both sides through fs.realpath. A new tarball-install regression test on macOS catches the same class of bug before publish.

breaking

API key required only for persistence; public endpoints accept unauthenticated calls

/v1/scenes/validate, /v1/scenes/render (inline-scene form), and /v1/scenes/normalize now accept requests without an Authorization header and apply a per-IP anonymous quota. Authenticated requests continue to use the per-plan rate limit. The MCP server's tool list reflects the mode: anonymous mode registers only the public-endpoint tools, authenticated mode adds the full persisted-scene tool set. Agents that previously sent placeholder bearer tokens should drop them so the anonymous quota applies cleanly.

new

Sequence diagram: Native rendering for the sequence family

Sequence diagram: the sequence family is now first-class. Lifelines and actors auto-size to fit their head labels (including multi-line labels via per-line tspan), foot bars render by default to close the diagram, lane pitch is computed per-pair so self-message arcs grow to fit their label and clear the source lifeline, self-message U-shapes render with rounded interior corners, the lifeline header band is treated as a label-placer obstacle so first-message labels never dip into the header zone, and async messages emit an open arrowhead. The new sequence.note element anchors over a single lifeline or spans a range, and containerType: "fragment" with extensions.operands renders alt-fragment branch dividers with their guard text protected from label collisions.

new

Examples library launches at /examples

The new /examples library showcases agent-native diagram workflows with before/after revision diffs, CI/CD recipes, and curated agent artifacts across every diagram family. The first sequence-diagram showcase walks through an HTTP /checkout handler request lifecycle (eight lifelines, twelve messages, an alt fragment around payment retry, and a fire-and-forget queue publish) and shows the diff a reviewer sees when an agent maintains the diagram alongside the handler.

new

ER diagram: Entities auto-size to fit their declared columns

ER diagram: compartmented entities (er.entity and er.weakEntity with extensions.columns) grow to fit the longest column row and full row count whenever the author-declared layout.width or layout.height is smaller than the intrinsic content size. Declared dimensions act as a floor, not a cap, so a default 150x60 layout still renders correctly when columns carry long names. Mirrors the canvas auto-extension semantics.

new

Diagnostics guide agents to declare diagramFamily and avoid origin-stacked layouts

Two new normalizer diagnostics help agents self-correct before the engine reaches layout. MISSING_DIAGRAM_FAMILY emits when a scene omits the top-level diagramFamily field and lists the seven valid values in data.allowedValues. LAYOUT_ABSOLUTE_AT_ORIGIN emits when a scene declares layoutStrategy: "absolute" but every node sits at (0, 0), the signature of an agent that meant to omit coordinates and rely on auto-layout.

new

State machine: Transition labels styled with EVENT [guard] / action typography

State-machine and BPMN transition labels of the form EVENT [guard] / action now render with three distinct typographic runs inside one text element: the event in the regular edge weight, the guard at smaller size and muted colour, and the action italicised. The eye locks onto the event name first while the guard and action stay legible without dominating the canvas. Detection is content-based and family-agnostic; plain labels keep their existing single-style rendering.

new

Architecture: External-system nodes get a distinct visual treatment

Architecture: nodes declared with nodeType: "externalSystem" now render with a dashed outline and muted fill so third-party services read as clearly outside the system boundary. Internal services keep their solid fill and crisp outline, giving readers an immediate visual separation between owned and external components.

new

Diagnostic accepts nested document-root fields

The normalizer now accepts scenes where diagramFamily, layoutStrategy, or other document-root fields appear nested inside document or scene, hoists them to the correct location, and emits an OUTER_FIELD_NESTED diagnostic so the agent learns the canonical shape. A common authoring slip that previously broke layout selection now produces a working diagram with a guidance code attached.

improved

Parallel edges with distinct labels fan out across the bundle face

When two or more edges connect the same source-target pair across one rank gap and at least one carries a label, the layered planner now inflates the bundle endpoints by the cumulative label-stack height so the existing funnel passes can redistribute anchors across the inflated face. Labels naturally place at distinct midpoint positions instead of clustering at one anchor, and the placer succeeds without further intervention.

improved

Label placer picks minimum-overlap candidate when no clean position exists

The label placer previously fell through to the polyline midpoint whenever every candidate position overlapped an obstacle, silently bypassing the spatial index and dumping labels on top of nodes, dividers, and other labels. It now tracks the best clean candidate and the minimum-overlap candidate in parallel, returning the clean one when available and otherwise the position that grazes the smallest obstacle area. Thin features (fragment dividers, header bands) are preferred over thick ones (node bodies), matching how a human would resolve the conflict.

improved

ER diagram: Leads the playground examples

ER diagram: the playground opens on the ER tab so column-row anchored edges, Crow's Foot auto-inference, PK fallback routing, and entity auto-sizing are visible on first paint. Architecture moves to the second slot.

improved

State machine: Diagnostic dedup, fan-in label collapse, orthogonal back-edge U-hook

State-machine diagrams pick up three quality improvements in one pass: duplicate diagnostic codes are deduped before they reach the agent, fan-in labels at a shared target collapse onto a single anchor row instead of stacking, and back-edges that cannot route cleanly drop into the canvas whitespace below all nodes as an obstacle-aware orthogonal U-hook. The U-hook keeps loopbacks clear of unrelated shapes instead of arcing through them, matching how mainstream BPMN and state modellers draw retry edges.

improved

Canvas bottom-padding aligned to industry convention

The canvas now reserves 60 px below the bottom-most rendered element instead of 40 px. Sequence foot bars, ER row bottoms, and workflow terminator events sit comfortably inside the canvas instead of crowding the lower edge. The constant is shared between the centering pass and final canvas sizing so the two stay in sync.

improved

Multi-rank fan-out trunks align and shared-label fan-ins merge

When a single source spawns several edges that travel across more than one rank before splitting, the router now aligns their initial trunk segments so the group reads as one bus rather than a frayed broom. Conversely, edges that share a label and converge on a single target merge into one trunk before terminating, giving hub patterns and shared-event fan-ins the visual coherence they had on simple source-target pairs.

improved

Canvas size adapts to layout aspect ratio

The canvas-tightening pass now respects the natural aspect ratio of the laid-out content instead of producing fixed-shape backgrounds. Tall state machines no longer waste horizontal space and wide workflows no longer truncate; the canvas snaps to the shape the content wants. A single source of truth drives both the centering pass and the final canvas dimensions so the two never diverge.

improved

Frame and lane titles use muted typography with knockout backgrounds

Generic frames and lane headers now render with smaller, muted title typography so the frame reads as ambient context rather than competing with its contained nodes. The title text knocks out the frame border behind it so the label is never bisected by an outline stroke. Applied uniformly across themed and no-theme renders.

improved

Generic edges default to a target-end triangle arrowhead

Edges authored without an explicit style.arrowhead now render with a filled target-end triangle by default. Agents that omit the field get the direction-conveying arrowhead readers expect on flow diagrams, while agents that set arrowhead: "none" or pick a different style keep full control.

improved

Node labels gain breathing room and parallel-bundle labels stagger along the path

Default node label padding moved from 12 px to 14 px so text sits with visible margin inside the shape rather than crowding the outline. The label placer now treats sibling edges as obstacles and staggers parallel-bundle labels along the path so two-edge bundles that share a midpoint no longer drop both labels on the same coordinate.

improved

UI flow: Card anchors skip the title-bar seam and contrast meets WCAG AA

UI flow: card screens narrow the funnel anchor span on east and west faces so edges no longer exit through the title-bar seam, and the polish bundle lifts text and outline contrast to WCAG AA across the family. Default arrowheads, shape-core targeting, and title font alignment land in the same pass so authored UI flows render with consistent typography and clearly-anchored connections.

fixed

Asymmetric layouts no longer pin against the bottom canvas edge

The canvas-centering pass can now shift content upward when the planner placed it low. Previously a defensive clamp refused negative shifts, so asymmetric workflows and state machines (where one branch fans further than the others) ended up with a huge top margin and almost no bottom margin. With the clamp removed, an 18-state workflow that rendered with an 824 px top margin and a 207 px bottom margin in a 1600 px canvas now renders centred at 515 px on each side.

fixed

Workflow: Decision diamonds render labels inside the shape

Workflow: decision diamonds authored as workflow.decision place their label inside the shape, matching generic flowchart convention. Previously the label floated below the diamond because the renderer grouped workflow.decision with the BPMN-spec gateway types, which intentionally reserve label space outside the shape for their marker glyphs. The four workflow.gateway* types keep their external-label BPMN treatment, and agent guidance across MCP and the docs nudges toward workflow.sequenceFlow for directional edges and workflow.decision for plain decision diamonds.

fixed

Sub-pixel residual segments removed from routed edges

Edge paths now run through simplifyPath after the funnel and channel-routing passes, so sub-pixel interior segments produced when source-funnel and target-funnel shifts collide are collapsed before the renderer sees them. Previously a hairline residual could survive to the SVG output, misorienting an arrow marker or producing a barely-visible bent path.

fixed

Back-edges no longer clip the canvas top and stay anchored on shared faces

The canvas now includes routed edge polylines when computing its bounds, so back-edges and U-hooks that arc above the top-most node no longer get sliced by the canvas edge. The same pass keeps anchor bounds in step with shape bounds during the centering shift, so when two or more edges share a target face the terminal segments stay pinned to the shape perimeter instead of drifting tens of pixels off the target. Visible on dense state-machine and architecture diagrams where multiple edges converge on the same node.

fixed

Edges no longer graze the outlines of unrelated nodes

The router's obstacle-grazing detection now treats sibling node outlines as hard boundaries, so a candidate path that touches a neighbouring shape's edge is rejected before it reaches the renderer. Previously a path could clip a neighbour by a fraction of a pixel and pass the cleanliness check, leaving the edge appearing to enter and exit through an unrelated node.

breaking

Sequence diagram: sequence.note centres over the named lifeline

Sequence diagram: a sequence.note with a single extensions.anchor value now centres OVER the named lifeline instead of placing to the right of it. The previous 'right of' contract was idiosyncratic and produced broken output for notes anchored to the right-most lifeline (they hung off the canvas edge, forcing auto-extension). Multi-anchor span semantics (extensions.anchor: ["a", "b"]) are unchanged. Authors who relied on the old offset should switch to a multi-anchor span or move the anchor.

breaking

@zindex-ai/mcp is now a thin HTTP client

The @zindex-ai/mcp npm package now talks to api.zindex.ai exclusively. Local mode is removed and ZINDEX_API_KEY is required for every tool call. Bundle size drops from 802 KB to 38 KB, agents always get persistence and revision history through dsp_create_scene, and the hosted state platform is the single source of truth. Set ZINDEX_API_KEY in your MCP host config; ZINDEX_API_URL defaults to the production host.

new

FK edges anchor at the named column row in ER diagrams

ER edges now anchor at the FK column row inside each entity, precisely connecting to the column that carries the foreign key relationship. When an edge's label matches a column declared in either endpoint's extensions.columns, the engine auto-promotes the edge to a column-row anchor and drops the label - the column position carries the FK identity. Agents can also set endpoint.column directly. Set style.forceLabel: true on an edge to keep its label visible.

new

Canvas auto-extends to fit computed layout

The layout engine now automatically expands the scene canvas when auto-positioned nodes overflow the declared dimensions. A CANVAS_AUTO_EXTENDED diagnostic is emitted so agents know the canvas grew. Declared dimensions are treated as a minimum, not a hard cap - no more clipped diagrams when layout is denser than expected.

fixed

Converging edges no longer render as invisible duplicates

Multiple edges arriving at the same target port are now fanned across adjacent anchor slots. Previously, edges sharing an exact port produced visually identical paths that stacked on top of each other and rendered as a single line.

fixed

Diverging edges no longer stack at a single source anchor

When a node has multiple outgoing edges leaving the same face, the router now distributes their exit anchors evenly along the face. Previously, hub fanout patterns - an ER table with many child relationships, or a service node with several outbound connections - could collapse onto one anchor and read as a single overloaded line. Bundled edges that intentionally share a trunk segment are preserved.

improved

Dark and sketch theme edge contrast

Edge strokes in dark and sketch render themes now use higher contrast values, improving legibility on dense diagrams. Container outline visibility in the sketch theme was also raised.

improved

Payment Processing playground example

The built-in Flow example in the playground has been replaced with a richer Payment Processing reference diagram that demonstrates gateway routing, parallel flows, and service integrations at a realistic scale.

new

Agent-native front door via content negotiation

Requests to zindex.ai/ with Accept: application/json now return a machine-readable JSON document with the full API surface, schema locations, MCP endpoint, and usage instructions. Agents that call the root URL directly get structured onboarding without parsing HTML.

new

Human / Agent view toggle on the homepage

A pill control on the homepage lets you switch between the human-facing marketing view and the agent-facing raw JSON view, so you can see exactly what an agent receives when it discovers the platform.

improved

Playground JSON panel redesigned

The JSON panel now sits below the rendered diagram rather than beside it, giving diagrams more horizontal space. A copy button was added and the hide/show toggle was removed to simplify the layout.

improved

Fonts now self-hosted

Inter and JetBrains Mono are now served from the Zindex CDN via @fontsource. No requests are made to Google Fonts.

new

Channel-based orthogonal routing

Edges that share horizontal or vertical corridors are now routed through dedicated channels so they no longer overlap. Dense diagrams with many parallel connections render as clearly distinct paths rather than a single merged stroke.

new

Hard ordering constraints

The order constraint type lets you pin the relative layout position of two nodes - useful when the diagram has a canonical reading order that the auto-layout algorithm should preserve regardless of edge direction.

new

Frame auto-positioning and activation bar placement

Pools, lanes, and containers can now participate in auto-layout. The engine sizes and positions frames from their child nodes. UML sequence activation bars are placed automatically based on message flow.

new

Auto-contrast text color on custom node fills

Node labels automatically switch between light and dark text based on the perceived luminance of the node's background fill. Custom-colored nodes no longer require manually setting a contrasting label color.

new

updateScene operation

A new updateScene op type lets agents modify scene-level metadata - title, palette, layout strategy, diagram family, and canvas dimensions - after the scene has been created, without recreating it.

new

Scene-level color palettes

Scenes now support a palette field that maps semantic color names to hex or oklch values. Nodes and edges can reference palette tokens by name, making it straightforward to apply brand or corporate color schemes across an entire diagram.

improved

Edge labels centered on stroke with readable backgrounds

Edge labels are now positioned on the edge stroke rather than floating above it, and a background rectangle is rendered behind each label to prevent it from blending into crossing edges or node fills.

improved

Computed layout section is now opt-in

GET /scenes/:id omits the computed layout section by default. Pass includeComputed=true when you need bounding boxes and routed edge paths. This significantly reduces response size and token consumption for agents that only need scene state.

new

New layout engine

A ground-up rewrite of the layout engine ships as the new default. The engine handles node placement, edge routing, label positioning, and frame sizing in a single integrated pipeline. Agents can omit all coordinates and the engine produces clean, readable diagrams automatically.

new

Aesthetic scoring with best-of-N candidate selection

The layout engine generates multiple candidate arrangements and scores each on edge crossings, bend count, symmetry, and whitespace balance. The highest-scoring layout is selected automatically - no author tuning required.

new

Edge bundling for hub nodes

Nodes with many connections now bundle their edges into shared trunk segments before branching to individual targets. This reduces visual clutter in diagrams with central hubs such as API gateways, message brokers, and load balancers.

new

Visual diff rendering

Pass diffFromRevision to dsp_render_scene to render a color-coded diff between two revisions of a scene. Added elements are highlighted green, removed elements red, and modified elements amber.

new

UIflow diagram family

A new uiflow diagram family renders screen and modal nodes with appropriate chrome (title bars, navigation affordances) and routes transition edges as user-gesture flows. Suitable for product wireflows and onboarding sequence maps.

improved

BPMN and UML sequence diagram quality

BPMN workflows and UML sequence diagrams were both elevated to excellent rendering quality. Event circles and gateway diamonds now use external labels, activation bars auto-place on lifelines, and combined fragment frames render with correct transparency.

fixed

Edges no longer route through node interiors

The router now treats all node bounding boxes as hard obstacles. Edges that previously cut through nodes on busy diagrams are re-routed around them.

fixed

Layout caching by content fingerprint

Layout results are now cached by a content fingerprint of the scene. Repeated renders of an unchanged scene skip the layout pass entirely, significantly reducing latency for high-frequency render workflows.