Help desk operations

Contacts, the embeddable widget, file attachments, reactions, the queue, and how the desk is doing.

The day-to-day pieces around conversations.

Contacts belong to the help desk's organization rather than to one desk, so the same customer writing to two desks is one person with one history. A contact created through the API with an externalId is identified by it, and the same externalId always comes back to the same contact. Editing a contact never changes whether it is identified.

Widgets are the messenger you embed on your own site. A widget's apiKey is publishable: it sits in your page source and authorizes nothing on its own. The identity-verification secret is the opposite. It is returned once, when you generate it, and can never be read again.

Attachments are uploaded first and sent second. Upload a file against a conversation, then name it in attachmentIds when you send a message; nobody sees it until then. Files are downloaded through the API with the same key, always as a download, with their stored content type.

Writes made as someone (uploads, reactions, claiming from the queue, editing contacts) need the key's owner to be an agent on the desk. Widget changes and the audit log need a desk lead or admin.

List contacts#

Contacts this desk can see, most recently active first. Look one up by `externalId` or `email`, or search across name, email, external id and company. For a desk shared with you by another organization, only people with a conversation on that desk are included.

GET/api/v1/help-desks/{helpDeskId}/contacts

Path parameters

helpDeskIdstringrequired
Help desk id.

Query parameters

externalIdstring
Your own id for the person. Exact match.
emailstring
Exact match, ignoring case.
searchstring
Matches name, email, external id or company name.
limitnumber
Page size, up to 200.
Default50
cursorstring
`meta.pagination.nextCursor` from the previous page.

Responses

A page of contacts.

curl -X GET "https://voice.evryhealth.com/api/v1/help-desks/{helpDeskId}/contacts" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"data": {
"contacts": [
{
"id": "f0d2…",
"organizationId": "77c1…",
"companyId": "9a40…",
"externalId": "cust-10442",
"email": "casey@example.com",
"phone": "+15555550100",
"name": "Casey Rivera",
"avatarUrl": null,
"locale": "en-US",
"timeZone": "America/Chicago",
"isIdentified": true,
"identifiedAt": "2026-09-02T10:11:00.000Z",
"attributes": {
"plan": "gold"
},
"firstSeenAt": "2026-09-02T10:11:00.000Z",
"lastSeenAt": "2026-09-30T15:04:52.000Z",
"sessionCount": 4,
"lastPageUrl": null,
"lastUserAgent": null,
"lastIpCountry": "US",
"isBlocked": false,
"company": {
"id": "9a40…",
"name": "Rivera Dental",
"externalId": "co-88",
"domain": "riveradental.com",
"planName": "Group",
"monthlySpend": 1200,
"attributes": {}
}
}
]
},
"meta": {
"requestId": "req_…",
"pagination": {
"nextCursor": "bzo1MA",
"limit": 50
}
}
}

Get a contact#

One contact, with their 20 most recent conversations on this desk.

GET/api/v1/help-desks/{helpDeskId}/contacts/{contactId}

Path parameters

helpDeskIdstringrequired
Help desk id.
contactIdstringrequired
Contact id.

Responses

The contact and their recent conversations here.

curl -X GET "https://voice.evryhealth.com/api/v1/help-desks/{helpDeskId}/contacts/{contactId}" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"data": {
"contact": {
"id": "f0d2…",
"organizationId": "77c1…",
"companyId": "9a40…",
"externalId": "cust-10442",
"email": "casey@example.com",
"phone": "+15555550100",
"name": "Casey Rivera",
"avatarUrl": null,
"locale": "en-US",
"timeZone": "America/Chicago",
"isIdentified": true,
"identifiedAt": "2026-09-02T10:11:00.000Z",
"attributes": {
"plan": "gold"
},
"firstSeenAt": "2026-09-02T10:11:00.000Z",
"lastSeenAt": "2026-09-30T15:04:52.000Z",
"sessionCount": 4,
"lastPageUrl": null,
"lastUserAgent": null,
"lastIpCountry": "US",
"isBlocked": false,
"company": {
"id": "9a40…",
"name": "Rivera Dental",
"externalId": "co-88",
"domain": "riveradental.com",
"planName": "Group",
"monthlySpend": 1200,
"attributes": {}
}
},
"conversations": [
{
"id": "2b6f…",
"subject": "Card declined at checkout",
"status": "open",
"priority": "high",
"createdAt": "2026-09-30T15:04:52.000Z"
}
]
},
"meta": {
"requestId": "req_…"
}
}

Update a contact#

Correct a contact's details. Only the fields you send change. `attributes` replaces the stored set as a whole. `companyId` must be a company in the same organization. Blocking stops new conversations from the contact; it does not hide existing ones. Needs `help_desk:configure` and the key's owner must be an agent on the desk.

PATCH/api/v1/help-desks/{helpDeskId}/contacts/{contactId}

Path parameters

helpDeskIdstringrequired
Help desk id.
contactIdstringrequired
Contact id.

Body

application/json
name
string
email
string
phone
string
companyId
string
locale
string
timeZone
string (IANA)
attributes
object
isBlocked
boolean

Responses

The updated contact.

curl -X PATCH "https://voice.evryhealth.com/api/v1/help-desks/{helpDeskId}/contacts/{contactId}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Casey Rivera",
"attributes": {
"plan": "gold"
}
}'
Response200
{
"data": {
"id": "f0d2…",
"organizationId": "77c1…",
"companyId": "9a40…",
"externalId": "cust-10442",
"email": "casey@example.com",
"phone": "+15555550100",
"name": "Casey Rivera",
"avatarUrl": null,
"locale": "en-US",
"timeZone": "America/Chicago",
"isIdentified": true,
"identifiedAt": "2026-09-02T10:11:00.000Z",
"attributes": {
"plan": "gold"
},
"firstSeenAt": "2026-09-02T10:11:00.000Z",
"lastSeenAt": "2026-09-30T15:04:52.000Z",
"sessionCount": 4,
"lastPageUrl": null,
"lastUserAgent": null,
"lastIpCountry": "US",
"isBlocked": false,
"company": {
"id": "9a40…",
"name": "Rivera Dental",
"externalId": "co-88",
"domain": "riveradental.com",
"planName": "Group",
"monthlySpend": 1200,
"attributes": {}
}
},
"meta": {
"requestId": "req_…"
}
}

List widgets#

The desk's active widgets. The identity secret is never included; `hasIdentitySecret` says whether one is set.

GET/api/v1/help-desks/{helpDeskId}/widgets

Path parameters

helpDeskIdstringrequired
Help desk id.

Responses

The widgets.

curl -X GET "https://voice.evryhealth.com/api/v1/help-desks/{helpDeskId}/widgets" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"data": {
"widgets": [
{
"id": "c3e8…",
"helpDeskId": "8d1e…",
"brandId": null,
"name": "Member portal",
"apiKey": "hdw_Zk3m…",
"theme": {
"accent": "#2563EB",
"accentForeground": "#FFFFFF",
"mode": "light",
"header": {
"style": "gradient",
"fade": true
},
"radiusScale": 1,
"fontSizeScale": 1,
"shadow": true
},
"launcher": {
"style": "bubble",
"icon": "chat",
"side": "right",
"offsetX": 20,
"offsetY": 20,
"size": 56,
"badge": "count"
},
"home": {
"cards": [
{
"type": "greeting",
"id": "hello",
"headline": "Hi there",
"showTeammates": true
},
{
"type": "article_search",
"id": "search",
"placeholder": "Search for help"
}
]
},
"navigation": {
"tabs": [
"home",
"messages",
"help"
]
},
"features": {
"attachments": true,
"maxAttachmentMb": 10,
"emoji": true,
"reactions": false,
"csat": true,
"emailTranscript": true,
"showAgentTyping": true,
"showContactTypingToAgent": true,
"readReceipts": true,
"articleSearch": true,
"news": false,
"voiceCalls": false,
"videoCalls": false,
"screenShare": false,
"recordLiveSessions": false,
"soundOnMessage": true,
"allowReopen": true
},
"localization": {
"fr": {
"home.greeting": "Bonjour.",
"nav.help": "Aide"
}
},
"preChatForm": {
"enabled": false,
"fields": []
},
"offlineForm": {
"enabled": true,
"fields": [
{
"builtin": "email",
"required": true
}
]
},
"customCss": null,
"allowedOrigins": [
"https://members.example.com"
],
"requireIdentityVerification": true,
"hasIdentitySecret": true,
"scheduleId": null,
"defaultFormId": null,
"hideBranding": false,
"isActive": true
}
]
},
"meta": {
"requestId": "req_…"
}
}

Create a widget#

A new widget with a fresh publishable key. Every section is optional and starts from the defaults. `allowedOrigins` takes full origins (`https://app.example.com`), bare hosts, or `*.example.com`; an empty list allows any site. Desk leads and admins only.

POST/api/v1/help-desks/{helpDeskId}/widgets

Path parameters

helpDeskIdstringrequired
Help desk id.

Body

application/json
name
string
required
brandId
string | null
theme
object
launcher
object
home
object
navigation
object
features
object
localization
object
preChatForm
object
offlineForm
object
customCss
string | null
allowedOrigins
string[]
requireIdentityVerification
boolean
scheduleId
string | null
defaultFormId
string | null
hideBranding
boolean

Responses

The created widget.

curl -X POST "https://voice.evryhealth.com/api/v1/help-desks/{helpDeskId}/widgets" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Member portal",
"allowedOrigins": [
"https://members.example.com"
],
"requireIdentityVerification": true
}'
Response201
{
"data": {
"id": "c3e8…",
"helpDeskId": "8d1e…",
"brandId": null,
"name": "Member portal",
"apiKey": "hdw_Zk3m…",
"theme": {
"accent": "#2563EB",
"accentForeground": "#FFFFFF",
"mode": "light",
"header": {
"style": "gradient",
"fade": true
},
"radiusScale": 1,
"fontSizeScale": 1,
"shadow": true
},
"launcher": {
"style": "bubble",
"icon": "chat",
"side": "right",
"offsetX": 20,
"offsetY": 20,
"size": 56,
"badge": "count"
},
"home": {
"cards": [
{
"type": "greeting",
"id": "hello",
"headline": "Hi there",
"showTeammates": true
},
{
"type": "article_search",
"id": "search",
"placeholder": "Search for help"
}
]
},
"navigation": {
"tabs": [
"home",
"messages",
"help"
]
},
"features": {
"attachments": true,
"maxAttachmentMb": 10,
"emoji": true,
"reactions": false,
"csat": true,
"emailTranscript": true,
"showAgentTyping": true,
"showContactTypingToAgent": true,
"readReceipts": true,
"articleSearch": true,
"news": false,
"voiceCalls": false,
"videoCalls": false,
"screenShare": false,
"recordLiveSessions": false,
"soundOnMessage": true,
"allowReopen": true
},
"localization": {
"fr": {
"home.greeting": "Bonjour.",
"nav.help": "Aide"
}
},
"preChatForm": {
"enabled": false,
"fields": []
},
"offlineForm": {
"enabled": true,
"fields": [
{
"builtin": "email",
"required": true
}
]
},
"customCss": null,
"allowedOrigins": [
"https://members.example.com"
],
"requireIdentityVerification": true,
"hasIdentitySecret": true,
"scheduleId": null,
"defaultFormId": null,
"hideBranding": false,
"isActive": true
},
"meta": {
"requestId": "req_…"
}
}

Get a widget#

One widget.

GET/api/v1/help-desks/{helpDeskId}/widgets/{widgetId}

Path parameters

helpDeskIdstringrequired
Help desk id.
widgetIdstringrequired
Widget id.

Responses

The widget.

curl -X GET "https://voice.evryhealth.com/api/v1/help-desks/{helpDeskId}/widgets/{widgetId}" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"data": {
"id": "c3e8…",
"helpDeskId": "8d1e…",
"brandId": null,
"name": "Member portal",
"apiKey": "hdw_Zk3m…",
"theme": {
"accent": "#2563EB",
"accentForeground": "#FFFFFF",
"mode": "light",
"header": {
"style": "gradient",
"fade": true
},
"radiusScale": 1,
"fontSizeScale": 1,
"shadow": true
},
"launcher": {
"style": "bubble",
"icon": "chat",
"side": "right",
"offsetX": 20,
"offsetY": 20,
"size": 56,
"badge": "count"
},
"home": {
"cards": [
{
"type": "greeting",
"id": "hello",
"headline": "Hi there",
"showTeammates": true
},
{
"type": "article_search",
"id": "search",
"placeholder": "Search for help"
}
]
},
"navigation": {
"tabs": [
"home",
"messages",
"help"
]
},
"features": {
"attachments": true,
"maxAttachmentMb": 10,
"emoji": true,
"reactions": false,
"csat": true,
"emailTranscript": true,
"showAgentTyping": true,
"showContactTypingToAgent": true,
"readReceipts": true,
"articleSearch": true,
"news": false,
"voiceCalls": false,
"videoCalls": false,
"screenShare": false,
"recordLiveSessions": false,
"soundOnMessage": true,
"allowReopen": true
},
"localization": {
"fr": {
"home.greeting": "Bonjour.",
"nav.help": "Aide"
}
},
"preChatForm": {
"enabled": false,
"fields": []
},
"offlineForm": {
"enabled": true,
"fields": [
{
"builtin": "email",
"required": true
}
]
},
"customCss": null,
"allowedOrigins": [
"https://members.example.com"
],
"requireIdentityVerification": true,
"hasIdentitySecret": true,
"scheduleId": null,
"defaultFormId": null,
"hideBranding": false,
"isActive": true
},
"meta": {
"requestId": "req_…"
}
}

Update a widget#

Change any part of a widget. Sections you leave out keep what they have, so a client editing only the theme cannot blank the launcher. A section you send is merged over the stored one: `{ theme: { accent } }` changes the accent and nothing else, nested objects such as `theme.header` merge the same way, arrays such as `home.cards` replace the stored list, and `null` for a key inside a section returns it to its default. The merged section is validated whole. For `brandId`, `scheduleId`, `defaultFormId` and `customCss`, `null` clears the value.

PATCH/api/v1/help-desks/{helpDeskId}/widgets/{widgetId}

Path parameters

helpDeskIdstringrequired
Help desk id.
widgetIdstringrequired
Widget id.

Body

application/json
name
string
brandId
string | null
theme
object
launcher
object
home
object
navigation
object
features
object
localization
object
preChatForm
object
offlineForm
object
customCss
string | null
allowedOrigins
string[]
requireIdentityVerification
boolean
scheduleId
string | null
defaultFormId
string | null
hideBranding
boolean

Responses

The updated widget.

curl -X PATCH "https://voice.evryhealth.com/api/v1/help-desks/{helpDeskId}/widgets/{widgetId}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"theme": {
"accent": "#0F766E"
},
"launcher": {
"side": "left"
},
"features": {
"attachments": false
},
"allowedOrigins": [
"https://members.example.com",
"*.example.org"
]
}'
Response200
{
"data": {
"id": "c3e8…",
"helpDeskId": "8d1e…",
"brandId": null,
"name": "Member portal",
"apiKey": "hdw_Zk3m…",
"theme": {
"accent": "#2563EB",
"accentForeground": "#FFFFFF",
"mode": "light",
"header": {
"style": "gradient",
"fade": true
},
"radiusScale": 1,
"fontSizeScale": 1,
"shadow": true
},
"launcher": {
"style": "bubble",
"icon": "chat",
"side": "right",
"offsetX": 20,
"offsetY": 20,
"size": 56,
"badge": "count"
},
"home": {
"cards": [
{
"type": "greeting",
"id": "hello",
"headline": "Hi there",
"showTeammates": true
},
{
"type": "article_search",
"id": "search",
"placeholder": "Search for help"
}
]
},
"navigation": {
"tabs": [
"home",
"messages",
"help"
]
},
"features": {
"attachments": true,
"maxAttachmentMb": 10,
"emoji": true,
"reactions": false,
"csat": true,
"emailTranscript": true,
"showAgentTyping": true,
"showContactTypingToAgent": true,
"readReceipts": true,
"articleSearch": true,
"news": false,
"voiceCalls": false,
"videoCalls": false,
"screenShare": false,
"recordLiveSessions": false,
"soundOnMessage": true,
"allowReopen": true
},
"localization": {
"fr": {
"home.greeting": "Bonjour.",
"nav.help": "Aide"
}
},
"preChatForm": {
"enabled": false,
"fields": []
},
"offlineForm": {
"enabled": true,
"fields": [
{
"builtin": "email",
"required": true
}
]
},
"customCss": null,
"allowedOrigins": [
"https://members.example.com"
],
"requireIdentityVerification": true,
"hasIdentitySecret": true,
"scheduleId": null,
"defaultFormId": null,
"hideBranding": false,
"isActive": true
},
"meta": {
"requestId": "req_…"
}
}

Delete a widget#

Remove a widget. One that has started conversations is turned off instead, so those conversations keep a record of where they came from. Either way it stops loading on your site and its key stops working; `effect` says which happened.

DELETE/api/v1/help-desks/{helpDeskId}/widgets/{widgetId}

Path parameters

helpDeskIdstringrequired
Help desk id.
widgetIdstringrequired
Widget id.

Responses

What happened.

curl -X DELETE "https://voice.evryhealth.com/api/v1/help-desks/{helpDeskId}/widgets/{widgetId}" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"data": {
"deleted": true,
"effect": "deactivated",
"message": "Turned off \"Member portal\"."
},
"meta": {
"requestId": "req_…"
}
}

Rotate a widget key#

Issue a new publishable key. The old one stops working immediately, with no grace period, so update your embed snippet straight after.

POST/api/v1/help-desks/{helpDeskId}/widgets/{widgetId}/api-key

Path parameters

helpDeskIdstringrequired
Help desk id.
widgetIdstringrequired
Widget id.

Responses

The new key.

curl -X POST "https://voice.evryhealth.com/api/v1/help-desks/{helpDeskId}/widgets/{widgetId}/api-key" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"data": {
"apiKey": "hdw_Q8vn…"
},
"meta": {
"requestId": "req_…"
}
}

Generate an identity secret#

Generate the secret your server signs signed-in users with: HMAC-SHA256 of the user's id, hex-encoded, sent by the widget as the identity hash. It is returned this once and can never be read again, so store it on your server. Generating another replaces it at once. The response is marked `Cache-Control: no-store`.

POST/api/v1/help-desks/{helpDeskId}/widgets/{widgetId}/identity-secret

Path parameters

helpDeskIdstringrequired
Help desk id.
widgetIdstringrequired
Widget id.

Responses

The secret, once.

curl -X POST "https://voice.evryhealth.com/api/v1/help-desks/{helpDeskId}/widgets/{widgetId}/identity-secret" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"data": {
"secret": "4f1c9e…"
},
"meta": {
"requestId": "req_…"
}
}

Upload an attachment#

Upload a file to a conversation as `multipart/form-data` with two parts, `conversationId` and `file`. Files up to 25 MB. The type is the part's declared content type and must be one of: PNG, JPEG, GIF, WebP, HEIC, PDF, plain text, CSV, JSON, zip, Word, Excel, MP4 or QuickTime. The file is held against the conversation and shown to nobody until a message names it in `attachmentIds`. ``` curl -X POST https://<host>/api/v1/help-desks/<helpDeskId>/attachments \ -H "Authorization: Bearer $KEY" \ -F conversationId=<conversationId> \ -F file=@declined-card.png;type=image/png ```

POST/api/v1/help-desks/{helpDeskId}/attachments

Path parameters

helpDeskIdstringrequired
Help desk id.

Body

multipart/form-data
conversationId
string
required
file
binary
required

Responses

The uploaded file, not yet sent.

curl -X POST "https://voice.evryhealth.com/api/v1/help-desks/{helpDeskId}/attachments" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: multipart/form-data" \
-d '{
"conversationId": "2b6f…",
"file": "(binary)"
}'
Response201
{
"data": {
"id": "5be0…",
"messageId": null,
"conversationId": "2b6f…",
"fileName": "declined-card.png",
"contentType": "image/png",
"sizeBytes": 48211,
"width": null,
"height": null,
"scanState": "skipped",
"downloadUrl": "/api/v1/help-desks/8d1e…/attachments/5be0…/content",
"createdAt": "2026-09-30T15:06:02.000Z"
},
"meta": {
"requestId": "req_…"
}
}

Download an attachment#

The file's bytes, with its stored content type, as a download (`Content-Disposition: attachment`) and `X-Content-Type-Options: nosniff`. The original file name is in `filename*`. A file that has not been cleared for serving is a 404.

GET/api/v1/help-desks/{helpDeskId}/attachments/{attachmentId}/content

Path parameters

helpDeskIdstringrequired
Help desk id.
attachmentIdstringrequired
Attachment id.

Responses

The file. Not JSON.

curl -X GET "https://voice.evryhealth.com/api/v1/help-desks/{helpDeskId}/attachments/{attachmentId}/content" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"body": "(binary)"
}

React to a message#

Toggle a reaction as the key's owner: added if they have not used that emoji on the message, removed if they have. One of 👍 👎 ❤️ 🎉 😄 😮 🙏 ✅. Returns the counts after the change.

POST/api/v1/help-desks/{helpDeskId}/conversations/{conversationId}/messages/{messageId}/reactions

Path parameters

helpDeskIdstringrequired
Help desk id.
conversationIdstringrequired
Conversation id.
messageIdstringrequired
Message id.

Body

application/json
emoji
string
required

Responses

The message's reactions.

curl -X POST "https://voice.evryhealth.com/api/v1/help-desks/{helpDeskId}/conversations/{conversationId}/messages/{messageId}/reactions" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"emoji": "👍"
}'
Response200
{
"data": {
"messageId": "d1f0…",
"reactions": [
{
"emoji": "👍",
"count": 2
}
]
},
"meta": {
"requestId": "req_…"
}
}

Get desk metrics#

The desk right now: open, pending and on-hold counts, unassigned, breaching, solved today, median first-reply and resolution times, satisfaction, how much the AI agent resolved on its own, agents online and the queue.

GET/api/v1/help-desks/{helpDeskId}/metrics

Path parameters

helpDeskIdstringrequired
Help desk id.

Responses

The metrics.

curl -X GET "https://voice.evryhealth.com/api/v1/help-desks/{helpDeskId}/metrics" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"data": {
"open": 42,
"pending": 9,
"onHold": 3,
"unassigned": 7,
"solvedToday": 61,
"breaching": 2,
"medianFirstReplySeconds": 184,
"medianResolutionSeconds": 5410,
"csatAverage": 4.6,
"csatResponseCount": 128,
"aiResolutionRate": 0.31,
"agentsOnline": 5,
"queueWaiting": 7,
"longestWaitSeconds": 412
},
"meta": {
"requestId": "req_…"
}
}

Get satisfaction#

Customer satisfaction over the last `days`: overall with its 1 to 5 distribution, per agent, and for conversations the AI agent handled. Every average comes with its count.

GET/api/v1/help-desks/{helpDeskId}/csat

Path parameters

helpDeskIdstringrequired
Help desk id.

Query parameters

daysnumber
Window in days, 1 to 365.
Default30

Responses

The breakdown.

curl -X GET "https://voice.evryhealth.com/api/v1/help-desks/{helpDeskId}/csat" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"data": {
"days": 30,
"overall": {
"average": 4.6,
"count": 128,
"distribution": {
"1": 2,
"2": 3,
"3": 9,
"4": 21,
"5": 93
}
},
"byAgent": [
{
"agentId": "a41c…",
"name": "Sam",
"average": 4.8,
"count": 40
}
],
"ai": {
"average": 4.2,
"count": 31
}
},
"meta": {
"requestId": "req_…"
}
}

Read the audit log#

Configuration changes on the desk, newest first: who changed what, and the fields that changed. Secret values never appear, only that they changed. Needs `help_desk:configure`, and a desk lead or admin role if the key's owner staffs the desk.

GET/api/v1/help-desks/{helpDeskId}/audit

Path parameters

helpDeskIdstringrequired
Help desk id.

Query parameters

actionstring
For example `widget.api_key_rotated`.
subjectTypestring
For example `widget` or `sla_policy`.
subjectIdstring
One record's changes.
actorUserIdstring
One person's changes.
limitnumber
Page size, up to 200.
Default50
cursorstring
`meta.pagination.nextCursor` from the previous page.

Responses

A page of entries.

curl -X GET "https://voice.evryhealth.com/api/v1/help-desks/{helpDeskId}/audit" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"data": {
"entries": [
{
"id": "1042",
"actorUserId": "3f6a…",
"actorName": null,
"action": "widget.api_key_rotated",
"subjectType": "widget",
"subjectId": "c3e8…",
"subjectLabel": "Member portal",
"changes": null,
"createdAt": "2026-09-30T15:10:00.000Z"
}
]
},
"meta": {
"requestId": "req_…",
"pagination": {
"nextCursor": "1042",
"limit": 50
}
}
}

Get the queue#

Open, unassigned conversations in the order they should be taken: priority first, then arrival. Snoozed conversations are not waiting and are not listed. `waitingSeconds` counts from when the conversation last joined the queue: when it was created, reopened, unassigned or woke from a snooze, whichever was latest.

GET/api/v1/help-desks/{helpDeskId}/queue

Path parameters

helpDeskIdstringrequired
Help desk id.

Query parameters

teamIdstring
Only this team's queue.
limitnumber
Up to 200.
Default50

Responses

The queue.

curl -X GET "https://voice.evryhealth.com/api/v1/help-desks/{helpDeskId}/queue" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"data": {
"queue": [
{
"conversationId": "2b6f…",
"waitingSeconds": 212,
"priority": "urgent"
}
]
},
"meta": {
"requestId": "req_…"
}
}

Claim the next conversation#

Take the conversation at the head of the queue, assigned to the key's owner. Atomic: two agents claiming at once never get the same one. A claim is recorded as an assignment, so it appears in the timeline and fires the same events. Taking work for yourself needs `help_desk:respond`; moving it between people is `help_desk:assign`. An empty queue answers `claimed: false`.

POST/api/v1/help-desks/{helpDeskId}/queue/claim

Path parameters

helpDeskIdstringrequired
Help desk id.

Query parameters

teamIdstring
Claim from this team's queue.

Responses

The claimed conversation, or none.

curl -X POST "https://voice.evryhealth.com/api/v1/help-desks/{helpDeskId}/queue/claim" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"data": {
"claimed": true,
"conversation": {
"id": "2b6f…",
"status": "open",
"assignedAgentId": "a41c…"
}
},
"meta": {
"requestId": "req_…"
}
}