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.
/api/v1/call-centers/{callCenterId}/qa/scorecardsPath parameters
callCenterIdstringrequired- Call center id.
Query parameters
includeInactiveboolean- Include deleted (deactivated) scorecards.
- Default
false
Responses
The scorecards.
curl -X GET "https://voice.evryhealth.com/api/v1/call-centers/{callCenterId}/qa/scorecards" \-H "Authorization: Bearer YOUR_API_KEY"
{"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.
/api/v1/call-centers/{callCenterId}/qa/scorecardsPath parameters
callCenterIdstringrequired- Call center id.
Body
application/jsontype- 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}]}'
{"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.
/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"
{"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.
/api/v1/call-centers/{callCenterId}/qa/scorecards/{scorecardId}Path parameters
callCenterIdstringrequired- Call center id.
scorecardIdstringrequired- Scorecard id.
Body
application/jsontype- 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}'
{"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.
/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"
{"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.
/api/v1/call-centers/{callCenterId}/qa/scoresPath 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.
- Default
1 pageSizenumber- Up to 200.
- Default
50
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"
{"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.
/api/v1/call-centers/{callCenterId}/qa/leaderboardPath 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"
{"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.
/api/v1/call-centers/{callCenterId}/qa/trendPath parameters
callCenterIdstringrequired- Call center id.
Query parameters
daysnumber- 1 to 365.
- Default
30 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"
{"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.
/api/v1/call-centers/{callCenterId}/calls/{callId}/scoresPath 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"
{"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.
/api/v1/call-centers/{callCenterId}/calls/{callId}/scoresPath parameters
callCenterIdstringrequired- Call center id.
callIdstringrequired- The call center's record of the call: `id` in the call center's call list.
Body
application/jsontype- 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."}'
{"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.
/api/v1/call-centers/{callCenterId}/calls/{callId}/scores/autoPath 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"
{"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.
/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/jsontype- 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."}'
{"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_…"}}