Call center QA

Scorecards, call scores, automatic scoring and the reporting built on them.

A scorecard is a rubric: weighted criteria, each scored from zero to its maximum. A call center call carries at most two scores, one manual (given by a person, or a supervisor's adjustment) and one automatic. The platform scores every call center call automatically once it is analyzed, against the first active scorecard that applies to the call's agent group; you can also score now, or by hand.

What a key sees follows what its owner sees in the product: an agent sees scores on calls they handled, a supervisor of some groups sees those groups' calls, and owners, admins and ungrouped supervisors see the whole call center. Reading needs call_center:read; managing scorecards and adjusting scores needs call_center:configure; giving a score needs call_center:dequeue and an owner who is an agent in the call center, because the score is attributed to them.

Every score written or adjusted publishes qa.call.scored, on the streams and to webhooks, with the call, the scorecard, the totals and whether it was an adjustment.

List scorecards#

The call center's scorecards with their criteria and the agent groups each applies to. A scorecard with no groups applies to every call.

GET/api/v1/call-centers/{callCenterId}/qa/scorecards

Path parameters

callCenterIdstringrequired
Call center id.

Query parameters

includeInactiveboolean
Include deleted (deactivated) scorecards.
Defaultfalse

Responses

The scorecards.

curl -X GET "https://voice.evryhealth.com/api/v1/call-centers/{callCenterId}/qa/scorecards" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"data": {
"scorecards": [
{
"id": "9b2e…",
"callCenterId": "cc1…",
"name": "Member services QA",
"isActive": true,
"groups": [],
"criteria": [
{
"id": "c1a0…",
"label": "Greeting",
"description": "Greeted the caller and introduced themselves.",
"maxScore": 5,
"weight": 20
},
{
"id": "c2b1…",
"label": "Resolution",
"description": "Resolved the issue or set clear next steps.",
"maxScore": 5,
"weight": 80
}
],
"createdAt": "2026-09-20T15:02:11.000Z"
}
]
},
"meta": {
"requestId": "req_…"
}
}

Create a scorecard#

Needs `call_center:configure`. A supervisor of some groups can only assign the scorecard to their own groups.

POST/api/v1/call-centers/{callCenterId}/qa/scorecards

Path parameters

callCenterIdstringrequired
Call center id.

Body

application/json
type
object
properties
{"name":{"type":"string","description":"Up to 255 characters."},"criteria":{"type":"array","description":"1 to 50 criteria, in order.","items":{"type":"object","properties":{"label":{"type":"string","description":"Up to 255 characters."},"description":{"type":"string","description":"What a full score means, up to 500 characters. Automatic scoring grades against it."},"maxScore":{"type":"integer","description":"1 to 100."},"weight":{"type":"number","description":"Relative weight in the total, 0 to 100."}},"required":["label","maxScore","weight"]}},"groupIds":{"type":"array","items":{"type":"string"},"description":"Agent groups it applies to. Omit for every call."},"isActive":{"type":"boolean","description":"Defaults to true."}}

Responses

The scorecard.

curl -X POST "https://voice.evryhealth.com/api/v1/call-centers/{callCenterId}/qa/scorecards" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Member services QA",
"criteria": [
{
"label": "Greeting",
"description": "Greeted the caller and introduced themselves.",
"maxScore": 5,
"weight": 20
},
{
"label": "Resolution",
"description": "Resolved the issue or set clear next steps.",
"maxScore": 5,
"weight": 80
}
]
}'
Response201
{
"data": {
"id": "9b2e…",
"callCenterId": "cc1…",
"name": "Member services QA",
"isActive": true,
"groups": [],
"criteria": [
{
"id": "c1a0…",
"label": "Greeting",
"description": "Greeted the caller and introduced themselves.",
"maxScore": 5,
"weight": 20
},
{
"id": "c2b1…",
"label": "Resolution",
"description": "Resolved the issue or set clear next steps.",
"maxScore": 5,
"weight": 80
}
],
"createdAt": "2026-09-20T15:02:11.000Z"
},
"meta": {
"requestId": "req_…"
}
}

Get a scorecard#

One scorecard, including a deleted one.

GET/api/v1/call-centers/{callCenterId}/qa/scorecards/{scorecardId}

Path parameters

callCenterIdstringrequired
Call center id.
scorecardIdstringrequired
Scorecard id.

Responses

The scorecard.

curl -X GET "https://voice.evryhealth.com/api/v1/call-centers/{callCenterId}/qa/scorecards/{scorecardId}" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"data": {
"id": "9b2e…",
"callCenterId": "cc1…",
"name": "Member services QA",
"isActive": true,
"groups": [],
"criteria": [
{
"id": "c1a0…",
"label": "Greeting",
"description": "Greeted the caller and introduced themselves.",
"maxScore": 5,
"weight": 20
},
{
"id": "c2b1…",
"label": "Resolution",
"description": "Resolved the issue or set clear next steps.",
"maxScore": 5,
"weight": 80
}
],
"createdAt": "2026-09-20T15:02:11.000Z"
},
"meta": {
"requestId": "req_…"
}
}

Update a scorecard#

Change the name, groups or active state, or replace the criteria. Replacing the criteria keeps existing scores' totals but drops their per-criterion breakdown, since the criteria it referred to are gone.

PATCH/api/v1/call-centers/{callCenterId}/qa/scorecards/{scorecardId}

Path parameters

callCenterIdstringrequired
Call center id.
scorecardIdstringrequired
Scorecard id.

Body

application/json
type
object
properties
{"name":{"type":"string"},"criteria":{"type":"array","description":"1 to 50 criteria, in order.","items":{"type":"object","properties":{"label":{"type":"string","description":"Up to 255 characters."},"description":{"type":"string","description":"What a full score means, up to 500 characters. Automatic scoring grades against it."},"maxScore":{"type":"integer","description":"1 to 100."},"weight":{"type":"number","description":"Relative weight in the total, 0 to 100."}},"required":["label","maxScore","weight"]}},"groupIds":{"type":"array","items":{"type":"string"}},"isActive":{"type":"boolean"}}

Responses

The scorecard.

curl -X PATCH "https://voice.evryhealth.com/api/v1/call-centers/{callCenterId}/qa/scorecards/{scorecardId}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Member services QA (2026)",
"isActive": true
}'
Response200
{
"data": {
"id": "9b2e…",
"callCenterId": "cc1…",
"name": "Member services QA",
"isActive": true,
"groups": [],
"criteria": [
{
"id": "c1a0…",
"label": "Greeting",
"description": "Greeted the caller and introduced themselves.",
"maxScore": 5,
"weight": 20
},
{
"id": "c2b1…",
"label": "Resolution",
"description": "Resolved the issue or set clear next steps.",
"maxScore": 5,
"weight": 80
}
],
"createdAt": "2026-09-20T15:02:11.000Z"
},
"meta": {
"requestId": "req_…"
}
}

Delete a scorecard#

Deactivates the scorecard: it is no longer offered or used for automatic scoring, and the scores already given against it keep it.

DELETE/api/v1/call-centers/{callCenterId}/qa/scorecards/{scorecardId}

Path parameters

callCenterIdstringrequired
Call center id.
scorecardIdstringrequired
Scorecard id.

Responses

Deactivated.

curl -X DELETE "https://voice.evryhealth.com/api/v1/call-centers/{callCenterId}/qa/scorecards/{scorecardId}" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"data": {
"deleted": true
},
"meta": {
"requestId": "req_…"
}
}

List scores#

Every score in the call center the key's owner can see, newest first, with the call and agent it belongs to. Follow `nextCursor` for the next page.

GET/api/v1/call-centers/{callCenterId}/qa/scores

Path parameters

callCenterIdstringrequired
Call center id.

Query parameters

scoredBystring
`auto` or `manual`.
agentIdstring
Only calls this agent handled.
groupIdstring
Only calls of this agent group. Must be a group the key's owner can see.
fromstring
ISO 8601. Scores given at or after.
tostring
ISO 8601. Scores given before.
pagenumber
Page number.
Default1
pageSizenumber
Up to 200.
Default50

Responses

A page of scores.

curl -X GET "https://voice.evryhealth.com/api/v1/call-centers/{callCenterId}/qa/scores" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"data": {
"scores": [
{
"id": "5d7f…",
"callCenterCallId": "ccc1…",
"callId": "5f1c…",
"scorecardId": "9b2e…",
"scorecardName": "Member services QA",
"scoredBy": "auto",
"scorePercentage": 84,
"totalScore": 420,
"maxTotalScore": 500,
"agentId": "ag1…",
"agentName": "Dan Whitfield",
"scorer": null,
"callerPhoneNumber": "+15555550123",
"talkSeconds": 184,
"createdAt": "2026-09-21T10:14:52.000Z"
}
],
"total": 37
},
"meta": {
"requestId": "req_…",
"pagination": {
"nextCursor": "2",
"limit": 50
}
}
}

Leaderboard#

Average score per agent over a window, best first. Defaults to the thirty days before `to` (or now). An agent-level key sees only its owner's row.

GET/api/v1/call-centers/{callCenterId}/qa/leaderboard

Path parameters

callCenterIdstringrequired
Call center id.

Query parameters

fromstring
ISO 8601 lower bound.
tostring
ISO 8601 upper bound.
scoredBystring
`auto` or `manual`.
groupIdstring
Only calls of this agent group. Must be a group the key's owner can see.

Responses

The leaderboard and the window it covers.

curl -X GET "https://voice.evryhealth.com/api/v1/call-centers/{callCenterId}/qa/leaderboard" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"data": {
"leaderboard": [
{
"agentId": "ag1…",
"agentName": "Dan Whitfield",
"averageScorePercentage": 91.5,
"callsScored": 12
}
],
"from": "2026-08-22T00:00:00.000Z",
"to": "2026-09-21T00:00:00.000Z"
},
"meta": {
"requestId": "req_…"
}
}

Score trend#

The average score per UTC day over the last `days` days. Days with no scores are absent.

GET/api/v1/call-centers/{callCenterId}/qa/trend

Path parameters

callCenterIdstringrequired
Call center id.

Query parameters

daysnumber
1 to 365.
Default30
agentIdstring
Only this agent's calls.
scoredBystring
`auto` or `manual`.
groupIdstring
Only calls of this agent group. Must be a group the key's owner can see.

Responses

The trend.

curl -X GET "https://voice.evryhealth.com/api/v1/call-centers/{callCenterId}/qa/trend" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"data": {
"trend": [
{
"date": "2026-09-20",
"averageScorePercentage": 86.25,
"count": 8
}
]
},
"meta": {
"requestId": "req_…"
}
}

Scores on a call#

Every score on one call, with each criterion's score and, for an automatic score, the grader's reasoning per criterion and overall.

GET/api/v1/call-centers/{callCenterId}/calls/{callId}/scores

Path parameters

callCenterIdstringrequired
Call center id.
callIdstringrequired
The call center's record of the call: `id` in the call center's call list.

Responses

The scores, newest first.

curl -X GET "https://voice.evryhealth.com/api/v1/call-centers/{callCenterId}/calls/{callId}/scores" \
-H "Authorization: Bearer YOUR_API_KEY"
Response200
{
"data": {
"scores": [
{
"id": "5d7f…",
"callCenterId": "cc1…",
"callCenterCallId": "ccc1…",
"scorecardId": "9b2e…",
"scorecardName": "Member services QA",
"scoredBy": "manual",
"scorer": {
"agentId": "ag1…",
"name": "Priya Shah"
},
"totalScore": 420,
"maxTotalScore": 500,
"scorePercentage": 84,
"notes": "Good recovery after the hold.",
"reasoning": null,
"criteria": [
{
"criterionId": "c1a0…",
"label": "Greeting",
"maxScore": 5,
"score": 5,
"reasoning": null
},
{
"criterionId": "c2b1…",
"label": "Resolution",
"maxScore": 5,
"score": 4,
"reasoning": null
}
],
"createdAt": "2026-09-21T10:14:52.000Z"
}
]
},
"meta": {
"requestId": "req_…"
}
}

Score a call#

Give a completed call its manual score, attributed to the key's owner as an agent of the call center. Every criterion of the scorecard must be scored once. The scorecard must be active and apply to the call's agent group. A call has one manual score; adjust it rather than scoring again.

POST/api/v1/call-centers/{callCenterId}/calls/{callId}/scores

Path parameters

callCenterIdstringrequired
Call center id.
callIdstringrequired
The call center's record of the call: `id` in the call center's call list.

Body

application/json
type
object
properties
{"scorecardId":{"type":"string"},"criteria":{"type":"array","items":{"type":"object","properties":{"criterionId":{"type":"string"},"score":{"type":"integer","description":"0 to the criterion's maxScore."}},"required":["criterionId","score"]}},"notes":{"type":"string","description":"Up to 10,000 characters."}}

Responses

The score.

curl -X POST "https://voice.evryhealth.com/api/v1/call-centers/{callCenterId}/calls/{callId}/scores" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"scorecardId": "9b2e…",
"criteria": [
{
"criterionId": "c1a0…",
"score": 5
},
{
"criterionId": "c2b1…",
"score": 4
}
],
"notes": "Good recovery after the hold."
}'
Response201
{
"data": {
"id": "5d7f…",
"callCenterId": "cc1…",
"callCenterCallId": "ccc1…",
"scorecardId": "9b2e…",
"scorecardName": "Member services QA",
"scoredBy": "manual",
"scorer": {
"agentId": "ag1…",
"name": "Priya Shah"
},
"totalScore": 420,
"maxTotalScore": 500,
"scorePercentage": 84,
"notes": "Good recovery after the hold.",
"reasoning": null,
"criteria": [
{
"criterionId": "c1a0…",
"label": "Greeting",
"maxScore": 5,
"score": 5,
"reasoning": null
},
{
"criterionId": "c2b1…",
"label": "Resolution",
"maxScore": 5,
"score": 4,
"reasoning": null
}
],
"createdAt": "2026-09-21T10:14:52.000Z"
},
"meta": {
"requestId": "req_…"
}
}

Score a call automatically#

Run automatic scoring now and answer with the score. Uses the first active scorecard that applies to the call and its transcript. Needs what a manual score needs.

POST/api/v1/call-centers/{callCenterId}/calls/{callId}/scores/auto

Path parameters

callCenterIdstringrequired
Call center id.
callIdstringrequired
The call center's record of the call: `id` in the call center's call list.

Responses

The score.

curl -X POST "https://voice.evryhealth.com/api/v1/call-centers/{callCenterId}/calls/{callId}/scores/auto" \
-H "Authorization: Bearer YOUR_API_KEY"
Response201
{
"data": {
"id": "5d7f…",
"callCenterId": "cc1…",
"callCenterCallId": "ccc1…",
"scorecardId": "9b2e…",
"scorecardName": "Member services QA",
"scoredBy": "auto",
"scorer": null,
"totalScore": 420,
"maxTotalScore": 500,
"scorePercentage": 84,
"notes": "Clear greeting; resolution was partial.",
"reasoning": "Clear greeting; resolution was partial.",
"criteria": [
{
"criterionId": "c1a0…",
"label": "Greeting",
"maxScore": 5,
"score": 5,
"reasoning": "Introduced themselves by name."
},
{
"criterionId": "c2b1…",
"label": "Resolution",
"maxScore": 5,
"score": 4,
"reasoning": "Set a follow-up but did not resolve."
}
],
"createdAt": "2026-09-21T10:14:52.000Z"
},
"meta": {
"requestId": "req_…"
}
}

Adjust a score#

A supervisor's adjustment, with the product's supervisor authority (`call_center:configure`). Criteria not sent keep their score and the totals are recomputed over the whole scorecard. The adjusted score becomes the call's manual score, so an automatic score cannot be adjusted while the call also has a manual one.

PATCH/api/v1/call-centers/{callCenterId}/calls/{callId}/scores/{scoreId}

Path parameters

callCenterIdstringrequired
Call center id.
callIdstringrequired
The call center's record of the call: `id` in the call center's call list.
scoreIdstringrequired
Score id.

Body

application/json
type
object
properties
{"criteria":{"type":"array","items":{"type":"object","properties":{"criterionId":{"type":"string"},"score":{"type":"integer","description":"0 to the criterion's maxScore."}},"required":["criterionId","score"]}},"notes":{"type":"string","description":"Replaces the notes. Null clears them."}}

Responses

The adjusted score.

curl -X PATCH "https://voice.evryhealth.com/api/v1/call-centers/{callCenterId}/calls/{callId}/scores/{scoreId}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"criteria": [
{
"criterionId": "c2b1…",
"score": 5
}
],
"notes": "Resolution confirmed on callback."
}'
Response200
{
"data": {
"id": "5d7f…",
"callCenterId": "cc1…",
"callCenterCallId": "ccc1…",
"scorecardId": "9b2e…",
"scorecardName": "Member services QA",
"scoredBy": "manual",
"scorer": {
"agentId": "ag1…",
"name": "Priya Shah"
},
"totalScore": 420,
"maxTotalScore": 500,
"scorePercentage": 84,
"notes": "Good recovery after the hold.",
"reasoning": null,
"criteria": [
{
"criterionId": "c1a0…",
"label": "Greeting",
"maxScore": 5,
"score": 5,
"reasoning": null
},
{
"criterionId": "c2b1…",
"label": "Resolution",
"maxScore": 5,
"score": 4,
"reasoning": null
}
],
"createdAt": "2026-09-21T10:14:52.000Z"
},
"meta": {
"requestId": "req_…"
}
}