The Speak AI API exposes 3 dashboards endpoints under the base URL https://api.speakai.co/v1. Every request needs the x-speakai-key and x-access-token headers described in Authentication.
Create and update analytics dashboards, including the viewer settings that decide what a dashboard’s share link shows: custom fields, the Feedback button, labels, comments, and, through the feedback submitter names, who a viewer may write as.
Labels and comments on a share link
| Setting | Shape | Rule |
|---|---|---|
labels |
{ isEnabled, mode: "view" | "apply", labelGroupIds } |
labelGroupIds (at most 100) must be active label groups of your company (400 on save). Empty means every active label. |
comments |
{ isEnabled, mode: "view" | "reply" } |
Each section replaces itself when sent and keeps its stored value when left out. Everything is off and read-only until you turn it on. Viewers use the shared link routes.
What can you do with the dashboards endpoints?
Speak AI groups these 3 endpoints under the dashboards resource. Each entry below links to the full reference for that endpoint further down this page.
Create a dashboard
/dashboardsCreate an analytics dashboard. Only spec is required. Creating a dashboard does not share it; the share link is made from the app.
Viewer settings
settings controls what viewers of the dashboard’s share link see on each media page. Each section (fields, feedback, labels, comments) is saved on its own: the section you send replaces the stored one in full, and a section you leave out keeps its stored value. Read the dashboard first and resend every key of the section you change.
Labels and comments are off until you turn them on. With labels.mode set to apply or comments.mode set to reply, a viewer picks one of the dashboard’s feedback submitter names (feedback.submitters, even while feedback itself is off), or types another name when feedback.allowOtherSubmitter is on, and writes under that name. With no names and other names off, nobody can write. The link has no sign-in, so these writes are not authenticated: the link holder chooses the name and Speak AI records the entry under it without checking who sent it, the same as feedback. Only the dashboard’s feedback names are accepted, plus any other name when feedback.allowOtherSubmitter is on. Speak AI accepts this risk by design and limits it: every entry written through the link is shown as made via the dashboard, a name can change or delete only the entries written under it from that dashboard, a name that is not allowed is refused on every write, and revoking the link stops every write at once. List only the names you are happy for link holders to write as.
Errors
400when a field fails validation, the spec is invalid, the payload is too large, or a label group is not an active group of your company (“Choose active label groups from this workspace.”). Nothing is saved.
Request body
Fields marked required are the ones the server rejects the request without. Anything conditional, where a field becomes required only alongside another, is described under Request rules above rather than marked here.
| Field | Type | Description |
|---|---|---|
spec |
object, required | The dashboard itself: title (1 to 60 characters), description (up to 280), source ({"type": "workspace"}, {"type": "team"} or {"type": "folders", "folderIds": [...]}), dateRange ({"preset": "last30days"}), sections and widgets. It is checked against the dashboard spec schema, and unknown keys return 400. |
icon |
string | Icon identifier. Optional. An empty string is allowed. |
assignTo |
string[] | User ids, or group ids written as <groupId> (G), that can view the dashboard. Optional. |
filters |
object | Saved filters for the dashboard. Optional. Up to 100 keys; filterList holds up to 20 filters. |
settings |
object | What viewers of the dashboard’s share link see on each media page. Optional. Every section is saved on its own: a section you send replaces that whole section, and a section you leave out keeps its stored value. Unknown keys return 400. |
settings.fields |
object | Which custom fields the media page shows, and how its Fields tab groups them. |
settings.feedback |
object | The Feedback button on the media page. |
settings.labels |
object | Labels on the shared media pages. Off unless you turn it on. |
settings.comments |
object | Comments on the shared media pages. Off unless you turn it on. |
settings.profiles |
object[] | Profiles: an image, a description and a link for values of a custom field, such as speakers, events or sponsors. Up to 5 groups, one per field (a field may appear only once). Sending profiles replaces the whole list. On an update it also needs the dashboard’s current revision at the top level of the body: without it the request returns 400, and a stale one returns 409. Saving only profiles does not change which fields media pages show. Names, images, descriptions and links are visible to anyone with the dashboard link. Optional. |
isDefault |
boolean | Make this your default dashboard. Setting it clears the flag on your other dashboards. Optional. |
settings.fields – 3 more fields
| Field | Type | Description |
|---|---|---|
includeIds |
string[], required | Field ids shown, in this order, private fields included. Empty shows the company’s public fields. Required in the section. Ids that are not fields of your company are dropped and listed in droppedFieldIds. |
orderIds |
string[] | Used only when includeIds is empty: these fields show first, in this order. Optional. |
groups |
object[], required | Pills on the Fields tab. Unique by key. Required in the section; send an empty array for none. |
groups[].key |
string, required | Stable key of the group, unique in the list. |
groups[].label |
string, required | Name shown on the pill. |
groups[].fieldIds |
string[], required | Field ids in the group, in order. At least one. |
settings.feedback – 9 more fields
| Field | Type | Description |
|---|---|---|
isEnabled |
boolean, required | Show the Feedback button. |
fieldIds |
string[], required | Fields a reviewer scores. Empty means every field the page shows. |
submitters |
string[], required | Names a reviewer picks from. Empty lets them type their own. |
removeReasons |
string[], required | Reasons for removing a call from scoring. Empty hides that option. |
reviewScope |
string | dashboard (the default) or company. Optional. One of: dashboard, company . |
allowOtherSubmitter |
boolean | Lets a reviewer type a name that is not in submitters. Optional. |
groups |
object[] | Pills in the Feedback dialog. Optional. |
groups[].key |
string, required | Stable key of the group, unique in the list. |
groups[].label |
string, required | Name shown on the pill. |
groups[].fieldIds |
string[], required | Field ids in the group, in order. At least one. |
fieldRules |
object | Per field id: an optional row label and the allowed min and max score. Optional. |
sheetWebhookUrl |
string | Google Apps Script web app URL that receives one row per submission. Never shown to viewers. Optional. |
settings.labels – 3 more fields
| Field | Type | Description |
|---|---|---|
isEnabled |
boolean, required | Show labels on media pages opened from the share link. |
mode |
string, required | view shows labels read-only. apply also lets a viewer add and remove labels under one of the reviewer names. One of: view, apply . |
labelGroupIds |
string[], required | Label groups the link shows and offers. Up to 100 group ids, no repeats. Empty means every active label. Each must be an active label group of your company, or the whole save returns 400 “Choose active label groups from this workspace.” Groups are refused rather than dropped, because dropping them all would widen the link to every label. If every chosen group is archived later, the link shows no labels. |
settings.comments – 2 more fields
| Field | Type | Description |
|---|---|---|
isEnabled |
boolean, required | Show comments on media pages opened from the share link. |
mode |
string, required | view shows comments read-only. reply also lets a viewer comment and reply under one of the reviewer names. One of: view, reply . |
settings.profiles – 4 more fields
| Field | Type | Description |
|---|---|---|
fieldId |
string, required | Id of the custom field the profiles are for. Required. |
label |
string, required | Name of the group, such as Speakers. 1 to 40 characters. Required. |
imageShape |
string | How images are cropped. Optional; round when left out. One of: round, square . |
entries |
object[], required | Up to 60 profiles. Required; send an empty array for none. |
entries[].id |
string | Stable id of the entry: lowercase letters, digits and hyphens, up to 64 characters. Optional. |
entries[].name |
string, required | The field value this profile is for, up to 80 characters. It matches the value ignoring case and extra spaces, and a name may appear only once in a group. Required. |
entries[].imageKey |
string | The imageKey returned by POST /dashboards/{dashboardId}/profile-image, or one read back from the dashboard unchanged. Optional. |
entries[].description |
string | Plain text shown with the profile, up to 600 characters. Optional. |
entries[].link |
string | An http or https link, up to 2048 characters. Optional. |
curl -X POST 'https://api.speakai.co/v1/dashboards' \
-H 'x-speakai-key: sk_test_speak_0000000000000000' \
-H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
-H 'Content-Type: application/json' \
-d '{
"spec": {
"title": "Sales calls",
"source": {
"type": "workspace"
},
"dateRange": {
"preset": "last30days"
},
"sections": [],
"widgets": []
},
"settings": {
"feedback": {
"isEnabled": true,
"fieldIds": [],
"submitters": [
"Reviewer A",
"Reviewer B"
],
"removeReasons": []
},
"labels": {
"isEnabled": true,
"mode": "apply",
"labelGroupIds": [
"Gr7kQ2pLm9xZ"
]
},
"comments": {
"isEnabled": true,
"mode": "reply"
}
}
}'200 Success
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | success on every 2xx response. One of: success . |
data |
object | |
data.dashboardId |
string | Id of the new dashboard. |
data.droppedFieldIds |
string[] | Field ids removed from settings because they are not fields of your company. Present only when some were removed. |
data.message |
string | Confirmation text. |
400 Validation failed
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | failed. One of: failed . |
requestId |
string | Id of this request, for support. |
code |
integer | The HTTP status code again. |
message |
string | What went wrong, in words you can show to a person. |
hints |
string[] | Generic hints for this kind of error. |
Update a dashboard
/dashboards/{dashboardId}Update a dashboard. Send only the keys you want to change. To change the dashboard itself, send the full spec with the revision you read: the write is accepted only while it still matches, and returns 409 otherwise. Settings, icon, sharing, filters and the default flag can be changed without a spec.
Viewer settings
settings controls what viewers of the dashboard’s share link see on each media page. Each section (fields, feedback, labels, comments, profiles) is saved on its own: the section you send replaces the stored one in full, and a section you leave out keeps its stored value. Read the dashboard first and resend every key of the section you change.
Labels and comments are off until you turn them on. With labels.mode set to apply or comments.mode set to reply, a viewer picks one of the dashboard’s feedback submitter names (feedback.submitters, even while feedback itself is off), or types another name when feedback.allowOtherSubmitter is on, and writes under that name. With no names and other names off, nobody can write. The link has no sign-in, so these writes are not authenticated: the link holder chooses the name and Speak AI records the entry under it without checking who sent it, the same as feedback. Only the dashboard’s feedback names are accepted, plus any other name when feedback.allowOtherSubmitter is on. Speak AI accepts this risk by design and limits it: every entry written through the link is shown as made via the dashboard, a name can change or delete only the entries written under it from that dashboard, a name that is not allowed is refused on every write, and revoking the link stops every write at once. List only the names you are happy for link holders to write as.
Errors
400when a field fails validation, the spec is invalid, the payload is too large, or a label group is not an active group of your company (“Choose active label groups from this workspace.”). Nothing is saved.404when the dashboard does not exist or you cannot edit it.409when the specrevisionis stale: “Dashboard was modified by another update. Reload and try again.”
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
dashboardId |
path | string | Yes | Id of the dashboard, as returned when it was created. |
Request body
Fields marked required are the ones the server rejects the request without. Anything conditional, where a field becomes required only alongside another, is described under Request rules above rather than marked here.
| Field | Type | Description |
|---|---|---|
spec |
object | The dashboard itself: title (1 to 60 characters), description (up to 280), source ({"type": "workspace"}, {"type": "team"} or {"type": "folders", "folderIds": [...]}), dateRange ({"preset": "last30days"}), sections and widgets. It is checked against the dashboard spec schema, and unknown keys return 400. When sent it must be the full spec, with revision. |
revision |
number | The dashboard’s current revision, a whole number. Required when you send settings.profiles without a spec; a stale one returns 409. |
icon |
string | Icon identifier. Optional. An empty string is allowed. |
assignTo |
string[] | User ids, or group ids written as <groupId> (G), that can view the dashboard. Optional. |
filters |
object | Saved filters for the dashboard. Optional. Up to 100 keys; filterList holds up to 20 filters. |
settings |
object | What viewers of the dashboard’s share link see on each media page. Optional. Every section is saved on its own: a section you send replaces that whole section, and a section you leave out keeps its stored value. Unknown keys return 400. |
settings.fields |
object | Which custom fields the media page shows, and how its Fields tab groups them. |
settings.feedback |
object | The Feedback button on the media page. |
settings.labels |
object | Labels on the shared media pages. Off unless you turn it on. |
settings.comments |
object | Comments on the shared media pages. Off unless you turn it on. |
settings.profiles |
object[] | Profiles: an image, a description and a link for values of a custom field, such as speakers, events or sponsors. Up to 5 groups, one per field (a field may appear only once). Sending profiles replaces the whole list. On an update it also needs the dashboard’s current revision at the top level of the body: without it the request returns 400, and a stale one returns 409. Saving only profiles does not change which fields media pages show. Names, images, descriptions and links are visible to anyone with the dashboard link. Optional. |
isDefault |
boolean | Make this your default dashboard. Setting it clears the flag on your other dashboards. Optional. |
settings.fields – 3 more fields
| Field | Type | Description |
|---|---|---|
includeIds |
string[], required | Field ids shown, in this order, private fields included. Empty shows the company’s public fields. Required in the section. Ids that are not fields of your company are dropped and listed in droppedFieldIds. |
orderIds |
string[] | Used only when includeIds is empty: these fields show first, in this order. Optional. |
groups |
object[], required | Pills on the Fields tab. Unique by key. Required in the section; send an empty array for none. |
groups[].key |
string, required | Stable key of the group, unique in the list. |
groups[].label |
string, required | Name shown on the pill. |
groups[].fieldIds |
string[], required | Field ids in the group, in order. At least one. |
settings.feedback – 9 more fields
| Field | Type | Description |
|---|---|---|
isEnabled |
boolean, required | Show the Feedback button. |
fieldIds |
string[], required | Fields a reviewer scores. Empty means every field the page shows. |
submitters |
string[], required | Names a reviewer picks from. Empty lets them type their own. |
removeReasons |
string[], required | Reasons for removing a call from scoring. Empty hides that option. |
reviewScope |
string | dashboard (the default) or company. Optional. One of: dashboard, company . |
allowOtherSubmitter |
boolean | Lets a reviewer type a name that is not in submitters. Optional. |
groups |
object[] | Pills in the Feedback dialog. Optional. |
groups[].key |
string, required | Stable key of the group, unique in the list. |
groups[].label |
string, required | Name shown on the pill. |
groups[].fieldIds |
string[], required | Field ids in the group, in order. At least one. |
fieldRules |
object | Per field id: an optional row label and the allowed min and max score. Optional. |
sheetWebhookUrl |
string | Google Apps Script web app URL that receives one row per submission. Never shown to viewers. Optional. |
settings.labels – 3 more fields
| Field | Type | Description |
|---|---|---|
isEnabled |
boolean, required | Show labels on media pages opened from the share link. |
mode |
string, required | view shows labels read-only. apply also lets a viewer add and remove labels under one of the reviewer names. One of: view, apply . |
labelGroupIds |
string[], required | Label groups the link shows and offers. Up to 100 group ids, no repeats. Empty means every active label. Each must be an active label group of your company, or the whole save returns 400 “Choose active label groups from this workspace.” Groups are refused rather than dropped, because dropping them all would widen the link to every label. If every chosen group is archived later, the link shows no labels. |
settings.comments – 2 more fields
| Field | Type | Description |
|---|---|---|
isEnabled |
boolean, required | Show comments on media pages opened from the share link. |
mode |
string, required | view shows comments read-only. reply also lets a viewer comment and reply under one of the reviewer names. One of: view, reply . |
settings.profiles – 4 more fields
| Field | Type | Description |
|---|---|---|
fieldId |
string, required | Id of the custom field the profiles are for. Required. |
label |
string, required | Name of the group, such as Speakers. 1 to 40 characters. Required. |
imageShape |
string | How images are cropped. Optional; round when left out. One of: round, square . |
entries |
object[], required | Up to 60 profiles. Required; send an empty array for none. |
entries[].id |
string | Stable id of the entry: lowercase letters, digits and hyphens, up to 64 characters. Optional. |
entries[].name |
string, required | The field value this profile is for, up to 80 characters. It matches the value ignoring case and extra spaces, and a name may appear only once in a group. Required. |
entries[].imageKey |
string | The imageKey returned by POST /dashboards/{dashboardId}/profile-image, or one read back from the dashboard unchanged. Optional. |
entries[].description |
string | Plain text shown with the profile, up to 600 characters. Optional. |
entries[].link |
string | An http or https link, up to 2048 characters. Optional. |
curl -X PUT 'https://api.speakai.co/v1/dashboards/Db4kX9mQ2pLr' \
-H 'x-speakai-key: sk_test_speak_0000000000000000' \
-H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
-H 'Content-Type: application/json' \
-d '{
"settings": {
"labels": {
"isEnabled": true,
"mode": "apply",
"labelGroupIds": [
"Gr7kQ2pLm9xZ"
]
},
"comments": {
"isEnabled": true,
"mode": "reply"
}
}
}'200 Success
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | success on every 2xx response. One of: success . |
data |
object | |
data.dashboardId |
string | Id of the dashboard. |
data.revision |
integer | The dashboard’s revision after the update. |
data.droppedFieldIds |
string[] | Field ids removed from settings because they are not fields of your company. Present only when some were removed. |
data.message |
string | Confirmation text. |
400 Validation failed
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | failed. One of: failed . |
requestId |
string | Id of this request, for support. |
code |
integer | The HTTP status code again. |
message |
string | What went wrong, in words you can show to a person. |
hints |
string[] | Generic hints for this kind of error. |
404 The dashboard does not exist or you cannot edit it
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | failed. One of: failed . |
requestId |
string | Id of this request, for support. |
code |
integer | The HTTP status code again. |
message |
string | What went wrong, in words you can show to a person. |
hints |
string[] | Generic hints for this kind of error. |
409 The spec revision is stale
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | failed. One of: failed . |
requestId |
string | Id of this request, for support. |
code |
integer | The HTTP status code again. |
message |
string | What went wrong, in words you can show to a person. |
hints |
string[] | Generic hints for this kind of error. |
Upload a profile image
/dashboards/{dashboardId}/profile-imageUpload one image for a dashboard profile, then put the returned imageKey on the profile entry in settings.profiles and save the dashboard with PUT /dashboards/{dashboardId}. Uploading alone does not change the dashboard.
Send the file as multipart form data, for example -F 'image=@speaker.png'. Send exactly one JPEG, PNG, WebP or GIF image of up to 2 MB. The server checks the file’s content, not its name or declared type.
Only people who can edit the dashboard can upload. Each user can upload up to 30 images in 10 minutes.
Errors
400when there is not exactly one file, or the file is not a JPEG, PNG, WebP or GIF of up to 2 MB: “Upload one JPEG, PNG, WEBP, or GIF image of up to 2 MB.”404when the dashboard does not exist or you cannot edit it.429after 30 uploads in 10 minutes.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
dashboardId |
path | string | Yes | Id of the dashboard, as returned when it was created. |
curl -X POST 'https://api.speakai.co/v1/dashboards/Db4kX9mQ2pLr/profile-image' \
-H 'x-speakai-key: sk_test_speak_0000000000000000' \
-H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
-F 'image='200 Success
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | success on every 2xx response. One of: success . |
data |
object | |
data.imageKey |
string | Where the image is stored. Put it on a profile entry as imageKey and save the dashboard. |
data.imageUrl |
string | A signed link to preview the image. It expires, so do not store it. |
400 Not exactly one JPEG, PNG, WebP or GIF image of up to 2 MB
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | failed. One of: failed . |
requestId |
string | Id of this request, for support. |
code |
integer | The HTTP status code again. |
message |
string | What went wrong, in words you can show to a person. |
hints |
string[] | Generic hints for this kind of error. |
404 The dashboard does not exist or you cannot edit it
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | failed. One of: failed . |
requestId |
string | Id of this request, for support. |
code |
integer | The HTTP status code again. |
message |
string | What went wrong, in words you can show to a person. |
hints |
string[] | Generic hints for this kind of error. |
429 More than 30 uploads in 10 minutes
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | failed. One of: failed . |
requestId |
string | Id of this request, for support. |
code |
integer | The HTTP status code again. |
message |
string | What went wrong, in words you can show to a person. |
hints |
string[] | Generic hints for this kind of error. |
Related pages
- API reference for the base URL, authentication, and the error format.
- Authenticate with the Speak AI API using access tokens
- Upload audio and video to Speak AI and read insights
- Create and update live transcription sessions
- Analyze text notes with the Speak AI text endpoints
Get an API key on the Speak AI developer page.