API

A local WebSocket server for reading and writing to your GalaxyBrain workspace. Build scripts, automations, and integrations that interact with your pages, templates, and workspace layout.

Getting Started

1. Install and start the API server

This downloads the server from npm and starts it on port 1924:

npx galaxybrain-api

To use a different port:

GB_API_PORT=3000 npx galaxybrain-api

2. Open GalaxyBrain in the browser

The app automatically connects to ws://localhost:1924 and registers itself as an instance.

3. Connect your client

const ws = new WebSocket("ws://localhost:1924");

ws.onopen = () => {
  ws.send(JSON.stringify({
    type: "command",
    requestId: "1",
    cmd: "ORIENTATION"
  }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  console.log(msg);
};

Architecture

The API has three moving pieces:

  1. The API server — a local WebSocket server, started with npx galaxybrain-api. An HTTP GET / returns the health check string "GalaxyBrain API Server/{protocolVersion}" (e.g. "GalaxyBrain API Server/1"). The protocol version increments on breaking changes to the WebSocket command/event format.
  2. GalaxyBrain browser tabs — each tab connects to the server and registers as an addressable instance.
  3. External clients — your scripts, tools, or integrations connect to the same server and send commands.

External clients do not connect directly to the browser tab. Commands are routed through the server to a specific instance, and responses are routed back.

GalaxyBrain instances can be in one of three states:

StateDescription
pickerNo project is open
folderA folder-backed project is open
demoA demo project is open

Always available commands

LIST_INSTANCES, LIST_FOLDERS, OPEN_FOLDER, OPEN_DEMO, CLOSE_PROJECT, REMOVE_RECENT_FOLDER, FILES_WATCH, FILES_UNWATCH, SUBSCRIBE, UNSUBSCRIBE

Require an open project

READ_PAGES, CREATE_PAGES, UPDATE_PAGES, DELETE_PAGES, QUERY, READ_TEMPLATES, CREATE_TEMPLATES, UPDATE_TEMPLATES, DELETE_TEMPLATES, CREATE_FROM_TEMPLATE, PUSH_PAGE_ITEMS, PUSH_TEMPLATE_ITEMS, POP_PAGE_ITEMS, POP_TEMPLATE_ITEMS, READ_WORKSPACE, WRITE_WORKSPACE, MAP, ANCESTORS, ORIENTATION

App URL Parameters

gb_port

API server port to connect to. Default: 1924.

?gb_port=3000

gb_id

Instance ID the browser tab registers with. If omitted, a random 6-character ID is generated.

?gb_id=desk-main

Use a stable gb_id when you want to target a specific browser tab from a script, or run multiple tabs simultaneously. If two tabs use the same gb_id, the older one is evicted.

demo

Startup demo selection. An app convenience — API clients use OPEN_DEMO instead.

Transport

Message Format

All messages are JSON objects with a type field. Five message types on the wire:

  • identify — sent by the browser app to register with the server
  • command — sent by external clients to the server
  • response — sent back for commands and identify
  • error — server-level routing or protocol errors
  • event — server-pushed subscription events

External clients only use the last four. The identify handshake is documented here because it governs instance registration and affects behavior visible to external clients.

Identify (Browser App → Server)

When a GalaxyBrain browser tab connects, it identifies itself:

{
  "type": "identify",
  "instanceId": "desk-main",
  "protocolVersion": 1,
  "state": "picker",
  "folder": null,
  "demo": null,
  "offline": false,
  "version": "0.5.0"
}

The server responds with:

{ "type": "response", "requestId": "identify", "ok": true, "serverVersion": "0.2.1" }

The serverVersion field contains the API server’s npm package version string.

Protocol mismatch: If protocolVersion does not match the server's version, the connection receives a PROTOCOL_MISMATCH error. If another tab is already registered with the same instanceId, the older tab receives an eviction event and is disconnected — the new tab takes over. External clients never send identify.

Command (Client → Server)

{
  "type": "command",
  "requestId": "req-1",
  "instance": "desk-main",
  "cmd": "READ_PAGES",
  ...
}
FieldTypeRequiredDescription
type"command"YesMessage type
requestIdstringYesUnique ID for correlating the response
cmdstringYesCommand name
instancestringNoTarget instance ID

Response (Server → Client)

{
  "type": "response",
  "requestId": "req-1",
  "cmd": "READ_PAGES",
  ...
}

Every response includes type, requestId, and cmd.

Error (Server → Client)

{
  "type": "error",
  "requestId": "req-1",
  "code": "INSTANCE_REQUIRED",
  "message": "Multiple instances connected. Specify \"instance\" field."
}

Event (Server → Client)

{
  "type": "event",
  "event": "pages_updated",
  "seq": 42,
  "instanceId": "desk-main",
  "timestamp": 1712766240
}

Instance Routing

  • One instance connected: instance is optional — auto-routed.
  • Multiple instances: instance is required, otherwise INSTANCE_REQUIRED.
  • No instances: NO_INSTANCES error.
  • Unknown instance: UNKNOWN_INSTANCE error.

Response Patterns

Commands use three distinct response shapes:

1. Singleton

Top-level ok with command-specific fields. Used by: LIST_FOLDERS, OPEN_FOLDER, OPEN_DEMO, CLOSE_PROJECT, QUERY, MAP, ANCESTORS, ORIENTATION, READ_WORKSPACE, WRITE_WORKSPACE, SUBSCRIBE, UNSUBSCRIBE, CREATE_FROM_TEMPLATE.

2. Batch

Top-level results array aligned to input order, each entry succeeds or fails independently. Used by: READ_PAGES, CREATE_PAGES, UPDATE_PAGES, DELETE_PAGES, READ_TEMPLATES, CREATE_TEMPLATES, UPDATE_TEMPLATES, DELETE_TEMPLATES, PUSH_PAGE_ITEMS, PUSH_TEMPLATE_ITEMS, POP_PAGE_ITEMS, POP_TEMPLATE_ITEMS.

3. Parse failure

When the payload is malformed before execution can begin.

Ordering and Delivery

  • Commands are processed FIFO per target instance.
  • Batch arrays execute in array order — later operations can observe earlier mutations from the same batch.
  • For API-triggered mutations, the response is sent first, then any resulting events are flushed.

IDs, Versions & Snapshots

ConceptFormatDescription
Page ID[a-zA-Z0-9]{20}20-character alphanumeric string. Templates also use page IDs.
Block IDNon-negative integerUnique within a page.
Variable IDNon-negative integerUnique within a page.
Page VersionIntegerIndependent per page. Used for optimistic concurrency via readVersion.
Workspace VersionIntegerIndependent from page versions.
snapshotSeqIntegerInstance's latest committed event sequence at read time. Use for sync detection.
Conflict detection: Mutation commands that accept readVersion compare it against the current version. Match → proceed. Mismatch → CONFLICT error. Omitted or null → skip check.

Data Model

Writable Schemas

These shapes are used when creating or updating pages and templates.

Page Body (Create / Full Replace)

PageBody = { icon: string -- single emoji title: TitleUnitInput[] -- title units (text or templateValue only) subtitle: UnitInput[] -- subtitle units (all unit types) blocks: BlockInput[] -- non-empty array } BlockInput = { blockId: number -- non-negative integer, unique within page items: ItemInput[] -- non-empty array linkOrder?: string | null -- sort rule (see Link Order) lastSelectedTemplateId?: string | null }

null entries in the pages array create default blank pages.

Item Input

TextItemInput = { type: "text" style: "" | "#" | "##" | "###" | "*" | "[ ]" | "[X]" | "ol" content: UnitInput[] indentLevel?: number -- clamped to 0-8, default 0 orderedListStart?: number | null -- only for "ol" style } VarItemInput = { type: "var" id: number -- non-negative, unique within page name: string formula: FormulaUnitInput[] -- text, metaRef, or templateValue only } ImageItemInput = | { type: "image", imageId: string } -- existing image by ID | { type: "image", imageFile: string } -- reference into images map PageLinkItemInput = { type: "pageLink" pageId: string -- cannot self-link }

Unit Input

TextUnitInput = { type: "text", text: string, unitStyle?: "bold" | "italic" | "boldItalic" } WebLinkUnitInput = { type: "webLink", text: string, url: string, unitStyle?: ... } PageLinkUnitInput = { type: "pageLink", pageId: string } MetaRefUnitInput = { type: "metaRef", ref: string } TemplateValueUnitInput = { type: "templateValue", valueType: "nextNumber" | "nextDay" | "dateToday" }
Restrictions: Titles accept only unstyled text and templateValue. Formulas accept only unstyled text, metaRef, and templateValue. Subtitle and text item content accept all unit types.

Surgical Update

UPDATE_PAGES and UPDATE_TEMPLATES support surgical block-level edits as an alternative to full replacement:

SurgicalUpdate = { icon?: string title?: TitleUnitInput[] subtitle?: UnitInput[] updateBlocks?: [{ blockId, items?, linkOrder?, lastSelectedTemplateId? }] insertBlocks?: [{ blockId, items, linkOrder?, lastSelectedTemplateId? }] deleteBlockIds?: number[] blockOrder?: number[] -- must list every surviving block exactly once }

blocks (full replacement) is mutually exclusive with all surgical fields.

Serialized (Read) Schemas

Read operations return these shapes. Fields marked (read-only) are computed and must not be round-tripped back into writes.

Page

{
  "pageId": "AbcDef1234567890GhIj",
  "icon": "📄",
  "title": [ /* serialized units */ ],
  "subtitle": [ /* serialized units */ ],
  "blocks": [ /* serialized blocks */ ],
  "blockOrder": [0, 1, 2],
  "createdAt": 1712766240,
  "updatedAt": 1712766300,
  "templateValues": { "nextNumber": "5" },
  "counts": { "words": 150, "characters": 820, ... }
}

Read-only fields: pageId, blockOrder, createdAt, updatedAt, templateValues, counts.

Block

{
  "blockId": 0,
  "linkOrder": "A.M.tt",
  "lastSelectedTemplateId": null,
  "items": [ /* serialized items */ ],
  "createdAt": 1712766240,
  "updatedAt": 1712766300,
  "counts": {
    "words": 50, "characters": 280,
    "checkboxes": 0, "checkboxesChecked": 0, "checkboxesUnchecked": 0,
    "pageLinks": 2, "listItems": 1
  }
}

Read-only fields: createdAt, updatedAt, counts.

Serialized Items

indentLevel is only present when > 0 (default 0). orderedListStart is only present when style is "ol".

// Text (indentLevel omitted when 0; orderedListStart only for "ol" style)
{ "type": "text", "style": "", "content": [ /* units */ ], "indentLevel": 2 }
{ "type": "text", "style": "ol", "content": [ /* units */ ], "orderedListStart": 3 }

// Var (value is read-only, computed from formula)
{ "type": "var", "id": 0, "name": "Total", "formula": [ /* units */ ], "value": "150" }

// Image
{ "type": "image", "imageId": "a1b2c3...64hex.png" }

// Page Link (title is read-only, resolved at read time)
{ "type": "pageLink", "pageId": "AbcDef1234567890GhIj", "title": "My Page" }

Read-only fields: var value, pageLink title.

Serialized Units

// Text
{ "type": "text", "text": "Hello", "unitStyle": "bold" }

// Web Link
{ "type": "webLink", "text": "Example", "url": "https://example.com" }

// Page Link (title is read-only)
{ "type": "pageLink", "pageId": "AbcDef1234567890GhIj", "title": "My Page" }

// Meta Ref — success
{ "type": "metaRef", "ref": "M.tw", "value": "150" }
// Meta Ref — error
{ "type": "metaRef", "ref": "V.AbcDef1234567890GhIj.0", "value": null, "error": "NOT_FOUND" }

// Template Value — on template page (no resolved value)
{ "type": "templateValue", "valueType": "dateToday" }
// Template Value — on regular page (resolved)
{ "type": "templateValue", "valueType": "dateToday", "value": "2026-04-10" }

Read-only fields: metaRef.value, metaRef.error, pageLink.title, templateValue.value on regular pages.

Meta ref errors: NOT_FOUND, VAR_MISSING_REFERENCE, VAR_CIRCULAR_REFERENCE.

Normalization

The parser applies these normalization steps to written content:

  • Adjacent compatible text and web-link units are merged.
  • A lone page-link unit inside an unstyled text item is converted into a standalone pageLink item.
  • Bullet, checkbox, and ordered-list items keep a lone inline page-link unit as text content.

Link Order

linkOrder controls automatic sorting of page-link items within a block. Format: "{direction}.{sortKey}".

DirectionSort Keys
A (ascending)M.tt (title), M.ca (created at), M.ua (updated at), M.tw (total words), M.tc (total characters), M.tb (total blocks), M.tli (total list items), M.tpl (total page links), M.tr (reference count), M.tcb (total checkboxes), M.tcbc (checked), M.tcbu (unchecked), V.{varName} (variable value)
D (descending)

Examples: A.M.tt, D.M.ua, A.V.score.

Meta References

metaRef units reference live computed values. The ref string uses dot-separated segments:

PatternDescriptionExample
CT.{format}Current timeCT.a.d
CA.{pageId}.{format}Page created-atCA.AbcDef1234567890GhIj.a.d
CA.{pageId}.{blockId}.{format}Block created-atCA.AbcDef1234567890GhIj.0.a.d
UA.{pageId}.{format}Page updated-atUA.AbcDef1234567890GhIj.r.d
V.{pageId}.{varId}Variable valueV.AbcDef1234567890GhIj.0
PLCV.{pageId}.{blockId}.{fn}.{varName}Page-link aggregationPLCV.AbcDef1234567890GhIj.0.sum.score
M.{type}Global metadataM.tp
M.{type}.{pageId}Page-level metadataM.tw.AbcDef1234567890GhIj

Time formats: a.ut (UNIX), a.d (date), a.dow (day of week), r.s / r.m / r.h / r.d (relative). Relative only valid for CA/UA.

PLCV derivations: cnt, sum, avg, min, max.

Images

Image IDs are content-addressed: {64 lowercase hex sha256}.{ext}. Allowed extensions: png, jpg, jpeg, webp, gif, svg.

Commands that accept new images support a top-level images map with base64 data URIs:

{
  "images": {
    "cover.png": "data:image/png;base64,iVBORw0KGgo..."
  }
}

Commands

Server

LIST_INSTANCES

Lists all connected GalaxyBrain browser instances.

Request
{ "type": "command", "requestId": "1", "cmd": "LIST_INSTANCES" }
Response
{
  "ok": true,
  "instances": [{
    "instanceId": "desk-main", "connectedAt": 1712766240,
    "state": "folder", "folder": "Work Notes",
    "offline": false, "version": "0.4.0"
  }]
}
Read

LIST_FOLDERS

Returns recent folders and available demos.

Request
{ "type": "command", "requestId": "1", "cmd": "LIST_FOLDERS" }
Response
{
  "ok": true,
  "recentFolders": [{ "id": 1, "name": "My Project", "permission": "granted" }],
  "demos": ["Simple", "F1", "The Premier League", "The Universe"]
}
Note: permission: "granted" can open via API immediately. "prompt" requires a user gesture first.
Write

OPEN_FOLDER

Opens a recent folder by its numeric ID.

Request
{ "type": "command", "requestId": "1", "cmd": "OPEN_FOLDER", "id": 1 }
Response
{ "ok": true, "folder": "My Project", "skippedFiles": [] }

Errors: PARSE_ERROR, FOLDER_NOT_FOUND, PERMISSION_REQUIRED, FOLDER_UNAVAILABLE, FOLDER_LOAD_FAILED.

Write

OPEN_DEMO

Opens a demo project by name.

Request
{ "type": "command", "requestId": "1", "cmd": "OPEN_DEMO", "name": "Simple" }

Errors: PARSE_ERROR, OFFLINE, DEMO_LOAD_FAILED.

Write

CLOSE_PROJECT

Closes the current project and returns to picker state. No-op if already in picker.

Write

REMOVE_RECENT_FOLDER

Removes a folder from the recent folders list. Does not delete the filesystem folder.

Request
{ "type": "command", "requestId": "1", "cmd": "REMOVE_RECENT_FOLDER", "id": 2 }
Response
{ "ok": true }

Errors: PARSE_ERROR, FOLDER_NOT_FOUND.

File Watching

FILES_WATCH

Subscribes to filesystem change events inside a subfolder of the open project (wraps the Chromium FileSystemObserver API).

Request
{ "type": "command", "requestId": "1", "cmd": "FILES_WATCH", "subpath": "inbox", "recursive": true }

subpath is forward-slash-delimited and must resolve to an existing directory under the project root. Leading /, \, Windows-absolute paths, backslashes, .., ., and empty segments are rejected.

Response
{ "ok": true, "subscriptionId": "fwatch_..." }

Errors: PARSE_ERROR, FOLDER_NOT_FOUND, NO_PROJECT, FOLDER_UNAVAILABLE, UNSUPPORTED_BROWSER.

Subscriptions live on the GalaxyBrain instance (the open browser tab) until FILES_UNWATCH, project close, folder switch, demo switch, app reload, or an errored record.

FILES_UNWATCH

Stops a previously started folder watch. Unknown subscription IDs are intentionally no-ops.

Request
{ "type": "command", "requestId": "2", "cmd": "FILES_UNWATCH", "subscriptionId": "fwatch_..." }
Response
{ "ok": true }

Errors: PARSE_ERROR (subscriptionId missing or not a string).

Sharp edges: unknown means the OS dropped events; errored means observation is dead and the subscription is torn down; Windows emits cross-directory moves as disappeared+appeared; file events are not file-readiness events; per-origin observation limit surfaces as errored.

Read

READ_PAGES

Reads regular pages by ID or by open tabs.

Request (by IDs)
{
  "type": "command", "requestId": "1",
  "cmd": "READ_PAGES",
  "pageIds": ["AbcDef1234567890GhIj"],
  "icon": true, "title": true, "subtitle": true,
  "blockIds": [0, 4]
}
Request (by tabs)
{ "type": "command", "requestId": "1", "cmd": "READ_PAGES", "tabs": true }

pageIds and tabs are mutually exclusive. Read options (icon, title, subtitle, blocks) default to true. Optional blockIds filters which blocks are serialized.

Write

CREATE_PAGES

Creates one or more regular pages. null entries create default blank pages. returnPages: true includes the serialized page in each result.

Request
{
  "type": "command", "requestId": "1",
  "cmd": "CREATE_PAGES",
  "returnPages": true,
  "pages": [null, {
    "icon": "📝",
    "title": [{ "type": "text", "text": "New Page" }],
    "subtitle": [],
    "blocks": [{ "blockId": 0, "items": [...] }]
  }]
}
Write

UPDATE_PAGES

Updates pages via full block replacement or surgical edits. Pass readVersion for optimistic concurrency.

Surgical example
{
  "type": "command", "requestId": "1",
  "cmd": "UPDATE_PAGES",
  "pages": [{
    "pageId": "AbcDef1234567890GhIj",
    "readVersion": 3,
    "updateBlocks": [{ "blockId": 0, "items": [...] }],
    "insertBlocks": [{ "blockId": 4, "items": [...] }],
    "blockOrder": [0, 4]
  }]
}
Write

DELETE_PAGES

Deletes one or more regular pages.

{ "type": "command", "requestId": "1", "cmd": "DELETE_PAGES", "pageIds": ["AbcDef1234567890GhIj"] }

Errors: PAGE_NOT_FOUND, TEMPLATE_PAGE, LAST_PAGE.

Read

QUERY

Searches and filters pages with sorting, pagination, and field projection.

Request
{
  "type": "command", "requestId": "1",
  "cmd": "QUERY",
  "scope": "pages",
  "search": { "text": "invoice", "sections": ["title", "blocks"] },
  "fields": ["icon", "title", "counts"],
  "sortBy": "updatedAt",
  "sortDirection": "desc",
  "maxResults": 20
}
Parameters
FieldTypeDefaultDescription
pageIdsstring[]nullRestrict to specific pages
scopestring"pages""pages", "templates", or "all"
searchobjectnullText, error, or references search (see Search below)
fieldsstring[][]Fields to include in results
sortBystringnullSort mode
sortDirectionstringnull"asc" or "desc"
offsetnumber0Pagination offset
maxResultsnumbernullMax results (null = unlimited)
Field values
ValueDescription
"icon"Page icon emoji
"title"Page title as plain text string
"subtitle"Page subtitle as plain text string
"blocks"Array of block preview strings
"outboundPageLinks"Array of linked page IDs
"inboundPageLinks"Array of page IDs linking to this page
"inboundReferences"Array of page IDs referencing this page in text
"timestamps"createdAt and updatedAt
"vars"Array of { id, name, value }
"counts"Page statistics
Search

Mutually exclusive โ€” exactly one of text, errors, or references.

  • Text: { "text": "query", "caseSensitive": false, "sections": ["title", "subtitle", "blocks"] }
  • Errors: { "errors": "all" | "brokenPageLinks" | "brokenValues" }
  • References: { "references": <target>, "sections": ["subtitle", "blocks"] }

text: "" and sections: [] match nothing. Error search ignores titles.

References search targets
TargetShapeMatches
imageId{ "imageId": "<64hex>" } or { "imageId": "<64hex>.<ext>" }image items in blocks
varName{ "varName": "...", "prefix": false }var definitions, V refs (live name lookup), PLCV refs, block linkOrder keys (prefix: true enables trailing-* match)
metaRef{ "metaRef": <MetaRefQuery> }metaValue units in subtitles, block text items, and var formulas
pageLink{ "pageLink": { "pageId": "..." } }standalone pageLink items and inline pageLink units
webLinkUrl{ "webLinkUrl": "..." }webLink units (exact URL match)
MetaRef query object

head is required; every other field must be valid for the head.

FieldApplies to headsNotes
headall"CT", "CA", "UA", "V", "PLCV", or "M"
pageIdCA, UA, V, PLCV, M20-char page ID
blockIdCA, UA, PLCV, Mnon-negative integer
varIdVnon-negative integer
typeMmetaType code (tp, tb, โ€ฆ)
derivationPLCVone of cnt, sum, avg, min, max
varNamePLCVexact match
formatCT, CA, UAtime-format code; CT accepts absolute formats only

A meta ref matches iff every non-null field equals its parsed component. { "head": "M" } alone matches every M ref. matchCount counts structural occurrences. Invalid input returns PARSE_ERROR.

Graph filters

linksToPage, notLinksToPage, linkedFromPage, notLinkedFromPage, reachesPage, notReachesPage, reachableFromPage, notReachableFromPage

Sort modes

title, createdAt, updatedAt, outboundPageLinkCount, inboundPageLinkCount, pageReach, pageReachDistinct

Templates Commands

Template commands mirror their page counterparts. Key differences: templates can contain templateValue units, regular page IDs return NOT_TEMPLATE_PAGE errors, and the default blank template icon is "📋".

Read

READ_TEMPLATES

Same as READ_PAGES (page-ID mode only, no tabs mode). Template reads serialize templateValue units without resolved value fields.

Request
{ "type": "command", "requestId": "1", "cmd": "READ_TEMPLATES", "pageIds": ["TplPage1234567890AbC"] }
Write

CREATE_TEMPLATES

Same as CREATE_PAGES. Templates may freely use all three templateValue types. Default blank icon is "📋".

Request
{
  "type": "command", "requestId": "1",
  "cmd": "CREATE_TEMPLATES",
  "pages": [{
    "icon": "📋",
    "title": [{ "type": "templateValue", "valueType": "dateToday" }, { "type": "text", "text": " — Daily Note" }],
    "subtitle": [],
    "blocks": [{ "blockId": 0, "items": [{ "type": "text", "style": "#", "content": [{ "type": "text", "text": "Tasks" }] }] }]
  }]
}
Write

UPDATE_TEMPLATES

Same structure and options as UPDATE_PAGES.

Write

DELETE_TEMPLATES

Same as DELETE_PAGES, but no "last page" restriction (templates are separate from the required real-page set).

Request
{ "type": "command", "requestId": "1", "cmd": "DELETE_TEMPLATES", "pageIds": ["TplPage1234567890AbC"] }
Write

CREATE_FROM_TEMPLATE

Creates a regular page from a template and inserts a page link into a source block.

Request
{
  "type": "command", "requestId": "1",
  "cmd": "CREATE_FROM_TEMPLATE",
  "pageId": "AbcDef1234567890GhIj",
  "blockId": 0,
  "templateId": "TplPage1234567890AbC",
  "insertAtTop": false,
  "returnPage": true
}

Template values resolved at creation: nextNumber (increments from existing links), nextDay (advances one day), dateToday (current date).

Items Commands

Add or remove items from individual blocks without replacing the entire page.

Anchor + Offset: All push/pop commands use anchor ("top"/"bottom") and offset. top + 0 = before first item. bottom + 0 = after last item. Offsets are clamped automatically.
Write

PUSH_PAGE_ITEMS / PUSH_TEMPLATE_ITEMS

Inserts items into a block.

{
  "type": "command", "requestId": "1",
  "cmd": "PUSH_PAGE_ITEMS",
  "operations": [{
    "pageId": "AbcDef1234567890GhIj",
    "blockId": 0,
    "anchor": "bottom", "offset": 0,
    "items": [{ "type": "text", "style": "[ ]", "content": [{ "type": "text", "text": "New task" }] }]
  }]
}

Response includes insertedAt, totalItemCount, didReorderPageLinks, and the serialized block.

Write

POP_PAGE_ITEMS / POP_TEMPLATE_ITEMS

Removes items from a block. Optional expectedItemType validates the removal range.

{
  "type": "command", "requestId": "1",
  "cmd": "POP_PAGE_ITEMS",
  "operations": [{
    "pageId": "AbcDef1234567890GhIj",
    "blockId": 0,
    "anchor": "bottom", "offset": 0,
    "count": 2
  }]
}

Workspace Commands

Read

READ_WORKSPACE

Returns the current workspace layout with tab positions, scroll state, and linked refs expansion.

Write

WRITE_WORKSPACE

Atomically replaces the workspace layout.

ValidationRule
tabsNon-empty array
scrollLeftPx, scrollTopPxNon-negative integers
displayWidthPxInteger between 350 and 5000
pathMust resolve to existing pages. Template tabs require empty path.

Discovery Commands

Read

MAP

Builds a recursive page-link tree rooted at one page.

FieldTypeDefaultDescription
pageIdstringRoot page ID (required)
limitsnumber[] | nullnullPer-layer link budgets. null = auto (~200 pages). [] = leaf only.
subtitlebooleantrueInclude subtitle text
blockTextbooleantrueInclude text preview (max 100 chars)

"+" in response indicates truncated links at that block.

Read

ANCESTORS

Walks UP the page-link tree from a target page. The complement to MAP.

Same parameters as MAP but limits is always required (no auto mode). Response includes viaBlocks on each parent entry showing which blocks contain the link.

Read

ORIENTATION

High-level structural overview. Automatically chooses between hub mode (well-connected pages, >= 60% coverage) and listing mode (mostly disconnected pages).

Hub mode returns up to 3 hubs with MAP trees. Listing mode returns up to 200 pages with detail tiers based on page count.

Events Commands

Event

SUBSCRIBE

Subscribes to real-time event notifications. Categories: "pages", "project", "workspace", "files". Subscriptions are additive.

{ "type": "command", "requestId": "1", "cmd": "SUBSCRIBE", "categories": ["pages", "project", "workspace", "files"] }

Response includes activeCategories and current seq.

Event

UNSUBSCRIBE

Removes event subscriptions. Unsubscribing from a non-active category is a no-op.

Event System

All events include type, event, seq, instanceId, and timestamp (UNIX seconds). Exception: eviction omits seq, instanceId, and timestamp.

Debounce: User page edits: 300ms trailing, 1000ms max wait. Scroll events: 2000ms trailing. API mutations: delivered immediately. Debounce merging can produce seq gaps — this is expected.
Cache staleness: Events are mutation-backed, not output-backed. If page B's title changes and page A has a page link to B, page A's resolved pageLink.title changes but page A does not emit an event. Cached reads can go stale without a direct event for the dependent page.

Pages Events

All include source ("user", "api", "system") and requestId when API-triggered.

pages_created

Includes icon, serialized title, and sourceTemplateId (page ID or null).

pages_updated

Every changed region includes before/after diffs. Fields: role ("direct"/"cascade"), scope (changed regions), blockChanges with ops: created, deleted, updated, reordered.

pages_deleted

Includes icon and title at deletion time.

Project Events

project_opened, project_closed, concurrent_access_detected (with detectedBy: "workspace_poll", "api_server", or "both").

Workspace Events

workspace_tab_opened, workspace_tab_closed, workspace_tab_navigated, workspace_tab_resized, workspace_scrolled, workspace_changed (coarse fallback, emitted by WRITE_WORKSPACE).

Files Events

Subscribe with category files. files_changed fires for each change record produced by an active FILES_WATCH subscription.

{ "type": "event", "event": "files_changed",
  "seq": 99, "instanceId": "desk-main", "timestamp": 1712766500,
  "source": "system",
  "subscriptionId": "fwatch_...",
  "subpath": "inbox", "recursive": true,
  "changeType": "appeared",
  "path": ["recipes", "curry.png"],
  "movedFrom": null }

Fields: subscriptionId, subpath, recursive, changeType (appeared, disappeared, modified, moved, unknown, errored), path (array, relative to subpath), movedFrom (array or null).

Eviction Event

Always delivered regardless of subscriptions. Sent when another tab connects with the same gb_id.

Error Reference

Server-Level Errors

Arrive as type: "error" messages.

CodeDescription
INVALID_JSONMessage is not valid JSON
MISSING_REQUEST_IDCommand missing requestId or cmd
NO_INSTANCESNo GalaxyBrain instances connected
UNKNOWN_INSTANCESpecified instance ID not found
INSTANCE_REQUIREDMultiple instances connected; must specify instance
INSTANCE_DISCONNECTEDInstance disconnected while processing
PROTOCOL_MISMATCHBrowser app protocol version mismatch
UNKNOWN_MESSAGE_TYPEUnrecognized message type

PROTOCOL_MISMATCH errors include additional fields:

  • serverProtocolVersion — the server's protocol version (number)
  • clientProtocolVersion — the client's claimed protocol version (number or null)

Command-Level Errors

Arrive inside response messages as { ok: false, error, message }.

General
CodeDescription
NO_PROJECTProject-level command with no project open
CONFLICTreadVersion mismatch
INTERNAL_ERRORUnexpected exception
Parse and validation
CodeDescription
PARSE_ERRORInvalid payload shape or field types
INVALID_ICONIcon is not a valid emoji
INVALID_STYLEUnsupported text item style
NO_BLOCKSPage has zero blocks
NO_ITEMSBlock or push payload has zero items
INVALID_BLOCK_IDInvalid block ID
INVALID_VAR_IDInvalid variable ID
DUPLICATE_BLOCK_IDBlock ID duplicated within a page
DUPLICATE_VAR_IDVar ID duplicated or collides with existing
INVALID_LINK_ORDERlinkOrder doesn't match grammar
INVALID_TITLE_UNITTitle used unsupported unit type or styled text
INVALID_FORMULA_UNITFormula used unsupported unit type or styled text
EMPTY_TEXTText or URL field is empty
INVALID_META_REFMeta reference string is malformed
SELF_LINKPage link points to itself
INVALID_TEMPLATE_IDTemplate ID invalid or is a regular page
INVALID_TEMPLATE_VALUETemplate value type invalid
NO_UPDATESSurgical update has no actual changes
DUPLICATE_BLOCK_OPSame block in multiple surgical operations
BLOCK_ORDER_MISMATCHblockOrder doesn't match final block set
Images
CodeDescription
IMAGE_NOT_FOUNDimageId is malformed
IMAGE_FILE_NOT_FOUNDimageFile key missing from images map
IMAGE_AMBIGUOUSImage item has both imageId and imageFile
Page and template targeting
CodeDescription
PAGE_NOT_FOUNDPage or template not found
TEMPLATE_PAGERegular-page command sent to a template
NOT_TEMPLATE_PAGETemplate command sent to a regular page
BLOCK_NOT_FOUNDBlock ID doesn't exist
BLOCK_ALREADY_EXISTSInserted block ID already exists
LAST_PAGECannot delete the last remaining real page
NO_REMAINING_ITEMSPop would remove every item
UNEXPECTED_ITEM_TYPEexpectedItemType mismatch
Workspace
CodeDescription
NO_TABSEmpty or missing tabs array
INVALID_TAB_PATHTab path structurally invalid
Folder and demo lifecycle
CodeDescription
FOLDER_NOT_FOUNDRecent-folder ID doesn't exist
PERMISSION_REQUIREDBrowser requires user gesture for folder access
FOLDER_UNAVAILABLESaved folder handle no longer accessible
FOLDER_LOAD_FAILEDFolder could not be loaded
DEMO_LOAD_FAILEDDemo could not be loaded
OFFLINECommand unavailable in offline mode
File watching
CodeDescription
UNSUPPORTED_BROWSERBrowser does not expose FileSystemObserver