Skip to content
ProductOpen console

Recovering lost calls

A call can happen, be answered, be recorded, and still not appear in the call log. It is rare, it has happened, and it is worth knowing the shape of because the failure is quiet at every step.

A call reaches the log through one narrow path. While the call runs, the media bridge and the carrier post lifecycle events (ringing, answered, ended, recording ready) to the platform, and one component writes them into a single call record. The log reads that record. Nothing else creates it.

That path is deliberately forgiving. It answers “received” the moment it takes a valid event, before it knows whether the write worked, so a slow database cannot hang up a live call. The cost of that choice is the failure mode here: if the write is rejected, the caller hears a completely normal conversation, the carrier bills a completely normal call, and the log stays empty.

THE ONLY PATH INTO THE CALL LOGCall happenscarrier records itAudio storedrecording uploadedRecord writtenanswers OK regardlessCall logreads the recorda failure here is silent on both sidesThe carrier still has the call. The audio is still in storage. Only the record is missing,which is exactly why the call can be rebuilt afterwards.
Every stage before the write is a real system with its own record. Only the last one is ours, and only the last one is where the call disappears.

The tell is a mismatch between two places that should agree.

  • Your phone bill or carrier console shows calls the log does not. This is the reliable check, and the one worth running after any change to voice.
  • Call volume drops to zero for a stretch while the line demonstrably still answers. A quiet afternoon looks the same as a broken write; a quiet afternoon during which someone successfully called the number does not.
  • A call you made yourself is not there a minute after you hung up. The log updates as calls end, so a test call that never appears is the fastest signal you have.

The parts of a call live in different systems, and only some of them are ours.

Part of the call Where it lives Recoverable
Start time, duration, direction, numbers The carrier’s own call history Yes
The recording Object storage, written on hangup Yes, if the upload finished
Transcript Held in memory during the call From the recording, without speaker labels
Outcome, sentiment, summary Read from the transcript afterwards Yes, once there is a transcript
Turn counts, response speed Held in memory during the call No

The recording is what makes most of this possible. The audio is the call, so a call whose audio survived can be transcribed after the fact, and everything read from a transcript follows from there.

What the recording cannot give back is who said which sentence. Both sides are mixed into a single track, so a transcript recovered this way is one block of speech with no speaker labels. That is why the console shows it as Both speakers and says where it came from, and why nothing offers to save a line from it as a saved response: half of it is the caller’s words.

Turn counts and response times are genuinely gone. They were measurements taken while the call ran, not properties of the audio, and no amount of listening recovers them.

Recovery is a deliberate, operator-run action, not something that happens automatically. Automating it would mean continuously reconciling against the carrier, and a reconciler that writes call records is a second writer for the same table, which is how the two disagree.

The voice-backfill admin command does the work.

  1. Pull the calls from the carrier. Their call history for the window is the authority: it has the start time, duration and dialled number for every call that actually connected.

  2. Write them into a file. A JSON array, one object per call, using the call’s provider-side identifier as call_ref:

    [
    {
    "call_ref": "078E22C396144505.1786553777.1069053",
    "started_at": "2026-08-12T16:56:17Z",
    "duration_s": 17,
    "direction": "inbound",
    "from": "+15551234567",
    "to": "+19292240694",
    "disposition": "answered"
    }
    ]

    call_ref has to match what the platform would have used, or the rebuilt row will not line up with its recording. Everything except call_ref and started_at is optional.

  3. Dry run it. Without --apply the command writes nothing and prints what it would do, including whether it found each call’s recording in storage:

    admin voice-backfill --app <app> --file calls.json
  4. Apply. Re-running is safe. A call that already has a record is skipped, never duplicated, so you can run the same file again after fixing one entry.

    admin voice-backfill --app <app> --file calls.json --apply

The command replays each call as real lifecycle events through the same component that writes live calls. There is no second write path, so a recovered row is built exactly the way a live one is.

Recovered rows are marked, because a record that quietly claims to be as complete as a live one is worse than a missing record.

  • They carry the recovered tag.
  • They carry a note saying which carrier history they were rebuilt from and what is missing.
  • The recording plays normally, if the audio survived.
  • The transcript, once one has been recovered, is one Both speakers block with a line above it saying it came from the recording.
  • Turn counts and response times are empty. That absence is itself the tell.

They sort into the log by when the call happened, not when the row was written, so a call recovered a week later still appears on the day it was made and counts on that day in the analytics.

The outcome, sentiment and summary arrive on their own within a few minutes of the record existing: the same pass that classifies live calls picks up anything finished and unclassified, whenever it happened. You do not run a second thing.

Recovering the calls is the smaller half. The write failed for a reason, and until that reason is fixed every new call is being lost the same way.

  1. Place a test call and confirm it lands. Recovery has told you nothing about whether the live path works again.

  2. Check the platform logs for rejected writes. A failing record write logs a warning on every event; the silence is only on the wire.

  3. Re-run the backfill for the gap. Once the live path is healthy, the window between the break and the fix is still empty and still recoverable.

Omazy CX documentation. One voice. Every channel. Always on.

Founded by Mosthofa Imran · imran@omazy.ai