Skip to main content

Excalidraw API updates · mutation guide

Update the same diagram without hiding concurrent edits

Choose the smallest mutation that fits: merge additive structure, patch known elements, or deliberately regenerate the whole scene. When replacement or targeted patching could collide with browser work, carry the live document revision and handle a stale read explicitly.

01

Open

Create live document

02

Read

Capture revision

03

Patch

Smallest change

04

Conflict

409 stale_read

05

Review

Same canvas

Editable workflow example

Editable OAuth PKCE sequence diagram used to demonstrate guarded API patching and stale-read refusal on one persistent canvas
Faithful SVG render from the editable v2 spec. The API proof restyles the state-validation element, then replays the old revision to demonstrate stale-read refusal.Open full-size SVG

Persistent diagram automation is a maintenance problem, not merely a create call. Good clients decide how much of the canvas they own, re-read before a conflicting write, and make reconciliation visible instead of silently replacing newer human work.

Product boundary: expectedUpdatedAt is a best-effort optimistic precondition on patch and regenerate, not a lock. A mismatched non-empty revision returns 409 stale_read before the write. Omit it to force a write deliberately. A never-opened diagram reports docUpdatedAt as null until its collaborative document exists; a narrow read-to-write race and scene-mirror lag can still occur.

Decision guide

Choose the smallest mutation

The three write surfaces have different ownership and concurrency semantics. Pick from what the update must change, then design the client around that contract.

Swipe to compare

JobUse whenConcurrency behavior
Additive edit/edit — add nodes or edges, or update allowed label and style fields, without moving, resizing, or deleting existing elements.No expectedUpdatedAt input. The fragment merges against the current live document, so use it for additive, non-reflow changes rather than replacement.
Element patch/patch — apply ordered add, update, move, resize, restyle, or delete operations to known element ids.Optional expectedUpdatedAt. A matching live revision proceeds, a stale non-empty revision returns 409, and omission skips the guard.
Regenerate/regenerate — replace the whole scene from a new spec while retaining the diagram id and editor URL.Optional expectedUpdatedAt is strongly recommended. Omission force-replaces; comments attached to removed element ids are system-resolved.

Prefer the smallest write

Use additive edit when existing geometry must remain untouched. Use patch when your integration owns specific element ids and needs explicit operations.

Regenerate deliberately

Use whole-scene replacement only when the new spec should become authoritative. Supply the last live revision, then re-read and reconcile after any 409.

Reproduce it

Copy setup

01

Open, then read the live revision

Open the editor once before relying on docUpdatedAt. A diagram that has never created its collaborative document reports null, so the script refuses to continue until a live revision exists.

Terminal

export EXCALIWOW_TOKEN=excw_pat_…
export DIAGRAM_ID=<existing-diagram-id>

printf 'Open before reading the live revision: https://excaliwow.com/c/%s\n' "$DIAGRAM_ID"

READ_RESPONSE=$(curl -fsS \
  "https://excaliwow.com/api/v1/diagrams/$DIAGRAM_ID" \
  -H "Authorization: Bearer $EXCALIWOW_TOKEN")
REVISION=$(printf '%s' "$READ_RESPONSE" | jq -r .docUpdatedAt)

if [ "$REVISION" = "null" ]; then
  printf 'Open the editor once, then re-read the diagram.\n' >&2
  exit 1
fi

02

Patch with the captured revision

Restyle one known element with expectedUpdatedAt, then replay the identical body. The first request advances the document revision; the replay must be refused as stale.

Terminal

PATCH_BODY=$(jq -n --arg revision "$REVISION" '{
  ops: [{
    op: "restyle",
    id: "app-validate",
    strokeColor: "#c92a2a",
    backgroundColor: "#ffe3e3",
    fillStyle: "solid"
  }],
  expectedUpdatedAt: $revision
}')

curl -fsS \
  "https://excaliwow.com/api/v1/diagrams/$DIAGRAM_ID/patch" \
  -H "Authorization: Bearer $EXCALIWOW_TOKEN" \
  -H "Content-Type: application/json" \
  --data-binary "$PATCH_BODY" \
  -o /tmp/patch-result.json

HTTP_STATUS=$(curl -sS \
  "https://excaliwow.com/api/v1/diagrams/$DIAGRAM_ID/patch" \
  -H "Authorization: Bearer $EXCALIWOW_TOKEN" \
  -H "Content-Type: application/json" \
  --data-binary "$PATCH_BODY" \
  -o /tmp/stale-result.json \
  -w '%{http_code}')

test "$HTTP_STATUS" = "409"
jq -e '.error == "stale_read"' /tmp/stale-result.json

03

Re-read before reconciliation

On 409 stale_read, fetch the current diagram, inspect the collaborator’s newer state, and build a fresh narrow operation. Do not automatically retry a replacement request against an unknown canvas.

Human review

Change what the agent misunderstood

  1. Move state validation into the trust-boundary position and give its failure treatment a strong red visual signal.
  2. Add the reviewer note that the PKCE verifier stays server-side and tokens do not appear in the browser URL.

Same-canvas follow-up

Send the correction back

Update the same diagram through a guarded element patch: restyle app-validate without moving the human-positioned trust boundary or replacing the reviewer note.

Review the critical path, not the pixels

  • The editor has been opened and docUpdatedAt is non-null before the guarded write.
  • The first patch applies to the same persistent diagram and advances its live revision.
  • Replaying the stale expectedUpdatedAt returns 409 with error stale_read.
  • Human labels and layout remain outside the narrow restyle operation.

Common questions

When should I use edit instead of patch?
Use edit for additive graph changes and allowed label or style updates when existing elements must not move, resize, or disappear. Use patch when you need ordered operations against known element ids.
Why is docUpdatedAt null on my diagram?
The live collaborative document does not exist until the diagram has been opened in the editor. Open it once, fetch it again, and only then capture the revision for a guarded patch or regeneration.
Does expectedUpdatedAt lock the diagram?
No. It is a best-effort optimistic precondition checked before patch or regeneration. It detects an already-stale supplied revision, but it is not a transaction-spanning lock and does not eliminate every read-to-write race.
What should a client do after stale_read?
Fetch the latest diagram, compare the intended change with the new canvas, and construct a fresh minimal request. Ask for human resolution when the automation and collaborator changed the same meaning.

Maintain one diagram honestly

Start free—no credit card. First 10,000 verified accounts keep the core workspace free; 3 diagrams to start and earn more capacity.

Start free