Wovea Public API · v1

API Reference

The Wovea API lets you create and manage Brain Boards programmatically. Authenticate with a personal API key generated in Settings → Developer.

Authentication

API Keys

Pass your key in the Authorization header on every request. Keys are prefixed wovea_sk_ and scoped to specific operations. The plaintext token is shown once at creation — store it in a secret manager.

Productionhttps://api.wovea.ai
Staginghttps://api-stg.wovea.ai
curl https://api.wovea.ai/v1/boards \
  -H "Authorization: Bearer wovea_sk_<your-token>"
!

Tokens expire after 1 year by default. A 401 Unauthenticated response means the token is missing, expired, or revoked. Generate a new one in Settings → Developer.

Limits

Rate Limits

Limits are per API key per minute and vary by plan tier. Exceeded requests receive 429 Too Many Requests.

PlanRequests / min
Free60
Creator120
Pro300
Team300

Errors

Error Responses

All errors return JSON with an error string. HTTP status codes follow standard semantics.

StatusMeaning
400 Bad RequestMissing or invalid body parameter
401 UnauthenticatedToken missing, expired, or revoked
403 ForbiddenToken lacks the required scope
404 Not FoundResource does not exist or you don't own it
429 Too Many RequestsRate limit exceeded
500 Internal ErrorSomething went wrong on our end
{ "error": "Scope 'board:write' required" }

Endpoints

Boards

Create, read, update, and delete your Brain Boards via the API.

POST/v1/boardsboard:write

Create a board

Creates a new Brain Board owned by the key's user.

Request body

namereq
string

Display name for the board.

mode
string

Vocabulary mode. One of story campaign memory custom. Defaults to story.

curl -X POST https://api.wovea.ai/v1/boards \
  -H "Authorization: Bearer wovea_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "name": "Summer Campaign", "mode": "campaign" }'

Response · 201

{
  "board": {
    "id":        "b1a2c3d4-...",
    "name":      "Summer Campaign",
    "mode":      "campaign",
    "createdAt": "2026-06-30T12:00:00.000Z"
  }
}
GET/v1/boards/{boardId}board:read

Get full board context

Returns everything needed to reconstruct the board: metadata, every collection (with its member file ids and agent notes), every non-deleted file, thought notes, semantic object links, and canvas node positions. Only boards owned by the key's user are accessible. Not paginated — document bodies (private R2 content) are intentionally excluded from files; fetch those via the documents endpoint.

Path parameters

boardIdreq
uuid

The board's unique identifier.

Query parameters

context
boolean

true filters the response down to AI-consumable content only, dropping three item types that carry no AI signal: collections[].agentNotes (agent provenance text, not board content), thoughtNotes (person-to-person reminders), and any file whose "Board context" toggle is off (meta.aiContext === false— set in the app's file info panel; the file still exists as a visual, its body just isn't meant to feed AI). Default (omitted or any other value) returns everything, unfiltered.

curl https://api.wovea.ai/v1/boards/b1a2c3d4-... \
  -H "Authorization: Bearer wovea_sk_..."

# AI-consumption view — drops agent notes, thought notes, and
# context-toggled-off files
curl "https://api.wovea.ai/v1/boards/b1a2c3d4-...?context=true" \
  -H "Authorization: Bearer wovea_sk_..."

Response · 200

{
  "board": {
    "id": "b1a2c3d4-...", "name": "Summer Campaign", "mode": "campaign",
    "aiEnabled": true, "customCardLabels": null, "canvasMode": false,
    "createdAt": "2026-06-30T12:00:00.000Z", "updatedAt": "2026-06-30T12:00:00.000Z"
  },
  "collections": [
    {
      "id": "c1...", "name": "The Big Idea", "cardType": "core_concept", "layout": "grid",
      "meta": { "description": "...", "hideAgentNotes": false },
      "fileIds": ["f1...", "f2..."],
      "agentNotes": ["Replay test — confirming notes persist."]
    }
  ],
  "files": [
    {
      "id": "f1...", "name": "hero.jpg", "folder": "media", "mimeType": "image/jpeg",
      "url": "https://images.wovea.ai/...", "width": 1600, "height": 900, "size": 240931,
      "title": null, "cardType": null, "tags": null,
      "muxAssetId": null, "muxPlaybackId": null, "videoStatus": null,
      "createdAt": "2026-06-30T12:00:00.000Z"
    }
  ],
  "thoughtNotes": [
    { "id": "n1...", "text": "Remember to...", "tint": "amber", "pinned": false,
      "rotation": -1.8, "authorName": "Lamont", "createdAt": "2026-06-30T12:00:00.000Z" }
  ],
  "objectLinks": [
    { "sourceType": "file", "sourceId": "f1...", "targetType": "file", "targetId": "f2...",
      "label": null, "relation": "supports" }
  ],
  "canvasNodes": [
    { "nodeType": "collection", "objectId": "c1...", "x": 120, "y": 40,
      "width": 392, "height": 320, "zIndex": 0 }
  ]
}
PATCH/v1/boards/{boardId}board:write

Update a board

Update the board's name or vocabulary mode. At least one field required. Switching to custom seeds labels from the story vocabulary.

Request body

name
string

New display name for the board.

mode
string

One of story campaign memory custom.

curl -X PATCH https://api.wovea.ai/v1/boards/b1a2c3d4-... \
  -H "Authorization: Bearer wovea_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "mode": "custom" }'

Response · 200

{
  "board": { "id": "b1a2c3d4-...", "name": "Summer Campaign", "mode": "custom" }
}
DELETE/v1/boards/{boardId}board:delete

Delete a board

Soft-deletes the board. Permanently removed after 24 hours. Only the board owner can delete.

Path parameters

boardIdreq
uuid

The board's unique identifier.

curl -X DELETE https://api.wovea.ai/v1/boards/b1a2c3d4-... \
  -H "Authorization: Bearer wovea_sk_..."

Response · 200

{ "ok": true }

Endpoints

Collections

Scaffold typed collections onto a board. Use the batch variant to create all collections in one request.

POST/v1/boards/{boardId}/collectionsboard:write

Create a collection

Adds a typed collection to a board you own.

Request body

namereq
string

Display name for the collection.

cardType
string

One of: core_concept audience voice expression evidence conflict absence inquiry context. Omit for untyped.

curl -X POST https://api.wovea.ai/v1/boards/b1a2c3d4-.../collections \
  -H "Authorization: Bearer wovea_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "name": "Key Messages", "cardType": "core_concept" }'

Response · 201

{
  "collection": { "id": "c1a2...", "name": "Key Messages", "cardType": "core_concept" }
}
POST/v1/boards/{boardId}/collectionsboard:write

Create collections (batch)

Scaffold multiple collections in a single request by passing { collections: [...] }" instead of a single object.

curl -X POST https://api.wovea.ai/v1/boards/b1a2c3d4-.../collections \
  -H "Authorization: Bearer wovea_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "collections": [
      { "name": "Event overview", "cardType": "core_concept" },
      { "name": "Run of show",    "cardType": "context"      },
      { "name": "Speakers",       "cardType": "audience"     }
    ]
  }'

Response · 201

{
  "collections": [
    { "id": "c1a2...", "name": "Event overview", "cardType": "core_concept" },
    { "id": "c2b3...", "name": "Run of show",    "cardType": "context"      },
    { "id": "c3c4...", "name": "Speakers",       "cardType": "audience"     }
  ]
}
GET/v1/boards/{boardId}/collections/{collectionId}board:read

Get a collection

Returns one collection with its full member files (not just ids) and agent notes. Use this when you only need a single collection's content rather than the whole board via GET /v1/boards/{boardId}.

Path parameters

boardIdreq
uuid

The board's unique identifier.

collectionIdreq
uuid

The collection's unique identifier.

Query parameters

context
boolean

true drops agentNotes (provenance text) and any file whose"Board context" toggle is off (meta.aiContext === false) — same semantics as GET /v1/boards/{boardId}?context=true. Default returns everything.

curl https://api.wovea.ai/v1/boards/b1a2c3d4-.../collections/c1a2c3d4-... \
  -H "Authorization: Bearer wovea_sk_..."

Response · 200

{
  "collection": {
    "id": "c1a2...", "name": "Key Messages", "cardType": "core_concept", "layout": "grid",
    "meta": { "description": "...", "hideAgentNotes": false },
    "agentNotes": ["Replay test — confirming notes persist."],
    "files": [
      {
        "id": "f1...", "name": "hero.jpg", "folder": "media", "mimeType": "image/jpeg",
        "url": "https://images.wovea.ai/...", "width": 1600, "height": 900, "size": 240931,
        "title": null, "cardType": null, "tags": null,
        "muxAssetId": null, "muxPlaybackId": null, "videoStatus": null,
        "createdAt": "2026-06-30T12:00:00.000Z"
      }
    ]
  }
}
POST/v1/boards/{boardId}/collections/{collectionId}/filesboard:write

Add file(s) to a collection

Attaches an existing board file to a collection — useful for a file that's currently uncollected, or already sitting in a different collection. This is additive: a file can belong to multiple collections at once (the in-app UI is a per-collection checkbox, not an exclusive move), so this never removes any other membership the file already has. Idempotent — adding a file already in the collection is a no-op for it.

Path parameters

boardIdreq
uuid

The board's unique identifier.

collectionIdreq
uuid

The collection's unique identifier.

Request body

fileId
string

A single file id to add. Provide this or fileIds.

fileIds
array

Up to 100 file ids to add in one call. Provide this or fileId.

curl -X POST https://api.wovea.ai/v1/boards/b1a2c3d4-.../collections/c1a2c3d4-.../files \
  -H "Authorization: Bearer wovea_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "fileId": "f1a2b3c4-..." }'

Response · 201

{
  "collectionId": "c1a2c3d4-...",
  "fileIds": ["f1a2b3c4-..."]
}
DELETE/v1/boards/{boardId}/collections/{collectionId}/files/{fileId}board:write

Remove a file from a collection

Drops the file from this collection only — the file itself is untouched and stays on the board (and in any other collection it belongs to), same as removing a file from a collection in the app.

Path parameters

boardIdreq
uuid

The board's unique identifier.

collectionIdreq
uuid

The collection's unique identifier.

fileIdreq
uuid

The file's unique identifier.

curl -X DELETE https://api.wovea.ai/v1/boards/b1a2c3d4-.../collections/c1a2c3d4-.../files/f1a2b3c4-... \
  -H "Authorization: Bearer wovea_sk_..."

Response · 200

{ "ok": true }

Endpoints

Assets

List, import, and upload the images, videos, and PDFs attached to a board. Listing is newest-first and paginated.

GET/v1/boards/{boardId}/assetsboard:read

List assets

Query parameters

limit
number

Items per page. Max 100, default 20.

offset
number

Items to skip. Default 0.

curl "https://api.wovea.ai/v1/boards/b1a2c3d4-.../assets?limit=20&offset=0" \
  -H "Authorization: Bearer wovea_sk_..."

Response · 200

{
  "assets": [
    {
      "id":            "f1a2b3c4-...",
      "name":          "hero.jpg",
      "mimeType":      "image/jpeg",
      "url":           "https://images.wovea.ai/tapestry/users/.../hero.jpg",
      "width":         3840,
      "height":        2160,
      "size":          2097152,
      "title":         "Golden hour rooftop shoot",
      "muxAssetId":    null,
      "muxPlaybackId": null,
      "videoStatus":   null,
      "createdAt":     "2026-06-30T12:00:00.000Z"
    },
    {
      "id":            "f2b3c4d5-...",
      "name":          "intro.mp4",
      "mimeType":      "video/mp4",
      "url":           null,
      "width":         1920,
      "height":        1080,
      "size":          null,
      "title":         null,
      "muxAssetId":    "abc123xyz",
      "muxPlaybackId": "playback456",
      "videoStatus":   "ready",
      "createdAt":     "2026-06-29T09:00:00.000Z"
    }
  ],
  "total":  42,
  "limit":  20,
  "offset": 0
}
POST/v1/boards/{boardId}/assets/from-urlboard:write

Import asset from URL

Fetches a remote image, PDF, or video URL and stores it on the board. Useful for AI agents that find a web resource and want to pin it. HEIC is not supported — convert to JPEG/PNG first.

Body (JSON)

urlreq
string

Public http/https URL to fetch.

name
string

Display name. Defaults to the last URL path segment.

collectionId
string

UUID of a collection on this board to add the asset to.

curl -X POST "https://api.wovea.ai/v1/boards/b1a2c3d4-.../assets/from-url" \
  -H "Authorization: Bearer wovea_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/photo.jpg","name":"Inspiration photo"}'

Response · 201

{
  "id":           "f1a2b3c4-...",
  "name":         "Inspiration photo",
  "mimeType":     "image/jpeg",
  "url":          "https://images.wovea.ai/tapestry/users/.../media/f1a2b3c4-...",
  "size":         204800,
  "boardId":      "b1a2c3d4-...",
  "collectionId": null,
  "createdAt":    "2026-06-30T12:00:00.000Z"
}
POST/v1/boards/{boardId}/assets/uploadboard:write

Upload file bytes

POST raw file bytes as the request body. Set Content-Type to the file's MIME type. Suitable for AI agents that generate or hold file bytes locally. HEIC is not supported.

Headers

Content-Typereq
string

MIME type — e.g. image/jpeg, image/png, application/pdf.

X-File-Name
string

Display name for the asset (e.g. photo.jpg).

X-Collection-Id
string

UUID of a collection on this board to add the asset to.

curl -X POST "https://api.wovea.ai/v1/boards/b1a2c3d4-.../assets/upload" \
  -H "Authorization: Bearer wovea_sk_..." \
  -H "Content-Type: image/jpeg" \
  -H "X-File-Name: mood-shot.jpg" \
  --data-binary @/path/to/photo.jpg

Response · 201

{
  "id":           "f1a2b3c4-...",
  "name":         "mood-shot.jpg",
  "mimeType":     "image/jpeg",
  "url":          "https://images.wovea.ai/tapestry/users/.../media/f1a2b3c4-...",
  "size":         409600,
  "boardId":      "b1a2c3d4-...",
  "collectionId": null,
  "createdAt":    "2026-06-30T12:00:00.000Z"
}

Endpoints

Thought Notes

Add sticky-note style thought cards to a board. Notes appear on the Spatial canvas at the given coordinates.

POST/v1/boards/{boardId}/notesboard:write

Create a thought note

Creates a note and places it on the board canvas. All fields are optional — an empty body creates a blank amber note at (0, 0).

Request body

text
string

Note content. Defaults to an empty string.

tint
string

Background colour. One of amber sky sage lilac. Defaults to amber.

x
number

Canvas x position (pixels). Defaults to 0.

y
number

Canvas y position (pixels). Defaults to 0.

pinned
boolean

Pin the note so it can't be moved. Defaults to false.

curl -X POST https://api.wovea.ai/v1/boards/b1a2c3d4-.../notes \
  -H "Authorization: Bearer wovea_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "text": "Don\'t forget the hero image", "tint": "sky", "x": 240, "y": 160 }'

Response · 201

{
  "id":           "n1a2b3c4-...",
  "canvasNodeId": "cn1a2b3c-...",
  "text":         "Don't forget the hero image",
  "tint":         "sky",
  "pinned":       false,
  "x":            240,
  "y":            160,
  "createdAt":    "2026-06-30T12:00:00.000Z"
}

Endpoints

Collection Agent Notes

Append markdown notes to a specific collection. Notes are agent-authored only — the UI renders them as a semi-transparent watermark behind the collection's card grid so the agent can surface context to the user without polluting the AI context window. Users can hide the watermark per collection via the collection info panel.

POST/v1/boards/{boardId}/collections/{collectionId}/notesboard:write

Append an agent note

Creates a new note for the collection. Notes accumulate — each POST appends; they are never auto-replaced.

Request body

contentreq
string

Markdown note content. Shown as watermark text in the collection body.

curl -X POST "https://api.wovea.ai/v1/boards/b1a2c3d4-.../collections/c1a2b3c4-.../notes" \
  -H "Authorization: Bearer wovea_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "content": "These shots all lean warm — consider a cooler hero for contrast." }'

Response · 201

{
  "id":           "n1a2b3c4-...",
  "collectionId": "c1a2b3c4-...",
  "content":      "These shots all lean warm — consider a cooler hero for contrast.",
  "createdAt":    "2026-06-30T12:00:00.000Z"
}
GET/v1/boards/{boardId}/collections/{collectionId}/notesboard:read

List agent notes

Returns all agent notes for the collection, oldest first. Useful for agents that want to avoid duplicating prior observations.

curl "https://api.wovea.ai/v1/boards/b1a2c3d4-.../collections/c1a2b3c4-.../notes" \
  -H "Authorization: Bearer wovea_sk_..."

Response · 200

{
  "notes": [
    {
      "id":        "n1a2b3c4-...",
      "content":   "These shots all lean warm — consider a cooler hero for contrast.",
      "createdAt": "2026-06-30T12:00:00.000Z"
    }
  ]
}

Endpoints

Documents

Create plain-text or Markdown documents on a board. Documents are stored in a private R2 bucket and are immediately openable in the Wovea editor at /editor/[id].

POST/v1/boards/{boardId}/documentsboard:write

Create a document

Creates a document and attaches it to the board. Pass collectionId to place it inside a specific collection.

Request body

namereq
string

Document title.

contentreq
string

Document body — plain text or Markdown.

mimeType
string

text/plain (default) or text/markdown. Plain text is wrapped in <p> tags for the editor; Markdown is stored as-is.

collectionId
uuid

When provided, the document is also added to this collection.

curl -X POST https://api.wovea.ai/v1/boards/b1a2c3d4-.../documents \
  -H "Authorization: Bearer wovea_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name":     "Creative Brief",
    "content":  "# Objective\n\nLaunch the summer campaign across three channels.",
    "mimeType": "text/markdown"
  }'

Response · 201

{
  "id":           "f1a2b3c4-...",
  "name":         "Creative Brief",
  "mimeType":     "text/markdown",
  "boardId":      "b1a2c3d4-...",
  "collectionId": null,
  "createdAt":    "2026-06-30T12:00:00.000Z"
}

Endpoints

Design Kit

A design kit is a brand-identity manifest — colors, fonts, and voice — attached to a board. It renders as a special collection (name "Design Kit") with a card per color/font plus a "Brand Voice" note, and is what buildOutputBriefing falls back to when generating Collection Outputs. Creating a kit always replaces any existing one on the board — there's only one design kit per board.

POST/v1/boards/{boardId}/design-kitboard:write

Create or replace the design kit

Persists a manifest to the board. Any existing design kit on the board is replaced — only the design kit's own generated cards are removed, never other files. Logo upload isn't supported via this endpoint; attach a logo image separately via the assets API if needed.

Path parameters

boardIdreq
uuid

The board's unique identifier.

Request body

manifest.versionreq
string

Must be the literal "1.0".

manifest.namereq
string

Kit / brand name.

manifest.colorsreq
array

1–10 items: { name, hex, role }. hex must be a 6-digit hex color (e.g. #FF7A00). role is one of primary secondary accent background text other.

manifest.fontsreq
array

1–4 items: { name, googleUrl?, role, weightHint? }. role is one of display body ui.

manifest.voicereq
string

Tone-of-voice description, 1–3 sentences.

curl -X POST https://api.wovea.ai/v1/boards/b1a2c3d4-.../design-kit \
  -H "Authorization: Bearer wovea_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "manifest": {
      "version": "1.0",
      "name": "Summer Campaign",
      "colors": [
        { "name": "Primary Orange", "hex": "#FF7A00", "role": "primary" },
        { "name": "Deep Teal",      "hex": "#004B66", "role": "accent"  }
      ],
      "fonts": [
        { "name": "Spectral", "role": "display", "weightHint": "600" },
        { "name": "DM Sans",  "role": "body",    "weightHint": "400" }
      ],
      "voice": "Warm, direct, a little irreverent."
    }
  }'

Response · 201

{
  "collectionId": "c1a2...",
  "manifest": {
    "version": "1.0", "name": "Summer Campaign",
    "colors": [
      { "name": "Primary Orange", "hex": "#FF7A00", "role": "primary" },
      { "name": "Deep Teal",      "hex": "#004B66", "role": "accent"  }
    ],
    "fonts": [
      { "name": "Spectral", "role": "display", "weightHint": "600" },
      { "name": "DM Sans",  "role": "body",    "weightHint": "400" }
    ],
    "voice": "Warm, direct, a little irreverent."
  }
}
GET/v1/boards/{boardId}/design-kitboard:read

Get the design kit

Returns the board's design kit manifest, or { "designKit": null } if none has been created yet.

Path parameters

boardIdreq
uuid

The board's unique identifier.

curl https://api.wovea.ai/v1/boards/b1a2c3d4-.../design-kit \
  -H "Authorization: Bearer wovea_sk_..."

Response · 200

{
  "collectionId": "c1a2...",
  "designKit": {
    "version": "1.0", "name": "Summer Campaign",
    "colors": [ { "name": "Primary Orange", "hex": "#FF7A00", "role": "primary" } ],
    "fonts":  [ { "name": "Spectral", "role": "display", "weightHint": "600" } ],
    "voice": "Warm, direct, a little irreverent."
  }
}

Reference

Scopes

Each API key is granted one or more scopes at creation time. Scopes cannot be added after creation — revoke and create a new key instead.

ScopePermits
board:readGET /v1/boards · GET /v1/boards/:boardId · GET .../assets
board:writePOST /v1/boards · PATCH /v1/boards/:boardId · POST .../collections · POST .../notes · POST .../documents · POST .../assets/from-url · POST .../assets/upload · POST .../collections/:id/notes
board:deleteDELETE /v1/boards/:boardId

Ready to build?

Generate your API key in Settings and start making requests.

Get your API key →