Drafts & Versioning
Create and manage draft copies of queries for safe editing without affecting production queries or cached data.
Draft Workflow
The draft workflow allows users to:
- Create a personal draft copy of a query
- Make changes to the draft without affecting the original
- Commit the draft to update the original query
- Discard the draft if changes are unwanted
Each user gets their own draft instance, allowing multiple users to experiment with changes simultaneously.
POST /api/queries/{id}/_edit
Create or retrieve a draft copy of a query for editing.
Authentication: Required
Permission: query:write
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Query ID (UUID) or natural ID of the original query |
Response:
If a draft already exists for the current user, it is returned. Otherwise, a new draft is created.
{
"id": "draft-uuid",
"naturalId": "draft-uuid",
"name": "Sales Analysis Query (Draft)",
"description": "Quarterly sales data with regional breakdowns",
"language": "sql",
"datasourceId": "mysql-prod",
"payload": {
"sql": "SELECT * FROM sales WHERE date >= :startDate"
},
"inputs": {
"startDate": {
"type": "date",
"label": "Start Date",
"default": "2024-01-01"
}
},
"flow": [],
"fields": {},
"settings": {},
"limit": -1,
"embedded": false,
"editingId": "550e8400-e29b-41d4-a716-446655440000",
"ownerId": "john.doe",
"createdAt": "2024-02-09T10:00:00Z",
"updatedAt": "2024-02-09T10:00:00Z",
"_links": {
"self": { "href": "/api/queries/draft-uuid" }
}
}
Draft Properties:
| Property | Description |
|---|---|
editingId | ID of the original query being edited |
ownerId | User ID of the draft owner (not team) |
name | Original name with " (Draft)" suffix |
Each user can have only one draft per query. Calling this endpoint multiple times returns the same draft instance.
GET /api/queries/{id}/draft
Get the current user's draft for a query.
Authentication: Required
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Query ID (UUID) or natural ID of the original query |
Response:
Returns 404 if no draft exists for the current user. Otherwise returns the draft query.
{
"id": "draft-uuid",
"name": "Sales Analysis Query (Draft)",
"editingId": "550e8400-e29b-41d4-a716-446655440000",
"hasUserSettings": true,
"ownerId": "john.doe",
"_links": {
"self": { "href": "/api/queries/draft-uuid" },
"inf:original": { "href": "/api/queries/550e8400-e29b-41d4-a716-446655440000" },
"inf:commit-draft": { "href": "/api/queries/550e8400-e29b-41d4-a716-446655440000/draft/_commit" }
},
"_embedded": {
"inf:datasource": {
"id": "mysql-prod",
"name": "Production MySQL",
"type": "mysql"
}
}
}
Additional Properties:
| Property | Description |
|---|---|
hasUserSettings | Whether any users have custom settings for this query |
PUT /api/queries/{id}/draft
Create or update a draft with specific changes.
Authentication: Required
Permission: query:write
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Query ID (UUID) or natural ID of the original query |
Request Body:
{
"language": "sql",
"payload": {
"sql": "SELECT * FROM sales WHERE region = :region AND date >= :startDate"
},
"inputs": {
"region": {
"type": "string",
"label": "Sales Region",
"default": "West"
},
"startDate": {
"type": "date",
"label": "Start Date"
}
},
"flow": [],
"datasourceId": "mysql-prod",
"settings": { "timeout": 90000 },
"fields": {
"region": { "format": "uppercase" }
}
}
Validation:
| Field | Type | Required | Description |
|---|---|---|---|
language | string | No | Query language |
payload | object | No | Language-specific query definition |
inputs | object | No | Input parameter definitions |
flow | array | No | Transformation steps |
datasourceId | string | No | Datasource UUID |
settings | object | No | Query settings |
fields | object | No | Field metadata (default: {}) |
Response:
Returns the created or updated draft with 200 OK status and Location header.
After updating a draft, use POST /api/queries/{draftId}/_execute to test the query without committing changes.
DELETE /api/queries/{id}/draft
Discard the current user's draft for a query.
Authentication: Required
Permission: query:write
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Query ID (UUID) or natural ID of the original query |
Response:
Returns 204 No Content on success, or 404 if no draft exists.
Drafts are automatically removed when committed (for ad-hoc queries) or when the draft owner is deleted.
POST /api/queries/{id}/draft/_commit
Commit a draft to update the original query.
Authentication: Required
Permission: query:write
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Query ID (UUID) or natural ID of the original query |
Request Body:
{
"clearAllUserSettings": true
}
Validation:
| Field | Type | Default | Description |
|---|---|---|---|
clearAllUserSettings | boolean | true | Whether to clear all user settings and cached data when committing |
Response:
Returns the updated original query with 200 OK status.
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"naturalId": "team:analytics:sales-query",
"name": "Sales Analysis Query",
"description": "Quarterly sales data with regional breakdowns",
"language": "sql",
"payload": {
"sql": "SELECT * FROM sales WHERE region = :region AND date >= :startDate"
},
"inputs": {
"region": { "type": "string", "label": "Sales Region" },
"startDate": { "type": "date", "label": "Start Date" }
},
"defnUpdatedAt": "2024-02-09T10:15:00Z",
"updatedAt": "2024-02-09T10:15:00Z"
}
Commit Side Effects:
When clearAllUserSettings is true (default):
- User Settings Cleared - All users' custom settings for the query are deleted
- Dataset Fields Cleared - All embedded dataset fields are reset
- Indexed Data Cleared - All cached Elasticsearch data is removed
- Dataset Cache Invalidated -
dataUpdatedAtis cleared for all embedded datasets - Job Settings Cleared - All job-specific dataset settings are removed
When clearAllUserSettings is false:
- Only the query definition is updated
- User settings and cached data are preserved
defnUpdatedAtis still updated to track definition changes
Committing with clearAllUserSettings: true will force all users to re-run the query and reconfigure their views. Use false for non-breaking changes like description updates.
For embedded queries, the draft is NOT deleted after commit - it persists for future edits. For ad-hoc queries, the draft is automatically deleted.
Draft Best Practices
When to Use Drafts
- Schema Changes - Adding/removing fields or changing data types
- Performance Tuning - Testing query optimizations
- Parameter Updates - Adding or modifying input parameters
- Breaking Changes - Any change that invalidates cached data
When to Skip Drafts
- Metadata Updates - Name, description, folder changes
- Non-Breaking Settings - Timeout adjustments, display settings
- Quick Fixes - Typos or minor corrections
Testing Workflow
# 1. Create a draft
POST /api/queries/my-query/_edit
# 2. Update the draft with changes
PUT /api/queries/my-query/draft
{
"payload": { "sql": "SELECT * FROM new_table" }
}
# 3. Test the draft query
POST /api/queries/{draftId}/_execute
{
"startDate": "2024-01-01"
}
# 4. Commit if tests pass
POST /api/queries/my-query/draft/_commit
{
"clearAllUserSettings": true
}
# Or discard if tests fail
DELETE /api/queries/my-query/draft
Clearing User Settings
| Scenario | clearAllUserSettings |
|---|---|
| Changed field names/types | true |
| Added new fields | true |
| Changed datasource | true |
| Performance optimization | true (to force re-fetch) |
| Updated description only | false |
| Changed timeout setting | false |
| Renamed query | false |