Your CRM Has a Note That Says Nothing


Out of the box, Vapi gives you a paragraph of prose and a pass or fail flag. It does not give you anything machine-readable. Its OpenAPI specification is explicit about the three defaults: summaryPlan.enabled defaults to true, successEvaluationPlan.enabled defaults to true, and structuredDataPlan.enabled "Defaults to false".
So every field a CRM write actually needs, the caller's name, the callback time, the service requested, is off until you turn it on. The blank structured-data case is the default state rather than a misconfiguration.
That is the first of four things that go wrong between the call ending and a useful record existing. The others are an empty summary with no error attached, a contact match that silently creates a second record, and a legal duty that follows from something easy to forget, which is that a summary written by a model is a record about a person.
Key takeaways
- Vapi's summary generation has a documented five-second timeout, and on timeout the field "will be empty" rather than erroring.
- The
analysisobject is a required field on the end-of-call report, so checking that it exists tells you nothing. Check the summary string.successEvaluationis typed as a string. On the default PassFail rubric a failed call gives you the literal"false", which is truthy.- Salesforce's Standard Contact Matching Rule cannot match on a phone number alone. Every matching equation requires First Name and Last Name.
- An AI-written summary is personal data, and Article 5(1)(d) requires every reasonable step to keep it accurate.
What does a voice platform give you after a call?
Less than you would assume, and configured more loosely than you would like. Vapi's post-call analysis produces three artefacts, and their defaults pull in different directions.
| Artefact | Default | What it gives you |
|---|---|---|
summary |
Enabled | Two or three sentences of prose |
successEvaluation |
Enabled | A string, "true" or "false" on the default rubric |
structuredData |
Disabled | Nothing, unless you enable it and supply a schema |
The default summary prompt is worth reading, because it ships unchanged in a great many deployments. Vapi's OpenAPI specification gives it as: "You are an expert note-taker. You will be given a transcript of a call. Summarize the call in 2-3 sentences. DO NOT return anything except the summary."
Two or three sentences is the entire specification. There is no instruction to capture the caller's request, any commitment made, or any action outstanding. If your CRM notes read as bland, that prompt is why.
One surface is being replaced
Every property on Vapi's analysisPlan carries "deprecated": true in the live OpenAPI specification, and the documentation now steers new builds elsewhere: "We recommend structured outputs over the analysisPlan for extracting data, summaries, and evaluations from your calls."
Do not read that as a shutdown notice. The same page says the older configuration "still works and remains available for existing setups". But it means call.analysis.summary and call.artifact.structuredOutputs are two different surfaces with different defaults, and most integration guides you will find are written against the older one.
Why does the summary arrive empty?
Because generating it is an LLM call on a five-second budget, and the documented failure mode is silence. Vapi's specification says of the summary timeout: "This is how long the request is tried before giving up. When request times out, call.analysis.summary will be empty." The default is five seconds, adjustable between one and sixty. The same wording appears on structured data and on success evaluation.
Note what does not happen. No error is raised, the webhook still succeeds, and your integration writes an empty note. The specification is candid about the trade-off: raise the timeout to guarantee a summary and you delay the end-of-call report.
The null check that always passes
The analysis object is a required field on the end-of-call report message, listed in the schema's required array alongside type, endedReason and artifact. It is present whether or not anything is in it.
So if (message.analysis) is always true and tells you nothing. The check has to be on the summary being a non-empty string. This is the single cheapest fix in the article and it is a one-line change.
The string that is not a boolean
successEvaluation is typed as a string, and the default rubric is PassFail, which Vapi describes as "A simple 'true' if call passed, 'false' if not." A failed call therefore arrives as the literal string "false", and in JavaScript that is truthy.
An integration doing a plain truthiness check treats every failed call as a success. Worse, switching the rubric to NumericScale changes the same field to "7" with no type change to warn you.
Why did it create a second contact?
Because phone-number matching is harder than every integration guide assumes, and one major CRM will not do it at all. Salesforce's Standard Contact Matching Rule cannot match on a phone number alone; every matching equation in it requires First Name and Last Name. If your agent captured a number and no name, Salesforce has nothing to match on.
HubSpot does treat phone number as a duplicate key, and its Search API is where a lookup-then-write integration meets its limits: five requests per second per account, a 10,000-result ceiling, 200 results per page, and a maximum of five filter groups of six filters each. A burst of post-call writes hits the rate limit before it hits anything else.
Retell's own blog reports that "During testing, duplicate creation rates were under 2% with phone-based matching enabled". Take the direction and not the number, since the denominator is unstated. Two per cent of a busy line is still a steady drip of duplicate records that someone has to merge by hand.
The UK phone number problem
One subscriber, three written forms, and no agreement about which one you stored. The same UK mobile is 07700 900123 nationally, +447700900123 in international format, and often 7700900123 after some system has stripped the trunk zero. Match on the raw string and you get duplicates by construction.
The standard is ITU-T Recommendation E.164, and its fifteen-digit limit is more slippery than it looks. Clause 6.1 frames fifteen digits as a recommendation on Member States rather than a hard property of numbers, and it excludes the international prefix from the count. In practice numbers longer than fifteen digits exist: libphonenumber's own metadata documents assigned German numbers that exceed it. If you sized a database column on the assumption, resize it.
While you are here, +44 (0)7700 900123 is not a valid international form. ITU-T E.123 clause 7.2 states that "The ( ) should not be used in an international number." It is a British typographical habit, not a standard.
Ofcom's own example numbers fail validation
This one is worth knowing before you write your test fixtures. We ran Ofcom's reserved drama ranges through libphonenumber. The 07700 900xxx mobile range and the whole 01632 geographic code both fail isValidNumber(), returning an unknown region and an unknown type. The London drama range 020 7946 0xxx passes.
So the numbers Ofcom publishes precisely so that nobody's real line gets called are, for the most part, not usable as fixtures in a validation-gated test. Use the London range if you need one that passes, and know why the others do not.
Withheld callers are their own category. The strings a platform typically hands you for a withheld number, including anonymous, withheld, unknown, an empty string and a bare +, all raise a parse exception rather than returning a tidy null. Catch it, and route those calls to a path that never attempts a contact match.
Will the summary even fit?
Usually, but the ceilings differ by more than an order of magnitude, and the tight one will surprise you. HubSpot's Notes API documents the body as "limited to 65,536 characters", which no call summary will ever approach. GoHighLevel's equivalent limit is 5,000 characters, which a verbose summary of a long call plausibly could.
Check the behaviour on overrun rather than assuming. HubSpot rejects an over-length property write with a validation error rather than truncating it silently, which is the better failure but still a failure your integration has to handle.
The record is about a person
A call summary describes an identifiable individual, so it is personal data, and Article 5(1)(d) of the UK GDPR applies to it: personal data shall be "accurate and, where necessary, kept up to date; every reasonable step must be taken to ensure that personal data that are inaccurate, having regard to the purposes for which they are processed, are erased or rectified without delay".
Read the two limbs. There is a positive duty to take every reasonable step, and the standard is relative to purpose, so a note that only ever informs a callback is held to a lower bar than one that decides whether someone gets an appointment. The Data (Use and Access) Act 2025 did not touch this provision; its amendments to Article 5 reach only paragraphs (1)(b) and (1)(e).
What if the caller says the summary is wrong?
Then you are in Article 16 territory, and you have one calendar month from the day of receipt to the corresponding date in the next month, extendable by two months for complex requests. The ICO's own worked example runs from 3 September to 3 October, and it suggests adopting 28 days operationally so you never miscount.
There is a genuine conditional here that we are not going to flatten. Where an AI output is honestly framed as a prediction or an opinion rather than a statement of fact, it is not straightforwardly "inaccurate", and the right to rectification does not bite in the same way. The ICO's guidance also says a record should show that it is an opinion "and, where appropriate, whose opinion it is". The qualifier is in the source and we are keeping it.
The practical consequence is a writing rule. A summary that says "caller was rude" invites a rectification request you will struggle to answer. A summary that says "agent recorded the caller as dissatisfied with the wait time" is a record of what happened, and it is defensible.
When does a classification become a decision?
If your post-call step marks a caller as unqualified and that determines what happens to them, you are near automated decision-making, and the law here changed recently. Article 22 has been repealed and replaced by Articles 22A to 22D, in force from 5 February 2026 under SI 2026/82, with a saving for decisions taken before that date.
Anything written about voice agents and Article 22 before February 2026 is describing a provision that no longer exists in that form. We are not going to tell you whether marking a lead as unqualified is a significant decision, because that turns on impact and the ICO's guidance is a test rather than a list. We are telling you the test moved, and that it is worth an hour of someone's attention if your agent classifies callers rather than just describing them.
What we write instead of a paragraph
Four rules, each aimed at one failure above.
- Structured fields first, prose second. Turn structured extraction on, define the schema, and treat the prose summary as a convenience for humans rather than the thing the CRM branches on.
- Never branch on a truthy check. Compare
successEvaluationagainst the literal string, and validate the summary is a non-empty string before writing anything. - Normalise to E.164 once, at the edge, store the normalised form, and keep the raw presented number alongside it so you can debug a bad match later. Withheld and unparseable numbers take a separate path.
- Write records of events, not judgements about people. It reads better and it is the version you can defend when someone asks you to correct it.
The booking half of this problem, where the record exists but the appointment does not, is in the write-failures guide. The reason the caller's name arrives wrong in the first place is in the names guide. For a busy HVAC line the three compound: a misheard name, matched to the wrong contact, summarised in a sentence nobody can act on.
Frequently asked questions
Should I store the recording, the transcript or the summary? Whichever you can justify keeping, for as long as you can justify keeping it. The ICO does not prescribe a retention period for any of the three, so anyone quoting you a fixed number is inventing it. Decide the period from your own purpose, write it down, and apply it. The summary is the smallest and most useful artefact, and it is also the one most likely to be wrong, which is an argument for keeping the transcript long enough to check it against.
Is a wrong AI summary a data breach? Not by itself. Inaccuracy is a breach of the accuracy principle rather than a personal data breach in the security sense, and the remedy is rectification rather than notification. It becomes more serious if the wrong record drives a decision about the person, which is where the Article 22A to 22D question above comes in.
Can I just let the model write freely into the CRM? You can, and it is the most common setup, and it is the reason so many CRMs contain notes that read well and say nothing. The specific risk is that a fluent summary of a call that went badly still reads like a summary of a call that went fine. Grade summaries against the transcript on a sample, the way the evaluation guide describes grading calls against the system of record.
What is the single cheapest fix here?
Change your null check. The analysis object is always present on the end-of-call report, so almost every integration built against it is guarded by a condition that can never be false. Checking that analysis.summary is a non-empty string takes one line and converts a silent empty note into something you can alert on.