# Canvas API

Canvas layouts. 2 operations under /api/v1. Errors are RFC 9457 problem+json with a stable `code` field. The complete schemas are in https://nebulactrl.com/api/v1/openapi.json.

## GET /projects/{projectId}/environments/{environmentId}/canvas

Get an environment's canvas layout

`version` 0 with an empty layout means nothing was saved yet. The ETag is the version.

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `projectId` (path, string, required)
- `environmentId` (path, string, required)

Response 200: OK (Canvas)
  - `layout` (Layout, required)
  - `version` (integer (int64), required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 422 Unprocessable Entity; 500 Internal Server Error

## PUT /projects/{projectId}/environments/{environmentId}/canvas

Save an environment's canvas layout

Requires `If-Match: "<version>"`; a stale version returns 412 `layout_conflict`. The layout is presentation only and never affects deployments.

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `projectId` (path, string, required)
- `environmentId` (path, string, required)
- `If-Match` (header, string): The ETag (version) from the last GET. Use "0" for the first save.

Request body (application/json, required): PutInputBody3
  - `layout` (Layout, required)

Response 200: OK (Canvas)
  - `layout` (Layout, required)
  - `version` (integer (int64), required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 412 Precondition Failed (`layout_conflict`); 422 Unprocessable Entity; 428 Precondition Required; 500 Internal Server Error
