Canvases
Read canvases and apply revision-safe graph operations.
Base URL: https://app.gavana.ai/api/canvas-agent/v1 · Authentication: HTTP bearer, token format cba_<token-id>.<secret>
Operations
| Operation | Purpose | Scopes |
|---|---|---|
GET /canvases | List accessible canvases | canvas:read |
POST /canvases | Create a canvas or resolve Agent Canvas | canvas:read, canvas:write |
GET /canvases/{canvasId} | Get a complete canvas and its current revision | canvas:read |
GET /canvases/{canvasId}/nodes/{nodeId} | Get one node with incoming and outgoing connections | canvas:read |
POST /canvases/{canvasId}/operations | Validate or apply one revision-safe operation batch | canvas:read |
GET /canvases/{canvasId}/render | Render a canvas as SVG | canvas:read |
GET /canvases
List accessible canvases
Owned and shared canvases are merged and ordered by updatedAt descending, then handle descending.
- Operation id:
listCanvases - Scopes: Requires
canvas:read. - CLI equivalent:
gavana canvas list
Parameters
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
limit | query | integer | No | Maximum number of canvases in this page. Canvas pages are capped at 25 because summaries are derived from complete graph documents. Default 25. min 1, max 25. |
cursor | query | string | No | Opaque continuation cursor from page.nextCursor. Reuse it with the same list filters. max length 8192. |
Responses
| Status | Payload | Meaning |
|---|---|---|
200 | object | Accessible canvas summaries. |
default | ErrorResponse | A stable machine-readable error. The response always includes X-Request-ID. |
200 response body
| Field | Type | Required | Notes |
|---|---|---|---|
canvases | array of CanvasSummary | Yes | — |
canvases[].id | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
canvases[].canvasType | string | No | One of agent. |
canvases[].handle | string | Yes | Pattern ^canvas:. |
canvases[].ownerUid | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
canvases[].title | string | Yes | — |
canvases[].createdAt | string (date-time) | Yes | — |
canvases[].updatedAt | string (date-time) | Yes | — |
canvases[].revision | string | Yes | — |
canvases[].accessRole | string | No | One of owner, editor, viewer. |
canvases[].nodeCount | integer | Yes | min 0. |
canvases[].connectionCount | integer | Yes | min 0. |
page | CursorPage | Yes | — |
page.limit | integer | Yes | min 1, max 200. |
page.hasMore | boolean | Yes | — |
page.nextCursor | string | null | Yes | max length 8192. |
Example
curl "https://app.gavana.ai/api/canvas-agent/v1/canvases" \
-H "Authorization: Bearer $GAVANA_AGENT_TOKEN"POST /canvases
Create a canvas or resolve Agent Canvas
- Operation id:
createCanvas - Scopes: Requires
canvas:read+canvas:write. - CLI equivalent:
gavana canvas create
Request body (application/json, required)
| Field | Type | Required | Notes |
|---|---|---|---|
id | string | No | Pattern ^[A-Za-z0-9_-]{1,180}$. |
title | string | No | max length 160. |
canvasType | string | No | One of agent. |
destination | string | No | One of agent-canvas. |
Responses
| Status | Payload | Meaning |
|---|---|---|
201 | CanvasResponse | A complete canvas with its current revision. |
default | ErrorResponse | A stable machine-readable error. The response always includes X-Request-ID. |
201 response body
| Field | Type | Required | Notes |
|---|---|---|---|
canvas | Canvas | Yes | — |
canvas.id | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
canvas.canvasType | string | No | One of agent. |
canvas.handle | string | Yes | Pattern ^canvas:[A-Za-z0-9_-]+:[A-Za-z0-9_-]+$. |
canvas.ownerUid | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
canvas.title | string | Yes | max length 160. |
canvas.createdAt | string (date-time) | Yes | — |
canvas.updatedAt | string (date-time) | Yes | — |
canvas.revision | string | Yes | min length 1. |
canvas.nodes | array of CanvasNode | Yes | — |
canvas.nodes[].id | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
canvas.nodes[].handle | string | Yes | Pattern ^node:. |
canvas.nodes[].type | string | Yes | One of image, video, text, sticky. |
canvas.nodes[].title | string | Yes | — |
canvas.nodes[].position | Position | Yes | — |
canvas.nodes[].position.x | number | Yes | min -10000000, max 10000000. |
canvas.nodes[].position.y | number | Yes | min -10000000, max 10000000. |
canvas.nodes[].width | number | Yes | — |
canvas.nodes[].height | number | Yes | — |
canvas.nodes[].metadata | object | No | — |
canvas.nodes[].asset | string | No | Pattern ^asset:. |
canvas.connections | array of CanvasConnection | Yes | — |
canvas.connections[].id | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
canvas.connections[].handle | string | Yes | Pattern ^connection:. |
canvas.connections[].fromNodeId | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
canvas.connections[].toNodeId | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
canvas.connections[].from | string | Yes | Pattern ^node:. |
canvas.connections[].to | string | Yes | Pattern ^node:. |
activity | array of object | Yes | — |
Example
curl -X POST "https://app.gavana.ai/api/canvas-agent/v1/canvases" \
-H "Authorization: Bearer $GAVANA_AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'GET /canvases/{canvasId}
Get a complete canvas and its current revision
- Operation id:
getCanvas - Scopes: Requires
canvas:read. - CLI equivalent:
gavana canvas get
Parameters
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
canvasId | path | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
ownerUid | query | string | No | Identifies the owner of a shared canvas or asset. Owner-qualified handles let the CLI supply it automatically. Pattern ^[A-Za-z0-9_-]{1,180}$. |
Responses
| Status | Payload | Meaning |
|---|---|---|
200 | CanvasResponse | A complete canvas with its current revision. |
default | ErrorResponse | A stable machine-readable error. The response always includes X-Request-ID. |
200 response body
| Field | Type | Required | Notes |
|---|---|---|---|
canvas | Canvas | Yes | — |
canvas.id | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
canvas.canvasType | string | No | One of agent. |
canvas.handle | string | Yes | Pattern ^canvas:[A-Za-z0-9_-]+:[A-Za-z0-9_-]+$. |
canvas.ownerUid | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
canvas.title | string | Yes | max length 160. |
canvas.createdAt | string (date-time) | Yes | — |
canvas.updatedAt | string (date-time) | Yes | — |
canvas.revision | string | Yes | min length 1. |
canvas.nodes | array of CanvasNode | Yes | — |
canvas.nodes[].id | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
canvas.nodes[].handle | string | Yes | Pattern ^node:. |
canvas.nodes[].type | string | Yes | One of image, video, text, sticky. |
canvas.nodes[].title | string | Yes | — |
canvas.nodes[].position | Position | Yes | — |
canvas.nodes[].position.x | number | Yes | min -10000000, max 10000000. |
canvas.nodes[].position.y | number | Yes | min -10000000, max 10000000. |
canvas.nodes[].width | number | Yes | — |
canvas.nodes[].height | number | Yes | — |
canvas.nodes[].metadata | object | No | — |
canvas.nodes[].asset | string | No | Pattern ^asset:. |
canvas.connections | array of CanvasConnection | Yes | — |
canvas.connections[].id | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
canvas.connections[].handle | string | Yes | Pattern ^connection:. |
canvas.connections[].fromNodeId | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
canvas.connections[].toNodeId | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
canvas.connections[].from | string | Yes | Pattern ^node:. |
canvas.connections[].to | string | Yes | Pattern ^node:. |
activity | array of object | Yes | — |
Example
curl "https://app.gavana.ai/api/canvas-agent/v1/canvases/canvas:8f2c1d40-9a77-4c2e-9c11-2b0a5f6d7e31" \
-H "Authorization: Bearer $GAVANA_AGENT_TOKEN"GET /canvases/{canvasId}/nodes/{nodeId}
Get one node with incoming and outgoing connections
- Operation id:
getCanvasNode - Scopes: Requires
canvas:read. - CLI equivalent:
gavana node get
Parameters
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
canvasId | path | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
nodeId | path | string | Yes | Pattern ^(?:node:)?[A-Za-z0-9_-]{1,180}$. |
ownerUid | query | string | No | Identifies the owner of a shared canvas or asset. Owner-qualified handles let the CLI supply it automatically. Pattern ^[A-Za-z0-9_-]{1,180}$. |
Responses
| Status | Payload | Meaning |
|---|---|---|
200 | object | A successful JSON response. |
default | ErrorResponse | A stable machine-readable error. The response always includes X-Request-ID. |
Example
curl "https://app.gavana.ai/api/canvas-agent/v1/canvases/canvas:8f2c1d40-9a77-4c2e-9c11-2b0a5f6d7e31/nodes/node:1f4b8c9a" \
-H "Authorization: Bearer $GAVANA_AGENT_TOKEN"POST /canvases/{canvasId}/operations
Validate or apply one revision-safe operation batch
Set validateOnly to true to run the canonical operation engine and graph validator without writing, consuming an idempotency key, or requiring canvas:write. Validation includes completionReview for overlap, full-frame Section containment, lineage, and output delivery; do not claim Done while completionReview.doneClaimAllowed is false. For mutation, read the canvas first, pass its revision as baseRevision, and provide an idempotencyKey. A 409 response includes the current revision when another actor changed the canvas.
- Operation id:
applyCanvasOperations - Scopes: Requires
canvas:read. When validateOnly is not true, also requirescanvas:write. - CLI equivalent:
gavana canvas apply
Parameters
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
canvasId | path | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
ownerUid | query | string | No | Identifies the owner of a shared canvas or asset. Owner-qualified handles let the CLI supply it automatically. Pattern ^[A-Za-z0-9_-]{1,180}$. |
Request body (application/json, required)
| Field | Type | Required | Notes |
|---|---|---|---|
baseRevision | string | No | min length 1, max length 200. |
idempotencyKey | string | No | Required for mutation. Ignored and not consumed when validateOnly is true. min length 8, max length 200. |
validateOnly | boolean | No | Run the canonical operation engine and graph validator without persisting any change. Default false. |
force | boolean | No | Apply even when the proposed agent write has blocking error-severity validation findings. New agent-owned overlap and full-frame Section overflow are blocked; historical user-canvas findings remain advisory. Without force, a blocking write is rejected with 422 and nothing is written. Default false. |
operations | array of CanvasOperation | Yes | min items 1, max items 200. |
Validation rules the service enforces across these fields:
- Non-empty content is writable only on text and sticky nodes.
Responses
| Status | Payload | Meaning |
|---|---|---|
200 | CanvasValidationResponse | MutationResponse | A canonical read-only validation result when validateOnly is true, otherwise a revision-safe mutation result. |
409 | ErrorResponse | A stable machine-readable error. The response always includes X-Request-ID. |
default | ErrorResponse | A stable machine-readable error. The response always includes X-Request-ID. |
200 response body
| Field | Type | Required | Notes |
|---|---|---|---|
(CanvasValidationResponse).canvas | CanvasSummary | Yes | — |
(CanvasValidationResponse).canvas.id | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
(CanvasValidationResponse).canvas.canvasType | string | No | One of agent. |
(CanvasValidationResponse).canvas.handle | string | Yes | Pattern ^canvas:. |
(CanvasValidationResponse).canvas.ownerUid | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
(CanvasValidationResponse).canvas.title | string | Yes | — |
(CanvasValidationResponse).canvas.createdAt | string (date-time) | Yes | — |
(CanvasValidationResponse).canvas.updatedAt | string (date-time) | Yes | — |
(CanvasValidationResponse).canvas.revision | string | Yes | — |
(CanvasValidationResponse).canvas.accessRole | string | No | One of owner, editor, viewer. |
(CanvasValidationResponse).canvas.nodeCount | integer | Yes | min 0. |
(CanvasValidationResponse).canvas.connectionCount | integer | Yes | min 0. |
(CanvasValidationResponse).proposal | object | Yes | — |
(CanvasValidationResponse).destructiveImpact | object | Yes | — |
(CanvasValidationResponse).validation | object | Yes | — |
(CanvasValidationResponse).validation.completionReview | CanvasCompletionReview | No | — |
(CanvasValidationResponse).validation.completionReview.status | string | Yes | One of ready, needs-review, blocked. |
(CanvasValidationResponse).validation.completionReview.doneClaimAllowed | boolean | Yes | False while agent-owned spatial or lineage findings remain, or a generated output is pending, failed, or non-durable. |
(CanvasValidationResponse).validation.completionReview.instruction | string | Yes | — |
(CanvasValidationResponse).validation.completionReview.outputs | CanvasCompletionOutputs | Yes | Generated-output count and durable, pending, and failed breakdown. |
(CanvasValidationResponse).validation.completionReview.outputs.count | integer | Yes | min 0. |
(CanvasValidationResponse).validation.completionReview.outputs.durableCount | integer | Yes | min 0. |
(CanvasValidationResponse).validation.completionReview.outputs.pendingCount | integer | Yes | min 0. |
(CanvasValidationResponse).validation.completionReview.outputs.failedCount | integer | Yes | min 0. |
(CanvasValidationResponse).validation.completionReview.outputs.handles | array of string | Yes | — |
(CanvasValidationResponse).validation.completionReview.overlap | CanvasCompletionFindingArea | Yes | — |
(CanvasValidationResponse).validation.completionReview.overlap.status | string | Yes | One of clear, needs-review, blocked. |
(CanvasValidationResponse).validation.completionReview.overlap.findingCodes | array of string | Yes | — |
(CanvasValidationResponse).validation.completionReview.overlap.nodeHandles | array of string | Yes | — |
(CanvasValidationResponse).validation.completionReview.overlap.connectionHandles | array of string | Yes | — |
(CanvasValidationResponse).validation.completionReview.containment | CanvasCompletionFindingArea | Yes | — |
(CanvasValidationResponse).validation.completionReview.containment.status | string | Yes | One of clear, needs-review, blocked. |
(CanvasValidationResponse).validation.completionReview.containment.findingCodes | array of string | Yes | — |
(CanvasValidationResponse).validation.completionReview.containment.nodeHandles | array of string | Yes | — |
(CanvasValidationResponse).validation.completionReview.containment.connectionHandles | array of string | Yes | — |
(CanvasValidationResponse).validation.completionReview.referenceLineage | CanvasCompletionFindingArea | Yes | — |
(CanvasValidationResponse).validation.completionReview.referenceLineage.status | string | Yes | One of clear, needs-review, blocked. |
(CanvasValidationResponse).validation.completionReview.referenceLineage.findingCodes | array of string | Yes | — |
(CanvasValidationResponse).validation.completionReview.referenceLineage.nodeHandles | array of string | Yes | — |
(CanvasValidationResponse).validation.completionReview.referenceLineage.connectionHandles | array of string | Yes | — |
(CanvasValidationResponse).validation.completionReview.productFidelity | CanvasProductFidelityReview | Yes | A conservative review signal, not an authoritative visual inspection. |
(CanvasValidationResponse).validation.completionReview.productFidelity.status | string | Yes | One of not-applicable, needs-review, passed. |
(CanvasValidationResponse).validation.completionReview.productFidelity.reviewedOutputCount | integer | Yes | min 0. |
(CanvasValidationResponse).validation.completionReview.productFidelity.needsReviewOutputHandles | array of string | Yes | — |
(CanvasValidationResponse).validation.completionReview.productFidelity.evidenceMissingOutputHandles | array of string | Yes | — |
(CanvasValidationResponse).validation.completionReview.productFidelity.reasons | array of object | No | — |
(CanvasValidationResponse).validation.completionReview.delivery | CanvasCompletionDelivery | Yes | Generated-output delivery evidence. A blocked state prevents a Done claim. |
(CanvasValidationResponse).validation.completionReview.delivery.status | string | Yes | One of clear, blocked. |
(CanvasValidationResponse).validation.completionReview.delivery.reasons | array of string | No | — |
(CanvasValidationResponse).validation.completionReview.delivery.pendingOutputHandles | array of string | Yes | — |
(CanvasValidationResponse).validation.completionReview.delivery.failedOutputHandles | array of string | Yes | — |
(CanvasValidationResponse).validation.completionReview.delivery.nonDurableOutputHandles | array of string | Yes | — |
(CanvasValidationResponse).validation.completionReview.blockingFindingCodes | array of string | Yes | — |
(CanvasValidationResponse).validation.completionReview.advisoryFindingCodes | array of string | Yes | — |
(MutationResponse).canvas | Canvas | Yes | — |
(MutationResponse).canvas.id | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
(MutationResponse).canvas.canvasType | string | No | One of agent. |
(MutationResponse).canvas.handle | string | Yes | Pattern ^canvas:[A-Za-z0-9_-]+:[A-Za-z0-9_-]+$. |
(MutationResponse).canvas.ownerUid | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
(MutationResponse).canvas.title | string | Yes | max length 160. |
(MutationResponse).canvas.createdAt | string (date-time) | Yes | — |
(MutationResponse).canvas.updatedAt | string (date-time) | Yes | — |
(MutationResponse).canvas.revision | string | Yes | min length 1. |
(MutationResponse).canvas.nodes | array of CanvasNode | Yes | — |
(MutationResponse).canvas.nodes[].id | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
(MutationResponse).canvas.nodes[].handle | string | Yes | Pattern ^node:. |
(MutationResponse).canvas.nodes[].type | string | Yes | One of image, video, text, sticky. |
(MutationResponse).canvas.nodes[].title | string | Yes | — |
(MutationResponse).canvas.nodes[].position | Position | Yes | — |
(MutationResponse).canvas.nodes[].position.x | number | Yes | min -10000000, max 10000000. |
(MutationResponse).canvas.nodes[].position.y | number | Yes | min -10000000, max 10000000. |
(MutationResponse).canvas.nodes[].width | number | Yes | — |
(MutationResponse).canvas.nodes[].height | number | Yes | — |
(MutationResponse).canvas.nodes[].metadata | object | No | — |
(MutationResponse).canvas.nodes[].asset | string | No | Pattern ^asset:. |
(MutationResponse).canvas.connections | array of CanvasConnection | Yes | — |
(MutationResponse).canvas.connections[].id | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
(MutationResponse).canvas.connections[].handle | string | Yes | Pattern ^connection:. |
(MutationResponse).canvas.connections[].fromNodeId | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
(MutationResponse).canvas.connections[].toNodeId | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
(MutationResponse).canvas.connections[].from | string | Yes | Pattern ^node:. |
(MutationResponse).canvas.connections[].to | string | Yes | Pattern ^node:. |
(MutationResponse).replayed | boolean | No | — |
(MutationResponse).resolvedIds | object | No | — |
(MutationResponse).changedNodeIds | array of string | No | — |
(MutationResponse).deletedNodeIds | array of string | No | — |
(MutationResponse).changedConnectionIds | array of string | No | — |
(MutationResponse).deletedConnectionIds | array of string | No | — |
Example
curl -X POST "https://app.gavana.ai/api/canvas-agent/v1/canvases/canvas:8f2c1d40-9a77-4c2e-9c11-2b0a5f6d7e31/operations" \
-H "Authorization: Bearer $GAVANA_AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'GET /canvases/{canvasId}/render
Render a canvas as SVG
- Operation id:
renderCanvasSvg - Scopes: Requires
canvas:read. - CLI equivalent:
gavana canvas render
Parameters
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
canvasId | path | string | Yes | Pattern ^[A-Za-z0-9_-]{1,180}$. |
ownerUid | query | string | No | Identifies the owner of a shared canvas or asset. Owner-qualified handles let the CLI supply it automatically. Pattern ^[A-Za-z0-9_-]{1,180}$. |
Responses
| Status | Payload | Meaning |
|---|---|---|
200 | image/svg+xml | An SVG rendering of the canvas. |
default | ErrorResponse | A stable machine-readable error. The response always includes X-Request-ID. |
Example
curl "https://app.gavana.ai/api/canvas-agent/v1/canvases/canvas:8f2c1d40-9a77-4c2e-9c11-2b0a5f6d7e31/render" \
-H "Authorization: Bearer $GAVANA_AGENT_TOKEN"Errors
Every failure uses the shared error envelope described in Errors and X-Request-ID. The response always carries X-Request-ID; share that value with support instead of the request payload.