Paste a call transcript and this scores it automatically against the referral-outreach rubric — critical guardrails, core execution, and professionalism — using the same tiered logic as the master scorecard.
Basic identifiers for the record.
| Staff Name | |
| Date | |
| Call ID |
Pull a call straight from Aircall, and send finished audits to a Google Sheet. Both go through one small relay script you deploy once — see setup below.
ANTHROPIC_API_KEY (from console.anthropic.com → API Keys) — this is required for the "Run AI audit" button to work at all; it proxies the scoring call through this script so the key is never exposed in the page. Also add AIRCALL_API_ID and AIRCALL_API_TOKEN (from Aircall Dashboard → Company Settings → API Keys) if you want the Aircall import button too — that part is optional.function doPost(e) {
var body = JSON.parse(e.postData.contents);
if (body.action === 'submit') return handleSubmit(body.payload);
if (body.action === 'fetchCall') return handleFetchCall(body.callId);
if (body.action === 'auditCall') return handleAuditCall(body.payload);
return json_({error: 'Unknown action'});
}
function doGet(e) {
return json_({status: 'Audit relay is running'});
}
var TARGET_SPREADSHEET_ID = '1y_iwRNLomBeeXM9g6nXzIJwfq-X_xMhrsUQnTjHTjCE';
var TARGET_GID = 2132488623;
function getTargetSheet_() {
var ss = SpreadsheetApp.openById(TARGET_SPREADSHEET_ID);
var sheets = ss.getSheets();
for (var i = 0; i < sheets.length; i++) {
if (sheets[i].getSheetId() === TARGET_GID) return sheets[i];
}
throw new Error('Tab with gid ' + TARGET_GID + ' not found in the target spreadsheet.');
}
function handleSubmit(payload) {
var sh = getTargetSheet_();
if (sh.getLastRow() === 0) {
sh.appendRow(['Timestamp','Staff Name','Date','Call ID','Identity','Safety','Consent',
'Intro','Program','Insurance','Scheduling','Closing','Objection','Tone',
'SLA','Canvas','Consent Sent','Total Score %','Tier','Needs Review','Note']);
}
sh.appendRow([new Date(), payload.staffName, payload.date, payload.callId,
payload.identity, payload.safety, payload.consent, payload.intro, payload.program,
payload.insurance, payload.scheduling, payload.closing, payload.objection, payload.tone,
payload.sla, payload.canvas, payload.consent_sent, payload.total, payload.tier, payload.needsReview,
payload.note]);
return json_({status: 'ok'});
}
function handleFetchCall(callId) {
var props = PropertiesService.getScriptProperties();
var apiId = props.getProperty('AIRCALL_API_ID');
var apiToken = props.getProperty('AIRCALL_API_TOKEN');
var auth = Utilities.base64Encode(apiId + ':' + apiToken);
var opts = { headers: { Authorization: 'Basic ' + auth }, muteHttpExceptions: true };
var callRes = UrlFetchApp.fetch('https://api.aircall.io/v1/calls/' + callId, opts);
var call = JSON.parse(callRes.getContentText());
if (call.error) return json_({error: call.error, troubleshoot: call.troubleshoot});
var transcriptText = '';
try {
var trRes = UrlFetchApp.fetch('https://api.aircall.io/v1/calls/' + callId + '/transcription', opts);
var tr = JSON.parse(trRes.getContentText());
if (tr.transcription && tr.transcription.utterances) {
transcriptText = tr.transcription.utterances.map(function(u) {
return (u.participant && u.participant.name ? u.participant.name : 'Speaker') + ': ' + u.text;
}).join('\\n');
}
} catch (err) {
transcriptText = '';
}
return json_({
callId: callId,
staffName: call.call && call.call.user ? call.call.user.name : '',
date: call.call ? new Date(call.call.started_at * 1000).toISOString().slice(0,10) : '',
recording: call.call ? call.call.recording : '',
transcript: transcriptText
});
}
function handleAuditCall(payload) {
var props = PropertiesService.getScriptProperties();
var apiKey = props.getProperty('ANTHROPIC_API_KEY');
if (!apiKey) return json_({error: 'ANTHROPIC_API_KEY is not set in Script Properties.'});
var opts = {
method: 'post',
contentType: 'application/json',
headers: { 'x-api-key': apiKey, 'anthropic-version': '2023-06-01' },
payload: JSON.stringify({
model: 'claude-sonnet-4-6',
max_tokens: 1800,
system: payload.system,
messages: [{ role: 'user', content: payload.userMessage }]
}),
muteHttpExceptions: true
};
var res = UrlFetchApp.fetch('https://api.anthropic.com/v1/messages', opts);
var data = JSON.parse(res.getContentText());
if (data.error) return json_({error: data.error.message || 'Anthropic API error'});
var text = (data.content || []).filter(function(b){ return b.type === 'text'; })
.map(function(b){ return b.text; }).join('\n');
return json_({text: text});
}
function json_(obj) {
return ContentService.createTextOutput(JSON.stringify(obj)).setMimeType(ContentService.MimeType.JSON);
}
Transcript import needs Aircall's AI Assist add-on enabled on your account. Without it, "Import from Aircall" still pulls staff name, date, and the recording link — paste the transcript in manually alongside it.
Paste the full call transcript below. Either run the AI audit automatically (needs the relay + Anthropic API key set up above), or copy a ready-made prompt to paste into claude.ai yourself — that path needs no API key at all. Either way, every result is editable and you can fill in everything manually too.
After Claude replies in the claude.ai chat, paste its full reply here (raw JSON, or JSON inside a code block — either works) and click Apply. This is only needed if you used "Copy prompt for claude.ai" above instead of the automatic button.
| Tiered Scoring Framework | Quality Metrics | Scores |
|---|---|---|
| Green Zone (≥ 90%) → Excellent quality call | Critical Guardrail | — |
| Yellow Zone (85–89%) → Acceptable but improvable | Core Execution | — |
| Red Zone (< 85%) → Needs coaching or corrective action | Process/Compliance | — |
| Total Score | — |