Documentation API keys Dashboard Open editor

eDraw documentation

eDraw is a browser-based editor for system diagrams — the boxes-and-arrows drawings that describe how the parts of a system fit together. This page covers both halves of it: the editor a person uses, and the API an AI agent uses to create, update, document and manage the same drawings.

Overview

A drawing in eDraw is a graph. Components (servers, databases, queues, users, and so on) are the nodes; connections between their ports are the edges. Everything sits on an artboard — a fixed-size page such as A4 or 1080p — which is what gets exported.

Diagrams belong to a user account, are addressed by UUID, and auto-save while you work. The same diagram can be opened in the editor, driven through the API, and rendered as a written document — all three views read the same record.

Editor/app — draw and edit
Dashboard/dashboard — your diagrams
API keys/api-keys — for agents
AI API/api/ai

The editor

Open /app. The left palette holds components and shapes; drag one onto the canvas or click to place it at the centre.

ActionHow
Add a componentDrag from the palette, or click a palette item
Connect two componentsDrag from a port (the dot on an edge) to another component's port
Edit text / colourSelect the element, then use the properties panel on the right
Label a connectionClick the connection, then type in the label field
Change the pageArtboard menu in the toolbar — A4, Letter, 1080p, 4K, custom
SaveCtrl/Cmd + S; autosave also runs every 30 seconds once a diagram has been saved once
ExportToolbar export menu — PNG, SVG, PDF, JSON, Markdown
Undo / redoCtrl/Cmd + Z, Ctrl/Cmd + Shift + Z

The interface is available in English and Bangla; the language toggle sits in the toolbar.

Accounts & sharing

Public registration is disabled — an administrator creates accounts from the admin panel. Each user sees only their own diagrams, organised with per-user categories.

Live share broadcasts the board you are editing over a socket to anyone holding the share link, view-only, for as long as the session lasts. Shares are held in memory and expire 24 hours after the last update — they are for presenting, not for storage.

How the AI layer works

The editor stores a diagram as raw canvas state: pixel coordinates, numeric element ids, port names. That is a poor thing to ask a language model to author. So the API speaks a declarative spec instead — a list of nodes and edges with no coordinates — and the server compiles it into real diagram data, computing the layout itself.

  spec (nodes + edges)  ──compile──▶  diagram data (coordinates, ids, ports)
  spec                  ◀─decompile──  diagram data
  patch ops             ──apply────▶  diagram data (in place, layout preserved)
  diagram data          ──describe──▶  Markdown documentation

Three rules follow from that, and they matter more than any endpoint detail:

  • Never compute coordinates. Send nodes and edges; the server lays them out. Pin a node with explicit x/y only when you deliberately want it fixed.
  • Address nodes by key, not by id. Keys are stable, survive a round-trip through the editor, and are what patches refer to.
  • Prefer PATCH over PUT. A patch edits the existing drawing and keeps manual changes a person made; a PUT replaces the whole thing.

Authentication

Create a key at /api-keys. Send it on every request:

X-API-Key: edk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Authorization: Bearer <key> and ?api_key= also work. A signed-in browser session authenticates too, which is how the in-app screens call the same endpoints.

ScopeAllows
readList and read diagrams, specs, documentation, activity
writeEverything above, plus create, update, patch, delete, and write documentation
A key acts entirely as its owner and sees only that user's diagrams. Only the SHA-256 hash is stored, so the plaintext is shown exactly once at creation. Revoking takes effect immediately.
Keys cannot mint other keys — key management is session-only. A leaked key can read and change your diagrams, but cannot extend its own access.

Rate limit: 240 requests per minute per key, answered with 429 when exceeded.

Quick start

1. Create a diagram

curl -X POST https://edraw.void.bd/api/ai/diagrams \
  -H "X-API-Key: $EDRAW_KEY" -H "Content-Type: application/json" \
  -d '{
    "title": "Payment flow",
    "category": "Architecture",
    "spec": {
      "layout": { "direction": "LR" },
      "nodes": [
        { "key": "user", "type": "user",     "label": "Customer" },
        { "key": "api",  "type": "api",      "label": "API Gateway", "subtitle": "Kong" },
        { "key": "pay",  "type": "microservice", "label": "Payment Service",
          "details": "Validates the card, calls the PSP, writes the ledger entry." },
        { "key": "db",   "type": "database", "label": "PostgreSQL" },
        { "key": "mq",   "type": "queue",    "label": "RabbitMQ" }
      ],
      "edges": [
        { "from": "user", "to": "api", "label": "HTTPS" },
        { "from": "api",  "to": "pay", "label": "gRPC" },
        { "from": "pay",  "to": "db",  "label": "SQL" },
        { "from": "pay",  "to": "mq",  "label": "payment.settled" }
      ]
    }
  }'

The response carries the new UUID, an editor URL, and the summary of the documentation that was generated alongside it:

{
  "success": true,
  "uuid": "8f3c…",
  "title": "Payment flow",
  "stats": { "nodes": 5, "edges": 4, "direction": "LR" },
  "warnings": [],
  "url": "/app?diagram=8f3c…",
  "docUrl": "/api/ai/diagrams/8f3c…/doc"
}

2. Read it back

curl -H "X-API-Key: $EDRAW_KEY" \
  https://edraw.void.bd/api/ai/diagrams/8f3c…/spec

3. Change it

curl -X PATCH https://edraw.void.bd/api/ai/diagrams/8f3c… \
  -H "X-API-Key: $EDRAW_KEY" -H "Content-Type: application/json" \
  -d '{ "ops": [
        { "op": "addNode", "node": { "key": "redis", "type": "cache", "label": "Redis" } },
        { "op": "addEdge", "from": "pay", "to": "redis", "label": "idempotency keys" }
      ] }'

4. Read its documentation

curl -H "X-API-Key: $EDRAW_KEY" \
  "https://edraw.void.bd/api/ai/diagrams/8f3c…/doc?format=md"

The diagram spec

The full JSON Schema is served at /api/ai/schema.

Node

FieldTypeMeaning
keystringStable identifier used by edges and patches. Defaults to a slug of the label.
typestringOne of the component types. Defaults to server.
labelstringMain text on the box.
subtitlestringSmaller second line — good for the concrete technology ("PostgreSQL 16").
detailsstringA bullet list — one bullet per line. Drawn inside the box and repeated in the generated documentation. The node widens to fit the longest bullet.
color, textColorstringHex overrides. Defaults come from the type's palette colour.
layoutstringcompact (default) or card for the taller card style.
width, heightnumberOverride the default size.
x, ynumberPin the node. Auto-layout skips pinned nodes. Both are required together.

Edge

FieldTypeMeaning
from, tostringRequired. Node keys. An unknown key is a 400, not a silent skip.
labelstringText drawn on the line — the protocol, the payload, the trigger.
arrowstringend (default), both, or none.
fromPort, toPortstringtop / right / bottom / left. Chosen automatically from the flow direction if omitted.

Top level

FieldMeaning
nodesRequired. 1–500 nodes.
edgesUp to 2000 edges.
layout{ direction, gapX, gapY } — see Layout.
artboardA size name, an object { size, width, height, bgColor }, or false for none. Omit it and the smallest standard page that fits is chosen.

Artboard sizes: A4, A4-landscape, A5, A5-landscape, A3, A3-landscape, Letter, Letter-landscape, Legal, Legal-landscape, 1080p, 4K, custom.

Use POST /api/ai/preview to compile a spec and see the resulting layout, warnings and thumbnail without saving anything. It is the cheapest way to check a spec is valid.

Component types

Pick the type that matches what the thing is — it sets the icon, the default colour, and how the component is grouped in generated documentation. The live list is in /api/ai/manifest.

note, label, textbox and image are annotations — they are listed separately in documentation and left out of flow analysis. The eight shape types (rectangle, circle, ellipse, triangle, diamond, hexagon, parallelogram, star) are plain geometry for grouping and decoration.

Layout

The server runs a layered layout. Nodes are ranked by longest path along the edges, so anything downstream lands in a later column; nodes within a rank are then ordered by the average position of their predecessors, which keeps crossings down. Cycles are tolerated — ranks simply stop growing.

DirectionFlowDefault ports
LRLeft to right (default)right → left
RLRight to leftleft → right
TBTop to bottombottom → top
BTBottom to toptop → bottom

gapX controls the space between ranks, gapY the space within a rank. Defaults are 90 and 60.

To re-run the layout on an existing drawing — after adding several nodes, say — send { "op": "relayout", "direction": "TB" }. Be aware that this discards any manual positioning a person did in the editor.

Updating with patches

A patch is a list of operations applied in order. This is the right way to evolve a diagram: it touches only what you name and leaves the rest — including human edits — alone.

PATCH /api/ai/diagrams/{uuid}
{
  "expectedUpdatedAt": "2026-08-08 01:22:07",
  "ops": [
    { "op": "addNode",    "node": { "key": "cdn", "type": "cdn", "label": "Cloudflare" } },
    { "op": "addEdge",    "from": "user", "to": "cdn", "label": "HTTPS" },
    { "op": "updateNode", "key": "db", "set": { "subtitle": "PostgreSQL 16", "color": "#0e7490" } },
    { "op": "updateEdge", "from": "api", "to": "pay", "set": { "label": "gRPC / mTLS" } },
    { "op": "removeEdge", "from": "user", "to": "api" },
    { "op": "setTitle",   "title": "Payment flow (v2)" },
    { "op": "relayout",   "direction": "LR" }
  ]
}
OpFieldsEffect
addNodenodeAdds a node. Its key is made unique if it collides. Triggers a relayout unless you pin coordinates or pass relayout: false.
updateNodekey, setChanges label, subtitle, details, colour, type, size, position, font.
moveNodekey, x, yRepositions without relayout.
removeNodekeyRemoves the node and every edge touching it.
addEdgefrom, to, label, arrowConnects two nodes. A duplicate (in either direction) is skipped with a warning.
updateEdgefrom, to, setChanges label, arrow or ports.
removeEdgefrom, toRemoves the connection.
setTitletitleRenames the diagram.
setArtboardsize, width, height, bgColorChanges the page.
relayoutdirectionRe-runs auto-layout and re-points every connection.
clearRemoves all nodes and edges, keeping the diagram record.

Node references resolve by key first, then numeric id, then exact label text — so { "op": "updateNode", "key": "PostgreSQL" } works even on a diagram drawn by hand that has no keys yet.

Add "dryRun": true to see applied, warnings and the resulting spec without writing anything. Worth doing before a destructive patch.

Generated documentation

Every diagram can describe itself. The generator walks the graph rather than the pixels and produces Markdown containing: a summary naming the entry points, terminals and busiest component; a component table grouped by family, with the details text as prose; a connection table; the end-to-end paths traced from each entry point; annotations; and a Mermaid version of the flowchart.

Documentation is written automatically on create, replace and patch. Fetch it in three formats:

RequestReturns
GET …/docMarkdown (the canonical form)
GET …/doc?format=htmlA styled, printable page
GET …/doc?format=jsonMarkdown plus the machine-readable graph
GET …/doc?refresh=trueRegenerates from the current drawing first
GET …/mermaidJust the Mermaid flowchart source

To write the documentation yourself instead — an agent explaining why the system is shaped this way, which no generator can infer — POST …/doc with { "content": "# …", "summary": "…" }. Authored documents are flagged generated: false and are not overwritten by later automatic regeneration unless you ask for it.

To keep the generated body but add context, pass notes on any create, PUT or PATCH — the text is inserted as a Notes section above the component tables.

In the browser, /docs/diagram/{uuid} renders the document for reading or printing to PDF.

Endpoint reference

Base URL /api/ai. Machine-readable index at /api/ai/manifest.

Discovery

GET/api/ai/manifest

Capabilities, component types, artboard sizes, patch ops, endpoint list. No auth required.

GET/api/ai/schema

JSON Schema for spec and patch bodies. No auth required.

GET/api/ai/whoami

Which user the credential resolves to, its scopes, and how many diagrams they own.

Diagrams

GET/api/ai/diagrams

List your diagrams. Filters: ?q= title search, ?category=, ?limit= (max 500).

POST/api/ai/diagrams

Create from { title, category, spec, notes }. Pass documentation: false to skip generating docs. Returns 201.

GET/api/ai/diagrams/{uuid}

Read one. ?include=spec,data,doc,mermaid — defaults to spec.

GET/api/ai/diagrams/{uuid}/spec

The drawing as an editable spec — the form to read before patching.

PUT/api/ai/diagrams/{uuid}

Replace contents with a new spec. With only title/category, updates metadata and leaves the drawing untouched.

PATCH/api/ai/diagrams/{uuid}

Apply ops. Supports dryRun and expectedUpdatedAt.

DELETE/api/ai/diagrams/{uuid}

Delete the diagram and its documentation. Not recoverable.

POST/api/ai/preview

Compile a spec and return the layout, warnings and thumbnail without saving.

Documentation

GET/api/ai/diagrams/{uuid}/doc

?format=md|html|json, ?refresh=true to regenerate first.

POST/api/ai/diagrams/{uuid}/doc

Regenerate, or replace with your own content.

DELETE/api/ai/diagrams/{uuid}/doc

Delete the stored document. The next read regenerates one.

GET/api/ai/diagrams/{uuid}/mermaid

Mermaid flowchart source. ?format=json to wrap it in JSON.

GET/api/ai/docs

Every diagram of yours that has a stored document.

Organisation

GET/api/ai/categories

List your categories.

POST/api/ai/categories

Create one. Idempotent by name.

GET/api/ai/activity

Recent automated changes to your diagrams — action, diagram, key used, timestamp.

Errors & concurrency

StatusMeaningWhat to do
400Bad spec or patch. The body names the offending index and field.Fix and resend — do not retry unchanged.
401Missing, invalid or revoked key.Check the X-API-Key header.
403The key lacks the required scope.Use a read/write key.
404No such diagram for this user.Check the UUID and the key's owner.
409expectedUpdatedAt did not match — someone else changed it.Re-read, rebase your ops, retry.
429Rate limit exceeded.Back off; the window is one minute.

Errors are precise on purpose. An unknown node type is downgraded to server with a warnings entry, but an edge pointing at a node that does not exist is a hard 400 — silently dropping it would produce a diagram that looks fine and is wrong.

Several agents editing one diagram should pass expectedUpdatedAt (from the updated_at you read) on every write. Without it, last write wins.

The editor autosaves every 30 seconds. If a person has the diagram open while you patch it, their next autosave will overwrite your change with the state their browser is holding. For anything long-running, agree the drawing is yours for the duration.

Instructions for an agent

Paste this into an agent's system prompt or tool description, with the key filled in.

You can draw and maintain system diagrams in eDraw.

Base URL: https://edraw.void.bd/api/ai
Auth:     header  X-API-Key: <key>

Discover the API with GET /manifest and GET /schema before your first write.

Creating a diagram
  POST /diagrams  { "title": "...", "spec": { "nodes": [...], "edges": [...] } }
  - Nodes: { key, type, label, subtitle, details }
  - Edges: { from, to, label }   (from/to are node keys)
  - Never send x/y. The server lays the diagram out.
  - Put the concrete technology in `subtitle`, and the explanation in `details`
    as one short bullet per line ("Owns the order lifecycle\nOnly writer to
    the orders table"). Do not write a paragraph — every line becomes a
    bullet, on the canvas and in the documentation.

Changing a diagram
  1. GET /diagrams/{uuid}/spec       - read the current nodes and edges
  2. PATCH /diagrams/{uuid} { "ops": [...] }
  Use ops (addNode, updateNode, removeNode, addEdge, updateEdge, removeEdge,
  setTitle, relayout). Do not re-send the whole spec unless you intend to
  discard everything, including edits a person made.
  Add "dryRun": true first when the patch removes anything.

Documentation
  Docs regenerate automatically on every write.
  GET  /diagrams/{uuid}/doc            - Markdown
  POST /diagrams/{uuid}/doc  { "notes": "..." }     - keep the generated body, add context
  POST /diagrams/{uuid}/doc  { "content": "..." }   - replace with your own prose

Rules
  - One diagram per subject. Check GET /diagrams before creating a near-duplicate.
  - Label every edge with what actually crosses it (protocol, payload, trigger).
  - Choose the type that matches reality: database, cache, queue, api,
    loadbalancer, microservice, cdn, firewall, storage, cloud, server,
    webapp, mobile, desktop, user.
  - A 400 means your body is wrong. Read the message; do not retry unchanged.

Stored data format

What the editor actually saves, in case you need to read or write it directly (?include=data, or data instead of spec on write):

{
  "elements": [{
    "id": 1,              // numeric, unique within the diagram
    "key": "api",         // added by the API; stable handle, ignored by the editor
    "type": "api",
    "x": 80, "y": 120, "width": 140, "height": 80,
    "text": "API Gateway",
    "subtitle": "Kong",
    "details": "…",
    "color": "#F59E0B", "textColor": "#ffffff",
    "layoutStyle": "compact",
    "locked": true
  }],
  "connections": [{
    "id": 1754600000000,
    "from": 1, "to": 2,           // element ids, not keys
    "fromPort": "right", "toPort": "left",
    "label": "gRPC",
    "arrowType": "end"
  }],
  "artboards": [{
    "id": 1, "name": "Artboard 1", "size": "A4-landscape",
    "x": 0, "y": 0, "width": 1123, "height": 794, "bgColor": "#ffffff"
  }],
  "markerStrokes": [],
  "panOffset": { "x": 0, "y": 0 },
  "zoom": 1,
  "meta": { "source": "ai-api", "direction": "LR", "generatedAt": "…" }
}
Unknown properties survive a round-trip through the editor, which is why key and meta stay attached after a person opens and saves the diagram.

Architecture

Node.js and Express, SQLite through better-sqlite3, Socket.IO for live share. The frontend is plain JavaScript against a single canvas — no build step, no framework.

FileRole
src/server.jsExpress app, routes, sessions, Socket.IO
src/database.jsSchema, migrations, every SQL query
src/auth.jsSession guards
src/app.jsThe editor — canvas, tools, export, autosave
src/ai-model.jsSpec compile/decompile, layered layout, patch engine
src/ai-docs.jsGraph analysis, Markdown/Mermaid/HTML generation
src/ai-thumb.jsSVG thumbnails for diagrams made without a browser
src/ai-routes.jsThe /api/ai router

Tables

TableHolds
usersAccounts; bcrypt hashes; role user or admin
diagramsUUID, owner, title, category, JSON data, thumbnail
user_categoriesPer-user category names
api_keysSHA-256 hash, prefix, scopes, last used, revoked
diagram_docsOne document per diagram; Markdown plus summary
ai_activityAudit trail of every automated change
visitors, active_sessionsAnalytics for the admin panel

Running & deploying

Locally

npm install
PORT=7411 npm start      # http://localhost:7411

The database is created at src/edraw.db on first run, with an admin account seeded if the users table is empty.

In production

The published copy runs from /published/edraw under systemd (edraw.service) on port 7412, behind nginx, reachable at edraw.void.bd over TLS and on the LAN at 192.168.31.132:7410.

# publish source, never the database
rsync -a --exclude edraw.db --exclude node_modules src/ /published/edraw/src/
sudo systemctl restart edraw
systemctl status edraw --no-pager
/published/edraw/src/edraw.db is the live database — every account, diagram and API key. Never copy over it, never delete it. Back it up before any deploy.