Skip to main content

Phone Call Records

Every phone conversation your voice agent has is kept: the calls it places for you, and — once the agent has its own phone number — the calls people place to it. Each record holds who was on the line, when the call happened, how long it lasted, a short summary, and the full transcript.
Inbound calls used to be answered but not kept. If someone called your agent’s number, the conversation happened and then vanished. It is now recorded the same way an outbound call is, so a call that comes in at 9pm is still there in the morning.

What a record contains

Calls come from two places: your voice agent places and answers them, and your team can call a lead from your organization’s number with Erdo ringing their own phone first. Both land in the same records.

Ask in chat

The simplest way in. Ask for the calls themselves, or for one call’s transcript:
“Has anyone called my Sofia number this week?”
“What did the caller say on that last call?”
Erdo lists the calls — newest first, with direction, both numbers, duration and a summary — and reads back the full transcript of whichever one you pick. You can narrow to one agent or to inbound calls only just by saying so.

Your agent asks callers who they are

On an inbound call the person on the line is a stranger — Erdo has no other way to reach them. So the agent asks, naturally and at most twice, for their name and the best email or phone to follow up on, spells an email back to confirm it, and accepts a “no thanks” gracefully. What they share lands in the transcript and the summary. On an outbound call it does the opposite: you already told Erdo who to call, so the agent never asks the recipient for their own contact details.

An inbound caller becomes a lead

Someone who dials your number is a stranger who wants something, which is what a lead is — so an inbound call that arrives with a caller ID is saved as one, whether or not the caller gave a name or an email. The number itself is enough to follow up on, and the carrier verified it, so it is the lead’s phone. A name and an email join the row when the caller gave them; the call’s summary rides along, so the row says why they rang. Two kinds of call produce no lead: one from a withheld number where the caller stated no way to reach them, and one whose details could not be tied to a single lead — the refused outcome described below. Outbound calls are not leads — you already knew who you were calling. The lead carries source = phone_incoming_call, which is how you tell callers apart from the leads your landing pages and website widget capture in the same table — and from sms_incoming, the text conversations that arrive on the same number and are governed by the same setting. Where the row lands is a setting on the phone deployment (Agents → Deployments → the phone runtime → Save leads), resolved the same way a widget’s is: a dataset you pick, else your organization’s leads dataset when you have exactly one, else a “Widget leads” dataset Erdo creates for you (the name is historical — it is the catch-all for leads with nowhere else to go). Untick the setting and calls are still recorded — they just stop producing leads. Someone who calls a second time from the same number is recognised: the call merges into the lead their first call created rather than starting a second row, and anything new they said — an email they didn’t give last time — fills in what was blank. Identity is the email address where there is one and the phone number otherwise, so a caller who leaves an email on one call and not on the next can still land as two rows — the same limit a landing page has. Where the number they rang from is already on more than one lead — a shared office or family line, each person captured separately — Erdo writes nothing rather than guess which of them called, and the record reads refused. Each call record says what happened to its lead, so you never have to guess: the contact Erdo read out of the call, and the outcome — created (a new lead), existing (merged into a lead already in the table), disabled (lead saving is off for this deployment), refused (the details could not be tied to a single lead, so no row was written), or test (a preview or automated check, never written).
A lead is not instant. The contact details are read out of the call after it ends, so the row appears within roughly 15 to 30 minutes of hang-up — the same pass that fills in the summary.

When someone spells their email

Speech recognition often mishears an unusual name, so a caller who says their email out loud will often spell it straight after. Erdo saves the spelled address, not the one it heard. Spellings in Spanish and Portuguese count too (“eme, a, erre…”, “arroba”, “ponto”). When the assistant reads a spelling back and the caller says yes, Erdo saves that confirmed spelling, and the assistant uses it for the rest of the call, goodbye included. contact records how the email and the name were given: A conflict is the case to look at before you write to someone: Erdo keeps the address they spelled and shows you the other one rather than picking between them without telling you. The follow-up engine reads these fields too. The fields are missing on conversations read before Erdo started recording them.

Read them programmatically

The same list-and-read surface is available over the REST API, the CLI, and to MCP clients, so a scheduled automation or an external dashboard can pull calls without going through chat. All of them are scoped to your organization by your credentials.
  • List — GET /v1/voice/calls (or a POST with a JSON body) returns each call’s id, direction, both numbers, status, timing, duration, summary, and the lead it produced, newest first and paginated. Narrow with agent (a voice agent’s slug), direction (inbound or outbound) and canonical_lead_id (one lead’s calls — see below); filters combine. Bound a reporting period with since and until — RFC3339 timestamps on when the call started, with since inclusive and until exclusive, so back-to-back periods such as two calendar months never count a call twice. Either bound can be left off, and a since that is not before until is refused rather than answered with an empty list. The response’s total is how many calls match the filters and the period at the moment of that request, whatever the page size — so a dashboard tile can show “37 calls this month” from one request; count is the number on this page. total is live: a call that comes in while you page through can change it from one page to the next, so a multi-page read is finished when a response has no next_cursor, not when the rows you have collected reach total. When a response includes next_cursor, pass it back as cursor to read the next stable page, keeping the same filters and period — this avoids skips when a call comes in during a multi-page scan. MCP tool: erdo_voice_call_list.
  • Read one — GET /v1/voice/calls/{call_id}, using a call_id from the list, returns transcript_turns — the conversation ready to display, as [{role, text, at}], where role is who spoke (user for the caller, agent for your agent), text is what they said, and at is when (the call’s start plus how far into the call the turn came). at is set on inbound calls; outbound calls leave it off because the call record starts before the call is answered, and any turn whose timing is not known leaves it off too — along with the same conversation as plain transcript_text, the raw transcript, a structured turns array with per-turn model latency and token usage, and the same lead fields the list carries. transcript_turns is an empty list when no transcript was captured. MCP tool: erdo_voice_call_get.
The lead a call produced reads the same on every surface. contact is the {first_name, last_name, email, phone} Erdo read out of the call, lead_status is created | existing | disabled | refused | test | no_booking, and lead_saved_at is when that outcome was recorded. no_booking is on a call Erdo placed to a lead on a booking phone line that ended without a meeting booked — there was nothing to add to the lead. refused means the details could not be tied to a single lead — they matched more than one, or the email and phone pointed at different leads, or the call left nothing in the columns your leads dataset identifies people by — so no row was written and the call was closed out rather than retried. A first-time caller is not refused: their details match no existing lead, so a new one is written and the call reads created. See lead identity. Two absences mean different things and are worth keeping apart if you are counting: contact is null while the call has not been read yet and an object with blank fields once it has been read and nobody was reachable, and lead_status is an empty string while the lead writer has not reached the call rather than meaning no lead. When your leads dataset uses lead identity, each call also names the lead it became: canonical_lead_id is that lead’s permanent UUID and lead_reference is the same id in the 22-character form a link or a text message carries. This is the field to join on when you show a lead’s calls — the caller’s number is only evidence, and the same person can reach you from two numbers or leave an email on a second call. Pass either form as canonical_lead_id on the list (GET /v1/voice/calls?canonical_lead_id=...) to read one lead’s calls; it narrows your organization’s calls and never reaches anyone else’s. The same filter works on text conversations and widget conversations, so one lead’s whole history is three reads keyed on one id. Both fields are empty in three cases, and none of them means “not a lead”: the call is still waiting for its lead to be written, its leads dataset is not enrolled in lead identity, or the call was recorded before calls carried the lead’s identity — those older calls keep empty fields rather than being matched to a lead after the fact. A call keeps the id its lead was written under, so after two leads are merged the absorbed lead’s calls still carry its id; reading the surviving lead lists the ids merged into it, and filtering by each of those covers the whole history. From the CLI:
Website chat widget conversations are a separate surface — they belong to a widget rather than to a phone number. See Website Concierge for those.

Good to know

  • Voicemail is detected, not left. When the AI agent’s call reaches a voicemail greeting or a carrier’s recording — in English, Spanish or Portuguese, including a short fragment such as “después del tono” or a “press 1” menu — it hangs up straight away without leaving a message, and the record’s dial outcome is voicemail. A call on which everything the other side said was such a recording is voicemail too, even if the agent kept talking. A call where the other side said one short thing and then nothing until the agent hung up is no_answer. A call that connects but on which nobody says anything is no_answer. The follow-up engine treats both like a call nobody answered. Calls made before 9 October 2026 have no outcome on the AI agent’s calls that connected; read the summary for those.
  • Summaries fill in on their own. A call is summarized shortly after it ends, so a record read moments after hang-up may show a transcript before its summary.
  • Records follow the agent, not the person. An inbound caller is not an Erdo user, so the record belongs to your organization and the agent that answered.
  • Retention matches your other conversation data. Transcripts are visible to your workspace members through Erdo — treat them like any other customer communication.