#Dashboards
GET /dashboards
List the organization's dashboards. Each row carries a chart count; the charts themselves come from the dashboard's detail endpoint.
Requires the analytics:read permission.
Query Parameters
limitnumberThe number of records to return.
cursorstringA pagination cursor. Fetch the next page by passing the nextCursor value from the previous response.
sortname | -name | createdAt | -createdAtThe sort order. Prefix a field with - to sort descending. Defaults to -createdAt.
filterstringA JSON-encoded filter document, for example {"createdAt":{"$gte":"2025-01-01T00:00:00Z"}}. Filterable fields: id, name, createdAt. See Filtering for the syntax.
curl \
"https://api.cascade.dev/dashboards" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@cascade-commerce/api/admin/client"
import { getDashboards } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getDashboards()require "net/http"
uri = URI("https://api.cascade.dev/dashboards")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
req = Request(
"https://api.cascade.dev/dashboards",
headers={"Authorization": "Bearer sk_YOUR_SECRET_KEY"},
)
with urlopen(req) as res:
print(json.load(res)){
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"chartCount": 0,
"isDefault": true,
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
}
],
"nextCursor": "string"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the dashboard."
},
"name": {
"type": "string",
"description": "The dashboard's title."
},
"chartCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many charts the dashboard holds."
},
"isDefault": {
"type": "boolean",
"description": "Whether this is the dashboard the app opens by default. At most one dashboard per organization is the default."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the dashboard was created."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the dashboard was last changed."
}
},
"required": ["id", "name", "chartCount", "isDefault", "createdAt", "updatedAt"],
"additionalProperties": false
},
"description": "The page of records, in the requested sort order."
},
"nextCursor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Pass this back as `cursor` to fetch the next page. Null once the last page has been returned."
}
},
"required": ["data", "nextCursor"],
"additionalProperties": false
}POST /dashboards
Create an empty dashboard. Charts are created onto it with POST /saved-charts.
Requires the analytics:write permission.
Body Parameters
dataobjectrequiredThe dashboard to create.
curl \
-X POST \
"https://api.cascade.dev/dashboards" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": {
"name": "string"
}
}'import { client } from "@cascade-commerce/api/admin/client"
import { postDashboards } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postDashboards({
body: {
data: {
name: "string",
},
},
})require "net/http"
uri = URI("https://api.cascade.dev/dashboards")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"data": {
"name": "string"
}
}
JSON
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
body = """{
"data": {
"name": "string"
}
}"""
req = Request(
"https://api.cascade.dev/dashboards",
data=body.encode(),
headers={
"Authorization": "Bearer sk_YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
)
with urlopen(req) as res:
print(json.load(res)){
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"chartCount": 0,
"isDefault": true,
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the dashboard."
},
"name": {
"type": "string",
"description": "The dashboard's title."
},
"chartCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many charts the dashboard holds."
},
"isDefault": {
"type": "boolean",
"description": "Whether this is the dashboard the app opens by default. At most one dashboard per organization is the default."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the dashboard was created."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the dashboard was last changed."
}
},
"required": ["id", "name", "chartCount", "isDefault", "createdAt", "updatedAt"],
"additionalProperties": false,
"description": "The new dashboard."
}GET /dashboards/{id}
Get a single dashboard by ID, with its charts in reading order (by grid row, then column), ready to run against /analytics/query.
Requires the analytics:read permission.
Path Parameters
idstring (uuid)requiredThe ID of the dashboard.
curl \
"https://api.cascade.dev/dashboards/550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@cascade-commerce/api/admin/client"
import { getDashboardsById } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getDashboardsById({
path: { id: "550e8400-e29b-41d4-a716-446655440000" },
})require "net/http"
uri = URI("https://api.cascade.dev/dashboards/550e8400-e29b-41d4-a716-446655440000")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
req = Request(
"https://api.cascade.dev/dashboards/550e8400-e29b-41d4-a716-446655440000",
headers={"Authorization": "Bearer sk_YOUR_SECRET_KEY"},
)
with urlopen(req) as res:
print(json.load(res)){
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"chartCount": 0,
"isDefault": true,
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z",
"charts": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"dashboardId": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"resource": "reviews",
"chartType": "stat",
"query": {
"measures": "...",
"groupBy": "...",
"granularity": "...",
"filter": "...",
"limit": "..."
},
"layout": {
"x": "...",
"y": "...",
"w": "...",
"h": "..."
},
"invalidReason": "string",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
}
]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the dashboard."
},
"name": {
"type": "string",
"description": "The dashboard's title."
},
"chartCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many charts the dashboard holds."
},
"isDefault": {
"type": "boolean",
"description": "Whether this is the dashboard the app opens by default. At most one dashboard per organization is the default."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the dashboard was created."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the dashboard was last changed."
},
"charts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the saved chart."
},
"dashboardId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "The dashboard the chart belongs to, for its whole life."
},
"name": {
"type": "string",
"description": "The title shown above the chart."
},
"resource": {
"type": "string",
"enum": [
"reviews",
"review-answers",
"orders",
"customers",
"products",
"loyalty-members",
"loyalty-activity",
"loyalty-rewards"
],
"description": "The resource to aggregate. Call `/analytics/schema` for what each one offers."
},
"chartType": {
"type": "string",
"enum": ["stat", "line", "area", "bar", "donut", "stackedBar", "groupedBar", "table"],
"description": "How the chart is drawn. It has to suit the query it is saved with: a `donut` needs one counting measure grouped by one non-date dimension, a `stat` needs no grouping at all, and `table` always fits."
},
"query": {
"type": "object",
"properties": {
"measures": {
"minItems": 1,
"type": "array",
"items": {
"type": "string"
},
"description": "The measures to report, by name. They have to be ones the resource offers."
},
"groupBy": {
"description": "The dimensions to group by, by name, at most three and at most one of them a date. Omit for a single total row.",
"type": "array",
"items": {
"type": "string"
}
},
"granularity": {
"type": "string",
"enum": ["hour", "day", "week", "month", "quarter", "year"],
"description": "The bucket size for a date dimension. Defaults to `day`. Ignored when nothing in `groupBy` is a date."
},
"filter": {
"description": "The filter document to apply, in the shape the resource's list endpoint takes. It is validated against the fields that screen allows.",
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
"limit": {
"description": "How many distinct group-by combinations to return, from 1 to 500.",
"type": "integer",
"minimum": 1,
"maximum": 500
}
},
"required": ["measures"],
"additionalProperties": false,
"description": "What the chart asks for. Send it to `/analytics/query` alongside `resource` to draw it."
},
"layout": {
"type": "object",
"properties": {
"x": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991,
"description": "The column the chart starts at, on a 12 column grid."
},
"y": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991,
"description": "The row the chart starts at."
},
"w": {
"type": "integer",
"minimum": 2,
"maximum": 12,
"description": "How many columns the chart spans, at least 2."
},
"h": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "How many rows tall the chart is. One row renders as 90 pixels."
}
},
"required": ["x", "y", "w", "h"],
"additionalProperties": false,
"description": "Where the chart sits on the dashboard's grid."
},
"invalidReason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Why the chart can no longer run, or null when it is fine. Definitions are validated on write, so this only fills in when a resource, measure or dimension the chart names was removed afterward."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the chart was saved."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the chart was last changed."
}
},
"required": [
"id",
"dashboardId",
"name",
"resource",
"chartType",
"query",
"layout",
"invalidReason",
"createdAt",
"updatedAt"
],
"additionalProperties": false
},
"description": "The dashboard's charts, in reading order: by row first, then by column."
}
},
"required": ["id", "name", "chartCount", "isDefault", "createdAt", "updatedAt", "charts"],
"additionalProperties": false,
"description": "The requested dashboard."
}PUT /dashboards/{id}
Rename a dashboard, or make it the one the app opens by default. Promoting a default demotes the dashboard that held it. Positions are changed through the layout endpoint.
Requires the analytics:write permission.
Path Parameters
idstring (uuid)requiredThe ID of the dashboard to update.
Body Parameters
dataobjectrequiredThe fields to update.
curl \
-X PUT \
"https://api.cascade.dev/dashboards/550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": {
"name": "string",
"isDefault": true
}
}'import { client } from "@cascade-commerce/api/admin/client"
import { putDashboardsById } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await putDashboardsById({
path: { id: "550e8400-e29b-41d4-a716-446655440000" },
body: {
data: {
name: "string",
isDefault: true,
},
},
})require "net/http"
uri = URI("https://api.cascade.dev/dashboards/550e8400-e29b-41d4-a716-446655440000")
req = Net::HTTP::Put.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"data": {
"name": "string",
"isDefault": true
}
}
JSON
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
body = """{
"data": {
"name": "string",
"isDefault": true
}
}"""
req = Request(
"https://api.cascade.dev/dashboards/550e8400-e29b-41d4-a716-446655440000",
data=body.encode(),
method="PUT",
headers={
"Authorization": "Bearer sk_YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
)
with urlopen(req) as res:
print(json.load(res)){
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"chartCount": 0,
"isDefault": true,
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the dashboard."
},
"name": {
"type": "string",
"description": "The dashboard's title."
},
"chartCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many charts the dashboard holds."
},
"isDefault": {
"type": "boolean",
"description": "Whether this is the dashboard the app opens by default. At most one dashboard per organization is the default."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the dashboard was created."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the dashboard was last changed."
}
},
"required": ["id", "name", "chartCount", "isDefault", "createdAt", "updatedAt"],
"additionalProperties": false,
"description": "The updated dashboard."
}DELETE /dashboards/{id}
Delete a dashboard and every chart on it. Each deletion is recorded in the audit log.
Requires the analytics:write permission.
Path Parameters
idstring (uuid)requiredThe ID of the dashboard to delete.
curl \
-X DELETE \
"https://api.cascade.dev/dashboards/550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@cascade-commerce/api/admin/client"
import { deleteDashboardsById } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await deleteDashboardsById({
path: { id: "550e8400-e29b-41d4-a716-446655440000" },
})require "net/http"
uri = URI("https://api.cascade.dev/dashboards/550e8400-e29b-41d4-a716-446655440000")
req = Net::HTTP::Delete.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
req = Request(
"https://api.cascade.dev/dashboards/550e8400-e29b-41d4-a716-446655440000",
method="DELETE",
headers={"Authorization": "Bearer sk_YOUR_SECRET_KEY"},
)
with urlopen(req) as res:
print(json.load(res)){
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"chartCount": 0,
"isDefault": true,
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the dashboard."
},
"name": {
"type": "string",
"description": "The dashboard's title."
},
"chartCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many charts the dashboard holds."
},
"isDefault": {
"type": "boolean",
"description": "Whether this is the dashboard the app opens by default. At most one dashboard per organization is the default."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the dashboard was created."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the dashboard was last changed."
}
},
"required": ["id", "name", "chartCount", "isDefault", "createdAt", "updatedAt"],
"additionalProperties": false,
"description": "The deleted dashboard."
}PUT /dashboards/{id}/layout
Replace the dashboard's whole grid layout in one write, so a drag session is one request. The payload has to place every chart on the dashboard exactly once, inside the 12 column grid.
Requires the analytics:write permission.
Path Parameters
idstring (uuid)requiredThe ID of the dashboard.
Body Parameters
dataobject[]requiredWhere every chart on the dashboard sits. The whole layout is replaced in one write, so one drag session is one request.
curl \
-X PUT \
"https://api.cascade.dev/dashboards/550e8400-e29b-41d4-a716-446655440000/layout" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": [
{
"chartId": "550e8400-e29b-41d4-a716-446655440000",
"x": 0,
"y": 0,
"w": 0,
"h": 0
}
]
}'import { client } from "@cascade-commerce/api/admin/client"
import { putDashboardsByIdLayout } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await putDashboardsByIdLayout({
path: { id: "550e8400-e29b-41d4-a716-446655440000" },
body: {
data: [
{
chartId: "550e8400-e29b-41d4-a716-446655440000",
x: 0,
y: 0,
w: 0,
h: 0,
},
],
},
})require "net/http"
uri = URI("https://api.cascade.dev/dashboards/550e8400-e29b-41d4-a716-446655440000/layout")
req = Net::HTTP::Put.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"data": [
{
"chartId": "550e8400-e29b-41d4-a716-446655440000",
"x": 0,
"y": 0,
"w": 0,
"h": 0
}
]
}
JSON
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
body = """{
"data": [
{
"chartId": "550e8400-e29b-41d4-a716-446655440000",
"x": 0,
"y": 0,
"w": 0,
"h": 0
}
]
}"""
req = Request(
"https://api.cascade.dev/dashboards/550e8400-e29b-41d4-a716-446655440000/layout",
data=body.encode(),
method="PUT",
headers={
"Authorization": "Bearer sk_YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
)
with urlopen(req) as res:
print(json.load(res)){
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"chartCount": 0,
"isDefault": true,
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z",
"charts": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"dashboardId": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"resource": "reviews",
"chartType": "stat",
"query": {
"measures": "...",
"groupBy": "...",
"granularity": "...",
"filter": "...",
"limit": "..."
},
"layout": {
"x": "...",
"y": "...",
"w": "...",
"h": "..."
},
"invalidReason": "string",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
}
]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the dashboard."
},
"name": {
"type": "string",
"description": "The dashboard's title."
},
"chartCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many charts the dashboard holds."
},
"isDefault": {
"type": "boolean",
"description": "Whether this is the dashboard the app opens by default. At most one dashboard per organization is the default."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the dashboard was created."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the dashboard was last changed."
},
"charts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the saved chart."
},
"dashboardId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "The dashboard the chart belongs to, for its whole life."
},
"name": {
"type": "string",
"description": "The title shown above the chart."
},
"resource": {
"type": "string",
"enum": [
"reviews",
"review-answers",
"orders",
"customers",
"products",
"loyalty-members",
"loyalty-activity",
"loyalty-rewards"
],
"description": "The resource to aggregate. Call `/analytics/schema` for what each one offers."
},
"chartType": {
"type": "string",
"enum": ["stat", "line", "area", "bar", "donut", "stackedBar", "groupedBar", "table"],
"description": "How the chart is drawn. It has to suit the query it is saved with: a `donut` needs one counting measure grouped by one non-date dimension, a `stat` needs no grouping at all, and `table` always fits."
},
"query": {
"type": "object",
"properties": {
"measures": {
"minItems": 1,
"type": "array",
"items": {
"type": "string"
},
"description": "The measures to report, by name. They have to be ones the resource offers."
},
"groupBy": {
"description": "The dimensions to group by, by name, at most three and at most one of them a date. Omit for a single total row.",
"type": "array",
"items": {
"type": "string"
}
},
"granularity": {
"type": "string",
"enum": ["hour", "day", "week", "month", "quarter", "year"],
"description": "The bucket size for a date dimension. Defaults to `day`. Ignored when nothing in `groupBy` is a date."
},
"filter": {
"description": "The filter document to apply, in the shape the resource's list endpoint takes. It is validated against the fields that screen allows.",
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
"limit": {
"description": "How many distinct group-by combinations to return, from 1 to 500.",
"type": "integer",
"minimum": 1,
"maximum": 500
}
},
"required": ["measures"],
"additionalProperties": false,
"description": "What the chart asks for. Send it to `/analytics/query` alongside `resource` to draw it."
},
"layout": {
"type": "object",
"properties": {
"x": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991,
"description": "The column the chart starts at, on a 12 column grid."
},
"y": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991,
"description": "The row the chart starts at."
},
"w": {
"type": "integer",
"minimum": 2,
"maximum": 12,
"description": "How many columns the chart spans, at least 2."
},
"h": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "How many rows tall the chart is. One row renders as 90 pixels."
}
},
"required": ["x", "y", "w", "h"],
"additionalProperties": false,
"description": "Where the chart sits on the dashboard's grid."
},
"invalidReason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Why the chart can no longer run, or null when it is fine. Definitions are validated on write, so this only fills in when a resource, measure or dimension the chart names was removed afterward."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the chart was saved."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the chart was last changed."
}
},
"required": [
"id",
"dashboardId",
"name",
"resource",
"chartType",
"query",
"layout",
"invalidReason",
"createdAt",
"updatedAt"
],
"additionalProperties": false
},
"description": "The dashboard's charts, in reading order: by row first, then by column."
}
},
"required": ["id", "name", "chartCount", "isDefault", "createdAt", "updatedAt", "charts"],
"additionalProperties": false,
"description": "The dashboard with its charts in the new order."
}