Dashboards
Save a dashboard's complete widget layout without changing its data connections.
Save widget layout
PUT /api/v1/dashboards/:id/layout
Requires an authenticated user who owns the dashboard in their active company,
with workflows:write access. Company viewers and invited viewers cannot change
layout. Public share links are read-only snapshots.
Send every widget in the dashboard, using IDs and positions from
GET /api/v1/dashboards/:id. The grid has 12 columns. Studio offers widths of
4, 6, or 12 columns and heights of 4 or 8 rows.
| Field | Requirements |
|---|---|
widgets | Complete array, at most 40 entries; empty only for an empty dashboard |
widgets[].id | Unique widget UUID belonging to this dashboard |
widgets[].x | Integer from 0 through 11 |
widgets[].y | Nonnegative safe integer, at most Number.MAX_SAFE_INTEGER - 12 |
widgets[].w | Positive integer, at most 12; x + w must not exceed 12 |
widgets[].h | Positive integer, at most 12 |
curl -X PUT 'https://API_HOST/api/v1/dashboards/DASHBOARD_ID/layout' \
-H 'Content-Type: application/json' \
-H 'Cookie: better-auth.session_token=SESSION_TOKEN' \
--data '{"widgets":[{"id":"00000000-0000-0000-0000-000000000001","x":0,"y":0,"w":6,"h":8}]}'The server closes vertical gaps, resolves overlaps in row/column order, and saves all positions atomically. Always apply the returned positions: they can differ from the submitted Y coordinates. Merge them into your existing widgets by ID. This endpoint never replaces widget configs, query connections, refresh settings, or snapshot data, and does not create a dashboard version.
{
"success": true,
"data": {
"widgets": [
{ "id": "00000000-0000-0000-0000-000000000001", "x": 0, "y": 0, "w": 6, "h": 8 }
],
"updatedAt": "2026-09-18T12:00:00.000Z"
}
}A successful save updates the dashboard timestamp and records one audit event. Send a request after a completed edit, rather than when rendering or polling.
| Status | Meaning |
|---|---|
200 | Complete accepted layout saved |
400 | Invalid coordinates, duplicate IDs, or an ID outside this dashboard |
401 / 403 | Missing authentication or insufficient scope |
404 | Dashboard is unavailable to the caller as its owner |
409 | The layout omits widgets added since the client loaded it; reload before saving |
413 | Request exceeds 1 MB |
429 | Write rate limit reached |
Invalid or stale sets save nothing. A concurrent widget creation is serialized
with a layout save. Creation remains server-placed: POST /:id/widgets accepts
optional width/height hints, and ignores requested X/Y positions. The existing
PATCH /:id/widgets/:widgetId also accepts a complete position within the same
bounds, compacts affected widgets, and preserves omitted config/source fields.
Loopfour