Drafts
Draft workflow for safe report editing without affecting the production version.
Overview
Reports support a draft workflow that allows users to make changes without modifying the live report. Each user can have their own draft of a report. Drafts are temporary copies that can be committed (published) or discarded.
POST /api/reports/{id}/_edit
Create a draft copy of a report for editing.
Authentication: Required
Permissions: report:write
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Report ID to create draft from |
Response:
Responds 200 with the draft (a private copy owned by the caller, linked back via editingId).
{
"_links": {
"self": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/_edit"
},
"inf:owner": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/owner"
},
"inf:draft": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/draft"
},
"inf:user-settings": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/settings"
},
"inf:comments": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/comments{?sort,limit}",
"templated": true
},
"inf:report-shares": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/shares"
},
"inf:report-datasets": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/dataset-refs"
},
"inf:report-template": {
"href": "https://informer.example.com/api/reports/template"
},
"inf:report-templates": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/templates"
},
"inf:input-templates": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/input-templates"
},
"inf:visual-templates": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/visual-templates"
},
"inf:input-filter-templates": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/input-filter-templates"
},
"inf:report-tags": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/_edit/tags"
},
"inf:favorite": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/_edit/favorite"
},
"inf:report-tag": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/_edit/tags/{tagId}",
"templated": true
},
"inf:export-bundle": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/_edit/_export"
},
"view": {
"href": "https://informer.example.com/v/report.html?id=00000000-0000-4000-8000-000000000001"
},
"inf:cloud-publish": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/_edit/_cloud-publish"
},
"inf:cloud-unpublish": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/_edit/_cloud-unpublish"
},
"inf:copy": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/_edit/_copy"
},
"inf:report-share": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/_edit/shares/{principalId}",
"templated": true
},
"inf:report-run": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/_run{?progress}",
"templated": true
}
},
"naturalId": "admin:sales-overview-1",
"permissions": {
"assignTags": true,
"changeOwner": true,
"cloudPublish": false,
"copy": true,
"dataset": true,
"delete": true,
"edit": true,
"filter": true,
"license": true,
"rename": true,
"run": true,
"share": true,
"variable": true,
"visual": true,
"write": true
},
"id": "00000000-0000-4000-8000-000000000001",
"type": "dashboard",
"name": "Sales Overview",
"tenant": "acme",
"ownerId": "admin",
"slug": "sales-overview-1",
"description": null,
"defn": {
"layout": {},
"visuals": [],
"layoutOptions": {
"showVisualOptions": true
}
},
"settings": {
"overallFilter": {}
},
"source": null,
"sourceId": null,
"shared": false,
"defnUpdatedAt": null,
"enableFilter": false,
"libraryId": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"editingId": "00000000-0000-4000-8000-000000000002",
"datasourceId": null,
"queryId": null,
"templateId": null,
"folderId": null,
"datasets": [
{
"id": "00000000-0000-4000-8000-000000000003",
"filter": {},
"chips": [],
"alias": "orders",
"label": "Orders",
"tenant": "acme",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"reportId": "00000000-0000-4000-8000-000000000001",
"datasetId": "00000000-0000-4000-8000-000000000004",
"snapshots": null
}
],
"_embedded": {
"inf:report-type": {
"_links": {
"self": {
"href": "https://informer.example.com/api/report-types/dashboard"
}
},
"id": "dashboard",
"name": "common:dashboard",
"description": "reports:dashboard.description",
"image": "/assets/reports/images/dashboard.svg",
"color": "teal",
"mappingRequired": false,
"viewComponent": "dashboardView",
"editComponent": "dashboardEdit",
"previewComponent": "dashboardPreview",
"openInNewTab": false,
"datasets": "none",
"hasApiWhitelist": false
}
}
}
Behavior:
- If a draft already exists for this user and report, returns the existing draft
- Creates a new draft with
editingIdpointing to the original report - Copies all report configuration including dataset references and snapshot settings
- Draft is owned by the current user (not the original report owner)
- Report drivers can customize draft creation via
beforeCreateDrafthook
GET /api/reports/{id}/draft
Get the current user's draft of a report.
Authentication: Required
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Original report ID |
Response:
Returns the draft report (same structure as GET /api/reports/{id}), with inf:original and inf:commit-draft links.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/reports/00000000-0000-4000-8000-000000000001"
},
"inf:owner": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/owner"
},
"inf:draft": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/draft"
},
"inf:user-settings": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/settings"
},
"inf:comments": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/comments{?sort,limit}",
"templated": true
},
"inf:report-shares": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/shares"
},
"inf:report-datasets": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/dataset-refs"
},
"inf:report-template": {
"href": "https://informer.example.com/api/reports/template"
},
"inf:report-templates": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/templates"
},
"inf:input-templates": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/input-templates"
},
"inf:visual-templates": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/visual-templates"
},
"inf:input-filter-templates": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/input-filter-templates"
},
"inf:report-tags": {
"href": "https://informer.example.com/api/reports/00000000-0000-4000-8000-000000000001/tags"
},
"inf:favorite": {
"href": "https://informer.example.com/api/reports/00000000-0000-4000-8000-000000000001/favorite"
},
"inf:report-tag": {
"href": "https://informer.example.com/api/reports/00000000-0000-4000-8000-000000000001/tags/{tagId}",
"templated": true
},
"inf:export-bundle": {
"href": "https://informer.example.com/api/reports/00000000-0000-4000-8000-000000000001/_export"
},
"view": {
"href": "https://informer.example.com/v/report.html?id=00000000-0000-4000-8000-000000000001"
},
"inf:cloud-publish": {
"href": "https://informer.example.com/api/reports/00000000-0000-4000-8000-000000000001/_cloud-publish"
},
"inf:cloud-unpublish": {
"href": "https://informer.example.com/api/reports/00000000-0000-4000-8000-000000000001/_cloud-unpublish"
},
"inf:copy": {
"href": "https://informer.example.com/api/reports/00000000-0000-4000-8000-000000000001/_copy"
},
"inf:report-share": {
"href": "https://informer.example.com/api/reports/00000000-0000-4000-8000-000000000001/shares/{principalId}",
"templated": true
},
"inf:report-run": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview-1/_run{?progress}",
"templated": true
},
"inf:original": {
"href": "https://informer.example.com/api/reports/00000000-0000-4000-8000-000000000002"
},
"inf:commit-draft": {
"href": "https://informer.example.com/api/reports/00000000-0000-4000-8000-000000000002/draft/_commit"
}
},
"naturalId": "admin:sales-overview-1",
"permissions": {
"assignTags": true,
"changeOwner": true,
"cloudPublish": false,
"copy": true,
"dataset": true,
"delete": true,
"edit": true,
"filter": true,
"license": true,
"rename": true,
"run": true,
"share": true,
"variable": true,
"visual": true,
"write": true
},
"id": "00000000-0000-4000-8000-000000000001",
"type": "dashboard",
"name": "Sales Overview",
"tenant": "acme",
"ownerId": "admin",
"slug": "sales-overview-1",
"description": null,
"defn": {
"layout": {},
"visuals": [],
"layoutOptions": {
"showVisualOptions": true
}
},
"settings": {
"overallFilter": {}
},
"source": null,
"sourceId": null,
"shared": false,
"defnUpdatedAt": null,
"enableFilter": false,
"libraryId": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"editingId": "00000000-0000-4000-8000-000000000002",
"datasourceId": null,
"queryId": null,
"templateId": null,
"folderId": null,
"datasource": null,
"datasets": {
"orders": {
"id": "00000000-0000-4000-8000-000000000003",
"filter": {},
"chips": [],
"alias": "orders",
"label": "Orders",
"tenant": "acme",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"reportId": "00000000-0000-4000-8000-000000000001",
"datasetId": "00000000-0000-4000-8000-000000000004",
"snapshots": null
}
},
"query": null,
"template": null,
"templates": [],
"_embedded": {
"inf:report-type": {
"_links": {
"self": {
"href": "https://informer.example.com/api/report-types/dashboard"
}
},
"id": "dashboard",
"name": "common:dashboard",
"description": "reports:dashboard.description",
"image": "/assets/reports/images/dashboard.svg",
"color": "teal",
"mappingRequired": false,
"viewComponent": "dashboardView",
"editComponent": "dashboardEdit",
"previewComponent": "dashboardPreview",
"openInNewTab": false,
"datasets": "none",
"hasApiWhitelist": false
}
}
}
PUT /api/reports/{id}/draft
Create or update a draft (alternative to POST /_edit).
Authentication: Required
Permissions: report:write
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Original report ID |
Request Body:
{
"defn": {
"visuals": []
},
"templateId": "template-123",
"datasourceId": "mysql-prod",
"queryId": null,
"settings": {}
}
Optional Fields:
| Field | Type | Description |
|---|---|---|
defn | object | Report definition |
templateId | string | Template ID |
datasourceId | string | Datasource ID |
queryId | string | Query ID |
settings | object | Report settings |
Response:
Returns the created/updated draft with 201 Created status and Location header.
Notes:
- Fields default to values from the original report
- The
namefield is intentionally removed (cannot rename via draft PUT) - Report drivers can customize via
beforeCreateDrafthook - Dataset references and snapshot settings are copied from the original
DELETE /api/reports/{id}/draft
Discard a draft without committing changes.
Authentication: Required
Permissions: report:write
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Original report ID |
Response:
Responds 200 on success.
Behavior:
- Deletes the draft report for the current user
- Report drivers can perform cleanup via
beforeDiscardDrafthook - Original report is unaffected
POST /api/reports/{id}/draft/_commit
Commit a draft, publishing changes to the original report.
Authentication: Required
Permissions: report:write
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Original report ID |
Response:
Returns the updated original report with committed changes.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/draft/_commit"
},
"inf:owner": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/owner"
},
"inf:draft": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/draft"
},
"inf:user-settings": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/settings"
},
"inf:comments": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/comments{?sort,limit}",
"templated": true
},
"inf:report-shares": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/shares"
},
"inf:report-datasets": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/dataset-refs"
},
"inf:report-template": {
"href": "https://informer.example.com/api/reports/template"
},
"inf:report-templates": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/templates"
},
"inf:input-templates": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/input-templates"
},
"inf:visual-templates": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/visual-templates"
},
"inf:input-filter-templates": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/input-filter-templates"
},
"inf:report-tags": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/draft/_commit/tags"
},
"inf:favorite": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/draft/_commit/favorite"
},
"inf:report-tag": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/draft/_commit/tags/{tagId}",
"templated": true
},
"inf:export-bundle": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/draft/_commit/_export"
},
"view": {
"href": "https://informer.example.com/v/report.html?id=00000000-0000-4000-8000-000000000001"
},
"inf:cloud-publish": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/draft/_commit/_cloud-publish"
},
"inf:cloud-unpublish": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/draft/_commit/_cloud-unpublish"
},
"inf:copy": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/draft/_commit/_copy"
},
"edit": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/_edit"
},
"inf:report-share": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/draft/_commit/shares/{principalId}",
"templated": true
},
"inf:report-run": {
"href": "https://informer.example.com/api/reports/admin%3Asales-overview/_run{?progress}",
"templated": true
}
},
"naturalId": "admin:sales-overview",
"permissions": {
"assignTags": true,
"changeOwner": true,
"cloudPublish": false,
"copy": true,
"dataset": true,
"delete": true,
"edit": true,
"filter": true,
"license": true,
"rename": true,
"run": true,
"share": true,
"variable": true,
"visual": true,
"write": true
},
"id": "00000000-0000-4000-8000-000000000001",
"type": "dashboard",
"name": "Sales Overview",
"tenant": "acme",
"ownerId": "admin",
"slug": "sales-overview",
"description": null,
"defn": {
"layout": {},
"visuals": [],
"layoutOptions": {
"showVisualOptions": true
}
},
"settings": {
"overallFilter": {}
},
"source": null,
"sourceId": null,
"shared": false,
"defnUpdatedAt": "2026-01-15T16:20:00.000Z",
"enableFilter": false,
"libraryId": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"editingId": null,
"datasourceId": null,
"queryId": null,
"templateId": null,
"folderId": null,
"datasource": null,
"datasets": [
{
"id": "00000000-0000-4000-8000-000000000002",
"filter": {},
"chips": [],
"alias": "orders",
"label": "Orders",
"tenant": "acme",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"reportId": "00000000-0000-4000-8000-000000000001",
"datasetId": "00000000-0000-4000-8000-000000000003",
"snapshots": null
}
],
"template": null,
"templates": [],
"_embedded": {
"inf:report-type": {
"_links": {
"self": {
"href": "https://informer.example.com/api/report-types/dashboard"
}
},
"id": "dashboard",
"name": "common:dashboard",
"description": "reports:dashboard.description",
"image": "/assets/reports/images/dashboard.svg",
"color": "teal",
"mappingRequired": false,
"viewComponent": "dashboardView",
"editComponent": "dashboardEdit",
"previewComponent": "dashboardPreview",
"openInNewTab": false,
"datasets": "none",
"hasApiWhitelist": false
}
}
}
Behavior:
- Standard fields committed:
defn,datasourceId,templateId,queryId,settings,enableFilter - Name updated conditionally: Only if draft name differs from original (allows Apps to rename via AI)
- Dataset references swapped: Draft's
ReportDatasetentries replace original's - Timestamps updated:
defnUpdatedAtset to commit time - Draft deleted: After successful commit
- User settings cleared: All user-specific view settings are removed
- Activity logged: Creates
reportUpdatedactivity entry - Driver hooks: Report drivers can modify changes via
afterCommitDrafthook
Draft commit operations run in a database transaction to ensure atomicity. If any step fails, all changes are rolled back.
Workflow Example
// 1. Create draft
POST /api/reports/team:sales-dashboard/_edit
// Returns: user:john.doe:sales-dashboard-draft
// 2. Make changes to draft
PUT /api/reports/user:john.doe:sales-dashboard-draft
{
"defn": {
"visuals": [/* updated visuals */]
}
}
// 3. Commit draft
POST /api/reports/team:sales-dashboard/draft/_commit
// Original report now has the changes
// OR: Discard draft
DELETE /api/reports/team:sales-dashboard/draft
// Changes are lost, original unchanged
Multi-User Drafts
- Each user maintains their own independent draft of a report
- Drafts are identified by
editingIdfield pointing to the original - Only the draft owner can commit or discard their draft
- Multiple users can have concurrent drafts of the same report
- Last commit wins (no merge conflict resolution)