Skip to main content

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:

ParameterTypeDescription
idstringReport ID to create draft from

Response:

Responds 200 with the draft (a private copy owned by the caller, linked back via editingId).

Start editing a reportPOST /api/reports/admin:sales-overview/_edit
Creates (or returns the existing) draft: a private copy owned by the caller, linked back to the original via editingId. Edit the draft, then commit it.
Response · 200
{
"_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
}
}
}
Captured from the API examples test suite; ids and timestamps are normalized.

Behavior:

  • If a draft already exists for this user and report, returns the existing draft
  • Creates a new draft with editingId pointing 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 beforeCreateDraft hook

GET /api/reports/{id}/draft

Get the current user's draft of a report.

Authentication: Required

Path Parameters:

ParameterTypeDescription
idstringOriginal report ID

Response:

Returns the draft report (same structure as GET /api/reports/{id}), with inf:original and inf:commit-draft links.

Get the draftGET /api/reports/admin:sales-overview/draft
The caller’s draft for this report, with inf:original and inf:commit-draft links.
Response · 200
{
"_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
}
}
}
Captured from the API examples test suite; ids and timestamps are normalized.

PUT /api/reports/{id}/draft

Create or update a draft (alternative to POST /_edit).

Authentication: Required

Permissions: report:write

Path Parameters:

ParameterTypeDescription
idstringOriginal report ID

Request Body:

{
"defn": {
"visuals": []
},
"templateId": "template-123",
"datasourceId": "mysql-prod",
"queryId": null,
"settings": {}
}

Optional Fields:

FieldTypeDescription
defnobjectReport definition
templateIdstringTemplate ID
datasourceIdstringDatasource ID
queryIdstringQuery ID
settingsobjectReport settings

Response:

Returns the created/updated draft with 201 Created status and Location header.

Notes:

  • Fields default to values from the original report
  • The name field is intentionally removed (cannot rename via draft PUT)
  • Report drivers can customize via beforeCreateDraft hook
  • 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:

ParameterTypeDescription
idstringOriginal report ID

Response:

Responds 200 on success.

Discard the draftDELETE /api/reports/admin:sales-overview/draft
Deletes the draft without touching the original report.
Response · 200 · no body
Captured from the API examples test suite; ids and timestamps are normalized.

Behavior:

  • Deletes the draft report for the current user
  • Report drivers can perform cleanup via beforeDiscardDraft hook
  • 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:

ParameterTypeDescription
idstringOriginal report ID

Response:

Returns the updated original report with committed changes.

Commit the draftPOST /api/reports/admin:sales-overview/draft/_commit
Applies the draft’s defn, datasets, and settings back onto the original report, then deletes the draft. Returns the updated original.
Response · 200
{
"_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
}
}
}
Captured from the API examples test suite; ids and timestamps are normalized.

Behavior:

  1. Standard fields committed: defn, datasourceId, templateId, queryId, settings, enableFilter
  2. Name updated conditionally: Only if draft name differs from original (allows Apps to rename via AI)
  3. Dataset references swapped: Draft's ReportDataset entries replace original's
  4. Timestamps updated: defnUpdatedAt set to commit time
  5. Draft deleted: After successful commit
  6. User settings cleared: All user-specific view settings are removed
  7. Activity logged: Creates reportUpdated activity entry
  8. Driver hooks: Report drivers can modify changes via afterCommitDraft hook
Transaction Safety

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 editingId field 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)