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.
https://api.wovea.aihttps://api-stg.wovea.aicurl 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.
| Plan | Requests / min |
|---|---|
| Free | 60 |
| Creator | 120 |
| Pro | 300 |
| Team | 300 |
Errors
Error Responses
All errors return JSON with an error string. HTTP status codes follow standard semantics.
| Status | Meaning |
|---|---|
400 Bad Request | Missing or invalid body parameter |
401 Unauthenticated | Token missing, expired, or revoked |
403 Forbidden | Token lacks the required scope |
404 Not Found | Resource does not exist or you don't own it |
429 Too Many Requests | Rate limit exceeded |
500 Internal Error | Something went wrong on our end |
{ "error": "Scope 'board:write' required" }Endpoints
Boards
Create, read, update, and delete your Brain Boards via the API.
Create a board
Creates a new Brain Board owned by the key's user.
Request body
namereqDisplay name for the board.
modeVocabulary mode. One of story campaign memory plan 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 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
boardIdreqThe board's unique identifier.
Query parameters
contexttrue 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 }
]
}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
nameNew display name for the board.
modeOne of story campaign memory plan 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 a board
Soft-deletes the board. Permanently removed after 24 hours. Only the board owner can delete.
Path parameters
boardIdreqThe 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.
Create a collection
Adds a typed collection to a board you own.
Request body
namereqDisplay name for the collection.
cardTypeOne 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" }
}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 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
boardIdreqThe board's unique identifier.
collectionIdreqThe collection's unique identifier.
Query parameters
contexttrue 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"
}
]
}
}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
boardIdreqThe board's unique identifier.
collectionIdreqThe collection's unique identifier.
Request body
fileIdA single file id to add. Provide this or fileIds.
fileIdsUp 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-..."]
}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
boardIdreqThe board's unique identifier.
collectionIdreqThe collection's unique identifier.
fileIdreqThe 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.
List assets
Query parameters
limitItems per page. Max 100, default 20.
offsetItems 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
}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)
urlreqPublic http/https URL to fetch.
nameDisplay name. Defaults to the last URL path segment.
collectionIdUUID 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"
}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-TypereqMIME type — e.g. image/jpeg, image/png, application/pdf.
X-File-NameDisplay name for the asset (e.g. photo.jpg).
X-Collection-IdUUID 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.
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
textNote content. Defaults to an empty string.
tintBackground colour. One of amber sky sage lilac. Defaults to amber.
xCanvas x position (pixels). Defaults to 0.
yCanvas y position (pixels). Defaults to 0.
pinnedPin 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.
Append an agent note
Creates a new note for the collection. Notes accumulate — each POST appends; they are never auto-replaced.
Request body
contentreqMarkdown 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"
}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].
Create a document
Creates a document and attaches it to the board. Pass collectionId to place it inside a specific collection.
Request body
namereqDocument title.
contentreqDocument body — plain text or Markdown.
mimeTypetext/plain (default) or text/markdown. Plain text is wrapped in <p> tags for the editor; Markdown is stored as-is.
collectionIdWhen 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.
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
boardIdreqThe board's unique identifier.
Request body
manifest.versionreqMust be the literal "1.0".
manifest.namereqKit / brand name.
manifest.colorsreq1–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.fontsreq1–4 items: { name, googleUrl?, role, weightHint? }. role is one of display body ui.
manifest.voicereqTone-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 the design kit
Returns the board's design kit manifest, or { "designKit": null } if none has been created yet.
Path parameters
boardIdreqThe 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."
}
}Endpoints
Custom Cards
Custom cards are user-defined card TYPES (card-schemas) and their instances (custom-cards) — the building blocks of a modeled system: Services, Datastores, whatever fields a type needs. Cards reference each other through card_ref fields and through links (connections), together forming a graph. A card-kit is a pre-built bundle of related card types (e.g. "Software Architecture") you can add to a board in one call instead of authoring each type by hand.
List available card kits
Board-agnostic catalog. Currently one kit — software-architecture (Service, Datastore, Queue / Topic, API Endpoint, wired by card_ref dependency fields).
curl https://api.wovea.ai/v1/card-kits \ -H "Authorization: Bearer wovea_sk_..."
Response · 200
{
"kits": [
{
"id": "software-architecture",
"name": "Software Architecture",
"description": "Model a system as a dependency graph...",
"icon": "🧩",
"kinds": [
{ "kind": "service", "name": "Service", "icon": "⚙️" },
{ "kind": "datastore", "name": "Datastore", "icon": "🗄️" },
{ "kind": "queue", "name": "Queue / Topic", "icon": "📨" },
{ "kind": "api", "name": "API Endpoint", "icon": "🔌" }
]
}
]
}Add a kit to a board
Instantiates the kit's card types as board-scoped card types. Idempotent per board — a kind already present is left untouched and reported back, not duplicated.
Path parameters
boardIdreqThe board's unique identifier.
Request body
kitIdreqA kit id from GET /v1/card-kits, e.g. "software-architecture".
curl -X POST https://api.wovea.ai/v1/boards/b1a2c3d4-.../card-kits \
-H "Authorization: Bearer wovea_sk_..." \
-H "Content-Type: application/json" \
-d '{ "kitId": "software-architecture" }'Response · 201
{
"kitId": "software-architecture",
"created": 4,
"schemas": [
{
"id": "142161ad-...", "kind": "service", "name": "Service", "icon": "⚙️",
"fields": [
{ "key": "name", "label": "Name", "type": "text", "role": "title", "required": true },
{ "key": "depends_on", "label": "Depends on", "type": "card_ref", "role": "body",
"maxItems": 16, "refKinds": ["service", "datastore", "queue", "api"] }
],
"display": { "layout": "stack", "accent": "primary", "width": 250, "...": "..." }
}
]
}List a board's card types
Every card type this board is entitled to use — its own, plus any added via a kit. Use a returned id as the schemaId when creating a card, and its fields to know what shape values should take.
Path parameters
boardIdreqThe board's unique identifier.
curl https://api.wovea.ai/v1/boards/b1a2c3d4-.../card-schemas \ -H "Authorization: Bearer wovea_sk_..."
Response · 200
{
"schemas": [
{ "id": "142161ad-...", "kind": "service", "name": "Service", "icon": "⚙️",
"fields": [ "..." ], "display": { "...": "..." } }
]
}Author a new card type
Creates a board-scoped card type from scratch — use this when no kit fits. Every type renders in "fields" mode (no raw HTML/CSS authoring via this API).
Path parameters
boardIdreqThe board's unique identifier.
Request body
kindreqStable lowercase slug, e.g. "service" — must match ^[a-z][a-z0-9_]*$, unique on this board.
namereqHuman name, e.g. "Service" (1–60 characters).
iconOptional emoji (max 8 characters).
fieldsreq1–48 field definitions: { key, label, type, role, required?, options?, placeholder?, maxItems?, refKinds? }. Exactly one field must have role: "title". type is one of text richtext number date url media select checkbox checklist rating color embed html calendar card_ref. role is one of title subtitle cover body meta none. For type: "card_ref", refKinds restricts which card kinds are valid targets — the field that turns a card set into a graph.
displayOptional presentation spec: { layout, cover, accent, density, badge, badgePosition, width, fieldOrder, hidden, fieldHeights, theme }. Numeric fields are clamped to safe ranges; unset fields take sane defaults.
curl -X POST https://api.wovea.ai/v1/boards/b1a2c3d4-.../card-schemas \
-H "Authorization: Bearer wovea_sk_..." \
-H "Content-Type: application/json" \
-d '{
"kind": "milestone",
"name": "Milestone",
"icon": "🚩",
"fields": [
{ "key": "name", "label": "Name", "type": "text", "role": "title", "required": true },
{ "key": "date", "label": "Date", "type": "date", "role": "subtitle" },
{ "key": "blocks", "label": "Blocks", "type": "card_ref", "role": "body", "refKinds": ["milestone"] }
]
}'Response · 201
{ "schema": { "id": "9a1b...", "kind": "milestone", "name": "Milestone", "icon": "🚩", "fields": [ "..." ], "display": { "...": "..." } } }Edit a card type
Edits live-propagate to every existing card of this type — only board-scoped types this board owns can be edited via this API.
Request body
nameNew name.
iconNew icon, or null to clear.
fieldsReplace the full field list (same shape as create).
displayReplace the display spec (same shape as create).
curl -X PATCH https://api.wovea.ai/v1/boards/b1a2c3d4-.../card-schemas/9a1b... \
-H "Authorization: Bearer wovea_sk_..." \
-H "Content-Type: application/json" \
-d '{ "name": "Key Milestone" }'Response · 200
{ "schema": { "id": "9a1b...", "name": "Key Milestone", "...": "..." } }Delete a card type
Existing card instances of this type survive — they keep their content and fall back to a generic renderer, never cascade-deleted.
curl -X DELETE https://api.wovea.ai/v1/boards/b1a2c3d4-.../card-schemas/9a1b... \ -H "Authorization: Bearer wovea_sk_..."
Response · 200
{ "ok": true }List a board's cards
Cards actually placed on this board (not just any card that references this board — placement is what makes a card "on" a board).
Query parameters
kindFilter to one card kind, e.g. ?kind=service.
curl https://api.wovea.ai/v1/boards/b1a2c3d4-.../custom-cards?kind=service \ -H "Authorization: Bearer wovea_sk_..."
Response · 200
{
"cards": [
{ "id": "273ed1...", "schemaId": "23b936...", "kind": "datastore",
"values": { "name": "Orders DB", "engine": "Postgres" },
"display": null, "cardType": null, "createdAt": "2026-01-01T00:00:00.000Z" }
]
}Create a card
Creates one card instance and places it on the canvas at (x, y). To reference another card (a card_ref field), create the target card first and use its returned id as { "cardId": "..." } — or an array of those for a multi-ref field.
Request body
schemaIdreqA card type id from GET .../card-schemas.
valuesField values keyed by field key. Loosely validated (matches the app's own permissive value model) — not checked per-field against the type's declared field types.
xreqCanvas x position.
yreqCanvas y position.
cardTypeOptional universal card-type tag.
displayOptional per-card override, layered over the type's own display (only the keys you set are overridden).
curl -X POST https://api.wovea.ai/v1/boards/b1a2c3d4-.../custom-cards \
-H "Authorization: Bearer wovea_sk_..." \
-H "Content-Type: application/json" \
-d '{
"schemaId": "142161ad-...",
"values": {
"name": "Billing Service",
"depends_on": [ { "cardId": "273ed1..." } ]
},
"x": 0, "y": 0
}'Response · 201
{
"card": { "id": "16ab92...", "schemaId": "142161ad-...", "kind": "service",
"values": { "name": "Billing Service", "depends_on": [ { "cardId": "273ed1..." } ] },
"display": null, "cardType": null, "createdAt": "2026-01-01T00:00:00.000Z" },
"canvasNodeId": "dd8961..."
}Get a card
curl https://api.wovea.ai/v1/boards/b1a2c3d4-.../custom-cards/16ab92... \ -H "Authorization: Bearer wovea_sk_..."
Response · 200
{ "card": { "id": "16ab92...", "schemaId": "142161ad-...", "kind": "service", "values": { "...": "..." }, "display": null, "cardType": null, "createdAt": "2026-01-01T00:00:00.000Z" } }Edit a card
Any subset of values, display, cardType — values replaces the whole values object (not a per-key merge).
curl -X PATCH https://api.wovea.ai/v1/boards/b1a2c3d4-.../custom-cards/16ab92... \
-H "Authorization: Bearer wovea_sk_..." \
-H "Content-Type: application/json" \
-d '{ "values": { "name": "Billing Service v2" } }'Response · 200
{ "card": { "id": "16ab92...", "values": { "name": "Billing Service v2" }, "...": "..." } }Remove a card from a board
Drops the card's placement and this board's connections to/from it. The card row itself is only deleted once it has no placement anywhere.
curl -X DELETE https://api.wovea.ai/v1/boards/b1a2c3d4-.../custom-cards/16ab92... \ -H "Authorization: Bearer wovea_sk_..."
Response · 200
{ "ok": true }Connect two board objects
Draws a typed connection between any two objects on the board — files, collections, thought notes, or custom cards. This is what turns a set of cards into a graph on the canvas. Creating a duplicate (same board/source/target) is a no-op that returns the existing link's id rather than erroring.
Request body
sourceTypereqOne of file collection thought custom.
sourceIdreqThe source object's id.
targetTypereqSame enum as sourceType.
targetIdreqThe target object's id.
relationOptional. One of supports contradicts raises answers tensions_with leads_to depends_on. Omit for an untyped connection.
labelOptional short label shown on the connection (max 120 characters).
curl -X POST https://api.wovea.ai/v1/boards/b1a2c3d4-.../links \
-H "Authorization: Bearer wovea_sk_..." \
-H "Content-Type: application/json" \
-d '{
"sourceType": "custom", "sourceId": "16ab92...",
"targetType": "custom", "targetId": "273ed1...",
"relation": "depends_on"
}'Response · 201
{ "id": "fe0332..." }Update a connection
Change the relation and/or label. Pass relation: null to make it untyped.
curl -X PATCH https://api.wovea.ai/v1/boards/b1a2c3d4-.../links/fe0332... \
-H "Authorization: Bearer wovea_sk_..." \
-H "Content-Type: application/json" \
-d '{ "relation": "leads_to" }'Response · 200
{ "updated": true }Delete a connection
There is no dedicated list endpoint — connections are returned as objectLinks in GET /v1/boards/:boardId, alongside customCards and cardSchemas for the same board.
curl -X DELETE https://api.wovea.ai/v1/boards/b1a2c3d4-.../links/fe0332... \ -H "Authorization: Bearer wovea_sk_..."
Response · 200
{ "deleted": true }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.
| Scope | Permits |
|---|---|
board:read | GET /v1/boards · GET /v1/boards/:boardId · GET .../assets · GET .../card-schemas · GET .../custom-cards |
board:write | POST /v1/boards · PATCH /v1/boards/:boardId · POST .../collections · POST .../notes · POST .../documents · POST .../assets/from-url · POST .../assets/upload · POST .../collections/:id/notes · POST/PATCH/DELETE .../card-kits · .../card-schemas · .../custom-cards · .../links |
board:delete | DELETE /v1/boards/:boardId |
Ready to build?
Generate your API key in Settings and start making requests.