Saved charts

GET /saved-charts

List the organization's saved charts across every dashboard. Filter by dashboardId to read one dashboard's charts, or use the dashboard's detail endpoint to get them in reading order.

Requires the analytics:read permission.

Query parameters

limitnumber

The number of records to return.

cursorstring

A pagination cursor. Fetch the next page by passing the nextCursor value from the previous response.

sortname | -name | createdAt | -createdAt

The sort order. Prefix a field with - to sort descending. Defaults to -createdAt.

filterstring

A JSON-encoded filter document, for example {"createdAt":{"$gte":"2025-01-01T00:00:00Z"}}. Filterable fields: id, dashboardId, name, resource, chartType, createdAt. See Filtering for the syntax.

Request
curl \
  "https://your-store.meisei.app/api/saved-charts" \
  -H "Authorization: Bearer sk_YOUR_SECRET_KEY"
Response
{
  "data": [
    {
      "id": "A4Ws1igz",
      "dashboardId": "A4Ws1igz",
      "name": "string",
      "resource": "reviews",
      "chartType": "stat",
      "query": {
        "measures": "...",
        "groupBy": "...",
        "granularity": "...",
        "filter": "...",
        "limit": "...",
        "lastDays": "..."
      },
      "layout": {
        "x": "...",
        "y": "...",
        "w": "...",
        "h": "..."
      },
      "invalidReason": "string",
      "feedCreatedAt": "2025-01-15T09:30:00Z",
      "createdAt": "2025-01-15T09:30:00Z",
      "updatedAt": "2025-01-15T09:30:00Z"
    }
  ],
  "nextCursor": "string"
}

POST /saved-charts

Save a chart onto a dashboard. The query document is validated against what the resource can measure and group by, its filter against the fields that resource's list endpoint allows, and the chart type against the shape the query returns, so a chart that would error every time it is opened is rejected here instead. Omit layout to append the chart at the bottom of the dashboard.

Requires the analytics:write permission.

Body parameters

dashboardIdstringrequired

The dashboard the chart goes on. It cannot be moved to another one later.

namestringrequired

The title to show above the chart.

resourcereviews | review-answers | orders | customers | products | loyalty-members | loyalty-activity | loyalty-rewardsrequired

The resource to aggregate. Call /analytics/schema for what each one offers.

chartTypestat | line | area | bar | donut | stackedBar | groupedBar | tablerequired

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.

queryobjectrequired

What the chart asks for.

layoutobject

Where the chart sits on the dashboard's grid. Omit it to append the chart at the bottom, half width.

Request
curl \
  -X POST \
  "https://your-store.meisei.app/api/saved-charts" \
  -H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "dashboardId": "string",
  "name": "string",
  "resource": "reviews",
  "chartType": "stat",
  "query": {
    "measures": [
      "string"
    ],
    "groupBy": [
      "string"
    ],
    "granularity": "hour",
    "filter": "...",
    "limit": 0,
    "lastDays": 0
  },
  "layout": {
    "x": 0,
    "y": 0,
    "w": 0,
    "h": 0
  }
}'
Response
{
  "id": "A4Ws1igz",
  "dashboardId": "A4Ws1igz",
  "name": "string",
  "resource": "reviews",
  "chartType": "stat",
  "query": {
    "measures": ["string"],
    "groupBy": ["string"],
    "granularity": "hour",
    "filter": "...",
    "limit": 0,
    "lastDays": 0
  },
  "layout": {
    "x": 0,
    "y": 0,
    "w": 0,
    "h": 0
  },
  "invalidReason": "string",
  "feedCreatedAt": "2025-01-15T09:30:00Z",
  "createdAt": "2025-01-15T09:30:00Z",
  "updatedAt": "2025-01-15T09:30:00Z"
}

POST /saved-charts/preview

Check a chart definition without saving it. It runs exactly the validation POST /saved-charts runs, and reports which chart types can draw the query, so a type that does not fit is caught before anything is written. Nothing is stored and nothing is recorded.

Requires the analytics:read permission.

Body parameters

resourcereviews | review-answers | orders | customers | products | loyalty-members | loyalty-activity | loyalty-rewardsrequired

The resource to aggregate. Call /analytics/schema for what each one offers.

chartTypestat | line | area | bar | donut | stackedBar | groupedBar | table

The chart type you are thinking of. Leave it off to only ask which types fit.

queryobjectrequired

What the chart would ask for.

Request
curl \
  -X POST \
  "https://your-store.meisei.app/api/saved-charts/preview" \
  -H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "resource": "reviews",
  "chartType": "stat",
  "query": {
    "measures": [
      "string"
    ],
    "groupBy": [
      "string"
    ],
    "granularity": "hour",
    "filter": "...",
    "limit": 0,
    "lastDays": 0
  }
}'
Response
{
  "valid": true,
  "reason": "string",
  "chartTypes": ["stat"],
  "recommendedChartType": "stat"
}

GET /saved-charts/{id}

Get a single saved chart by ID.

Requires the analytics:read permission.

Path parameters

idstringrequired

The ID of the saved chart.

Request
curl \
  "https://your-store.meisei.app/api/saved-charts/A4Ws1igz" \
  -H "Authorization: Bearer sk_YOUR_SECRET_KEY"
Response
{
  "id": "A4Ws1igz",
  "dashboardId": "A4Ws1igz",
  "name": "string",
  "resource": "reviews",
  "chartType": "stat",
  "query": {
    "measures": ["string"],
    "groupBy": ["string"],
    "granularity": "hour",
    "filter": "...",
    "limit": 0,
    "lastDays": 0
  },
  "layout": {
    "x": 0,
    "y": 0,
    "w": 0,
    "h": 0
  },
  "invalidReason": "string",
  "feedCreatedAt": "2025-01-15T09:30:00Z",
  "createdAt": "2025-01-15T09:30:00Z",
  "updatedAt": "2025-01-15T09:30:00Z"
}

PUT /saved-charts/{id}

Rename a saved chart, change how it is drawn, or replace what it asks for. The resource it aggregates and the dashboard it belongs to cannot be changed, and its position is written through the dashboard's layout endpoint. The whole definition is revalidated, so a new query the saved chart type cannot draw is rejected.

Requires the analytics:write permission.

Path parameters

idstringrequired

The ID of the saved chart to update.

Body parameters

namestring

The title shown above the chart.

chartTypestat | line | area | bar | donut | stackedBar | groupedBar | table

How the chart is drawn. It still has to suit the query. Omit to leave it alone.

queryobject

Replaces what the chart asks for, wholesale. The resource cannot be changed. Omit to leave it alone.

Request
curl \
  -X PUT \
  "https://your-store.meisei.app/api/saved-charts/A4Ws1igz" \
  -H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "string",
  "chartType": "stat",
  "query": {
    "measures": [
      "string"
    ],
    "groupBy": [
      "string"
    ],
    "granularity": "hour",
    "filter": "...",
    "limit": 0,
    "lastDays": 0
  }
}'
Response
{
  "id": "A4Ws1igz",
  "dashboardId": "A4Ws1igz",
  "name": "string",
  "resource": "reviews",
  "chartType": "stat",
  "query": {
    "measures": ["string"],
    "groupBy": ["string"],
    "granularity": "hour",
    "filter": "...",
    "limit": 0,
    "lastDays": 0
  },
  "layout": {
    "x": 0,
    "y": 0,
    "w": 0,
    "h": 0
  },
  "invalidReason": "string",
  "feedCreatedAt": "2025-01-15T09:30:00Z",
  "createdAt": "2025-01-15T09:30:00Z",
  "updatedAt": "2025-01-15T09:30:00Z"
}

DELETE /saved-charts/{id}

Delete a saved chart from its dashboard. The deletion is recorded in the audit log.

Requires the analytics:write permission.

Path parameters

idstringrequired

The ID of the saved chart to delete.

Request
curl \
  -X DELETE \
  "https://your-store.meisei.app/api/saved-charts/A4Ws1igz" \
  -H "Authorization: Bearer sk_YOUR_SECRET_KEY"
Response
{
  "id": "A4Ws1igz",
  "dashboardId": "A4Ws1igz",
  "name": "string",
  "resource": "reviews",
  "chartType": "stat",
  "query": {
    "measures": ["string"],
    "groupBy": ["string"],
    "granularity": "hour",
    "filter": "...",
    "limit": 0,
    "lastDays": 0
  },
  "layout": {
    "x": 0,
    "y": 0,
    "w": 0,
    "h": 0
  },
  "invalidReason": "string",
  "feedCreatedAt": "2025-01-15T09:30:00Z",
  "createdAt": "2025-01-15T09:30:00Z",
  "updatedAt": "2025-01-15T09:30:00Z"
}

GET /saved-charts/{id}/data

Read a saved chart's rows, the same ones /analytics/query returns for its definition, so another tool can show what the dashboard shows from one address. from and to narrow it to a time range, which is how a BI tool's time picker reaches it. Answers are kept for up to a minute, and a request answered from that copy costs 1 rather than 5.

Requires the analytics:read permission.

Costs 5 rate limit tokens instead of the usual one. See Rate limits.

Path parameters

idstringrequired

The ID of the saved chart.

Query parameters

fromstring (date-time)

Only count records at or after this instant, as an ISO 8601 date and time. It applies to timeField, beside the filter, and a grouped date's series then starts here.

tostring (date-time)

Only count records before this instant, as an ISO 8601 date and time. It applies to timeField, beside the filter, and a grouped date's series then ends here.

timeFieldstring

The date from and to apply to, by dimension name. Defaults to the date in groupBy, or to createdAt when nothing is grouped by a date.

timezonestring

The time zone to bucket date dimensions in, as an IANA name such as America/New_York. Defaults to the organization's own time zone.

includeOtherstring

Set to false to leave out the Other row the chart collapses its smaller values into, so every value is a real one. With no limit on the chart, every combination comes back.

formatjson | csv

Answer as csv rather than json: one header row naming the columns, then one line per row. Sending Accept: text/csv does the same.

Request
curl \
  "https://your-store.meisei.app/api/saved-charts/A4Ws1igz/data" \
  -H "Authorization: Bearer sk_YOUR_SECRET_KEY"
Response
{
  "data": ["..."],
  "meta": {
    "resource": "reviews",
    "timezone": "string",
    "granularity": "hour",
    "dimensions": [
      {
        "name": "...",
        "label": "...",
        "type": "...",
        "association": "...",
        "valueLabels": "..."
      }
    ],
    "measures": [
      {
        "name": "...",
        "label": "...",
        "type": "...",
        "description": "..."
      }
    ],
    "truncated": true,
    "otherLabel": "string"
  }
}

POST /saved-charts/{id}/feed

Make a data link for a saved chart: an address that answers with the chart's rows to anyone who has it, for a spreadsheet or BI tool that cannot send an API key. The token is returned once, here, and making a new link turns the old one off. Making one is recorded in the audit log.

Requires the analytics:write permission.

Path parameters

idstringrequired

The ID of the saved chart.

Request
curl \
  -X POST \
  "https://your-store.meisei.app/api/saved-charts/A4Ws1igz/feed" \
  -H "Authorization: Bearer sk_YOUR_SECRET_KEY"
Response
{
  "token": "string",
  "url": "string",
  "createdAt": "2025-01-15T09:30:00Z"
}

DELETE /saved-charts/{id}/feed

Turn off a saved chart's data link. Anything still reading it gets a 404 from then on. Turning one off is recorded in the audit log.

Requires the analytics:write permission.

Path parameters

idstringrequired

The ID of the saved chart.

Request
curl \
  -X DELETE \
  "https://your-store.meisei.app/api/saved-charts/A4Ws1igz/feed" \
  -H "Authorization: Bearer sk_YOUR_SECRET_KEY"
Response
{
  "id": "A4Ws1igz",
  "dashboardId": "A4Ws1igz",
  "name": "string",
  "resource": "reviews",
  "chartType": "stat",
  "query": {
    "measures": ["string"],
    "groupBy": ["string"],
    "granularity": "hour",
    "filter": "...",
    "limit": 0,
    "lastDays": 0
  },
  "layout": {
    "x": 0,
    "y": 0,
    "w": 0,
    "h": 0
  },
  "invalidReason": "string",
  "feedCreatedAt": "2025-01-15T09:30:00Z",
  "createdAt": "2025-01-15T09:30:00Z",
  "updatedAt": "2025-01-15T09:30:00Z"
}