Create or update a workbook by slug
curl --request PUT \
--url https://{tenant}.cubecloud.dev/api/v1/deployments/{deploymentId}/workbooks/by-slug/{slug} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"folderId": 123,
"meta": {},
"name": "<string>"
}
'import requests
url = "https://{tenant}.cubecloud.dev/api/v1/deployments/{deploymentId}/workbooks/by-slug/{slug}"
payload = {
"folderId": 123,
"meta": {},
"name": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({folderId: 123, meta: {}, name: '<string>'})
};
fetch('https://{tenant}.cubecloud.dev/api/v1/deployments/{deploymentId}/workbooks/by-slug/{slug}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://{tenant}.cubecloud.dev/api/v1/deployments/{deploymentId}/workbooks/by-slug/{slug}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'folderId' => 123,
'meta' => [
],
'name' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://{tenant}.cubecloud.dev/api/v1/deployments/{deploymentId}/workbooks/by-slug/{slug}"
payload := strings.NewReader("{\n \"folderId\": 123,\n \"meta\": {},\n \"name\": \"<string>\"\n}")
req, _ := http.NewRequest("PUT", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.put("https://{tenant}.cubecloud.dev/api/v1/deployments/{deploymentId}/workbooks/by-slug/{slug}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"folderId\": 123,\n \"meta\": {},\n \"name\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://{tenant}.cubecloud.dev/api/v1/deployments/{deploymentId}/workbooks/by-slug/{slug}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"folderId\": 123,\n \"meta\": {},\n \"name\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"calculatedFields": {},
"createdAt": "2023-12-25",
"deploymentId": 123,
"id": 123,
"meta": {},
"name": "<string>",
"updatedAt": "2023-12-25",
"createdBy": 123,
"dashboardDraft": {
"app": {
"entry": "<string>",
"files": {},
"html": "<string>",
"manifest": {
"dependencies": {},
"version": 123
}
},
"description": "<string>",
"layout": {
"breakpoints": {
"lg": 123,
"md": 123,
"sm": 123,
"xs": 123,
"xxs": 123
},
"cols": {
"lg": 123,
"md": 123,
"sm": 123,
"xs": 123,
"xxs": 123
},
"containerPadding": [
123
],
"margin": [
123
],
"rowHeight": 123
},
"settings": {},
"theme": {},
"title": "<string>",
"widgets": [
{
"id": "<string>",
"position": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"config": {},
"responsiveLayouts": {
"lg": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"md": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"sm": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"xs": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"xxs": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
}
},
"style": {}
}
]
},
"dashboardPublished": {
"app": {
"entry": "<string>",
"files": {},
"html": "<string>",
"manifest": {
"dependencies": {},
"version": 123
}
},
"description": "<string>",
"layout": {
"breakpoints": {
"lg": 123,
"md": 123,
"sm": 123,
"xs": 123,
"xxs": 123
},
"cols": {
"lg": 123,
"md": 123,
"sm": 123,
"xs": 123,
"xxs": 123
},
"containerPadding": [
123
],
"margin": [
123
],
"rowHeight": 123
},
"settings": {},
"theme": {},
"title": "<string>",
"widgets": [
{
"id": "<string>",
"position": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"config": {},
"responsiveLayouts": {
"lg": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"md": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"sm": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"xs": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"xxs": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
}
},
"style": {}
}
]
},
"folderId": 123,
"folderPath": [
{
"id": 123,
"name": "<string>"
}
],
"isFavorite": true,
"publishedDashboard": {
"allowEmbed": true,
"config": {
"app": {
"entry": "<string>",
"files": {},
"html": "<string>",
"manifest": {
"dependencies": {},
"version": 123
}
},
"description": "<string>",
"layout": {
"breakpoints": {
"lg": 123,
"md": 123,
"sm": 123,
"xs": 123,
"xxs": 123
},
"cols": {
"lg": 123,
"md": 123,
"sm": 123,
"xs": 123,
"xxs": 123
},
"containerPadding": [
123
],
"margin": [
123
],
"rowHeight": 123
},
"settings": {},
"theme": {},
"title": "<string>",
"widgets": [
{
"id": "<string>",
"position": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"config": {},
"responsiveLayouts": {
"lg": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"md": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"sm": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"xs": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"xxs": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
}
},
"style": {}
}
]
},
"deploymentId": 123,
"id": 123,
"publicId": "<string>",
"reportSnapshots": [
{
"id": 123,
"reportId": 123,
"versionNumber": 123,
"description": "<string>",
"fillMissingRows": {
"member": "<string>",
"excludedDimensions": [
"<string>"
]
},
"meta": {},
"name": "<string>",
"periodComparisons": [
{
"measure": "<string>",
"offset": {
"amount": 123,
"unit": "<string>"
},
"timeDimension": "<string>",
"outputs": [
"<string>"
]
}
],
"preferences": {
"columnFormats": {}
},
"spec": "<string>",
"sqlQuery": "<string>",
"title": "<string>"
}
],
"versionId": 123,
"versionNumber": 123,
"workbookId": 123,
"createdBy": 123,
"description": "<string>",
"fromSharedWorkspace": true,
"restrictDataDownload": true,
"title": "<string>",
"useBoardDashboards": true
},
"slug": "<string>",
"user": {
"email": "<string>",
"id": 123,
"firstName": "<string>",
"picture": "<string>"
},
"userId": 123
}Workbooks
Create or update a workbook by slug
PUT
/
api
/
v1
/
deployments
/
{deploymentId}
/
workbooks
/
by-slug
/
{slug}
Create or update a workbook by slug
curl --request PUT \
--url https://{tenant}.cubecloud.dev/api/v1/deployments/{deploymentId}/workbooks/by-slug/{slug} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"folderId": 123,
"meta": {},
"name": "<string>"
}
'import requests
url = "https://{tenant}.cubecloud.dev/api/v1/deployments/{deploymentId}/workbooks/by-slug/{slug}"
payload = {
"folderId": 123,
"meta": {},
"name": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({folderId: 123, meta: {}, name: '<string>'})
};
fetch('https://{tenant}.cubecloud.dev/api/v1/deployments/{deploymentId}/workbooks/by-slug/{slug}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://{tenant}.cubecloud.dev/api/v1/deployments/{deploymentId}/workbooks/by-slug/{slug}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'folderId' => 123,
'meta' => [
],
'name' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://{tenant}.cubecloud.dev/api/v1/deployments/{deploymentId}/workbooks/by-slug/{slug}"
payload := strings.NewReader("{\n \"folderId\": 123,\n \"meta\": {},\n \"name\": \"<string>\"\n}")
req, _ := http.NewRequest("PUT", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.put("https://{tenant}.cubecloud.dev/api/v1/deployments/{deploymentId}/workbooks/by-slug/{slug}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"folderId\": 123,\n \"meta\": {},\n \"name\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://{tenant}.cubecloud.dev/api/v1/deployments/{deploymentId}/workbooks/by-slug/{slug}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"folderId\": 123,\n \"meta\": {},\n \"name\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"calculatedFields": {},
"createdAt": "2023-12-25",
"deploymentId": 123,
"id": 123,
"meta": {},
"name": "<string>",
"updatedAt": "2023-12-25",
"createdBy": 123,
"dashboardDraft": {
"app": {
"entry": "<string>",
"files": {},
"html": "<string>",
"manifest": {
"dependencies": {},
"version": 123
}
},
"description": "<string>",
"layout": {
"breakpoints": {
"lg": 123,
"md": 123,
"sm": 123,
"xs": 123,
"xxs": 123
},
"cols": {
"lg": 123,
"md": 123,
"sm": 123,
"xs": 123,
"xxs": 123
},
"containerPadding": [
123
],
"margin": [
123
],
"rowHeight": 123
},
"settings": {},
"theme": {},
"title": "<string>",
"widgets": [
{
"id": "<string>",
"position": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"config": {},
"responsiveLayouts": {
"lg": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"md": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"sm": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"xs": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"xxs": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
}
},
"style": {}
}
]
},
"dashboardPublished": {
"app": {
"entry": "<string>",
"files": {},
"html": "<string>",
"manifest": {
"dependencies": {},
"version": 123
}
},
"description": "<string>",
"layout": {
"breakpoints": {
"lg": 123,
"md": 123,
"sm": 123,
"xs": 123,
"xxs": 123
},
"cols": {
"lg": 123,
"md": 123,
"sm": 123,
"xs": 123,
"xxs": 123
},
"containerPadding": [
123
],
"margin": [
123
],
"rowHeight": 123
},
"settings": {},
"theme": {},
"title": "<string>",
"widgets": [
{
"id": "<string>",
"position": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"config": {},
"responsiveLayouts": {
"lg": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"md": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"sm": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"xs": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"xxs": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
}
},
"style": {}
}
]
},
"folderId": 123,
"folderPath": [
{
"id": 123,
"name": "<string>"
}
],
"isFavorite": true,
"publishedDashboard": {
"allowEmbed": true,
"config": {
"app": {
"entry": "<string>",
"files": {},
"html": "<string>",
"manifest": {
"dependencies": {},
"version": 123
}
},
"description": "<string>",
"layout": {
"breakpoints": {
"lg": 123,
"md": 123,
"sm": 123,
"xs": 123,
"xxs": 123
},
"cols": {
"lg": 123,
"md": 123,
"sm": 123,
"xs": 123,
"xxs": 123
},
"containerPadding": [
123
],
"margin": [
123
],
"rowHeight": 123
},
"settings": {},
"theme": {},
"title": "<string>",
"widgets": [
{
"id": "<string>",
"position": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"config": {},
"responsiveLayouts": {
"lg": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"md": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"sm": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"xs": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
},
"xxs": {
"h": 123,
"w": 123,
"x": 123,
"y": 123
}
},
"style": {}
}
]
},
"deploymentId": 123,
"id": 123,
"publicId": "<string>",
"reportSnapshots": [
{
"id": 123,
"reportId": 123,
"versionNumber": 123,
"description": "<string>",
"fillMissingRows": {
"member": "<string>",
"excludedDimensions": [
"<string>"
]
},
"meta": {},
"name": "<string>",
"periodComparisons": [
{
"measure": "<string>",
"offset": {
"amount": 123,
"unit": "<string>"
},
"timeDimension": "<string>",
"outputs": [
"<string>"
]
}
],
"preferences": {
"columnFormats": {}
},
"spec": "<string>",
"sqlQuery": "<string>",
"title": "<string>"
}
],
"versionId": 123,
"versionNumber": 123,
"workbookId": 123,
"createdBy": 123,
"description": "<string>",
"fromSharedWorkspace": true,
"restrictDataDownload": true,
"title": "<string>",
"useBoardDashboards": true
},
"slug": "<string>",
"user": {
"email": "<string>",
"id": 123,
"firstName": "<string>",
"picture": "<string>"
},
"userId": 123
}Idempotent create-or-update (“upsert”) of a workbook keyed by its deployment-scoped slug — the portable identity for managing dashboards as code (CI/CD pipelines re-applying the same definition against any environment).
If a workbook with this slug exists in the deployment, it is updated: only the fields you send are changed, and
meta is merged into the existing metadata (a meta.dashboardDraft is validated like everywhere else). Otherwise a new workbook is created with this slug. The slug is normalized (trimmed, lowercased) before matching. Re-applying the same request is a no-op.
Access: updating requires edit access to the workbook holding the slug (requests that include folderId require manage access, plus edit access to the destination folder); creating requires the same access as POST /workbooks (AI BI User role or higher).
Whether this request creates or updates is decided — and access-checked — before it is applied. If another writer changes that in between (creates or deletes the workbook holding this slug), the request is rejected with 409 and code: "upsert_branch_changed" rather than applied under the wrong permission check. This is transient: retry the request. It only happens when two writers target the same slug concurrently — for example two pipeline runs applying the same bundle at once, where one creates the workbook and the other must then retry into the update path.
Returns 404 if the deployment does not exist.Authorizations
Token authentication. Send Authorization: Bearer <YOUR_TOKEN>.
Response
200 - application/json
Available options:
FOLDER, WORKBOOK, REPORT Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
⌘I