# The recording is longer than any tool call is allowed to run

> A long transcription cannot answer inside a tool call. It gets queued, you get a handle immediately, and the retry that follows a timeout does not double the bill.

Canonical HTML version: https://mcpbelt.com/problems/transcribe-a-recording-longer-than-a-request

## The problem

You call the transcription tool on an hour of audio and wait. The call has a deadline, the engine on the other side does not care about it, and the deadline arrives first. Your agent gets nothing back, which looks exactly like a service that is down.

What happens next is the expensive part. **An agent that times out retries**, because retrying is the right thing to do when a call does not answer. The first attempt was never cancelled, so the same hour is now being transcribed twice, and unless somebody planned for it, paid for twice. The second attempt has the same deadline as the first, so it times out too, and so does the third.

The failure is silent in the way that costs money: no error mentions duplication, no log says the work is still running somewhere, and the only visible symptom is a tool that never seems to work on long files.

## Why it fails locally

This is not a speed problem, and a faster model does not fix it. A tool call is a request: it has to answer, and it has to answer before the client on the other end gives up. Work measured in minutes does not fit that shape however fast the minutes are, and running a speech model on your own machine makes it worse, since real time or slower on a laptop means an hour of audio takes an hour while the call sits there waiting.

The shape that does fit is a job: start it, take a receipt, come back for the result. Building that yourself means somewhere to keep the job, somewhere to keep the output, a worker that outlives the process which accepted the request, and a way to hold the reserved credit across the two.

The piece that gets written last is the one that matters: recognising that the retry which just arrived is the same work already in flight. Without it every timeout is a second engine call, and every engine call is real money leaving. **The retry is not an edge case, it is the normal behaviour of every agent you will ever connect.**

## The code

Above a few minutes `listen` does not wait. It answers immediately with a handle and `status: running`, and the same handle serves the transcript once it is ready.

```
upload(filename="workshop.m4a", content_type="audio/mp4")
  -> key, upload_url

curl -T workshop.m4a "<upload_url>"

listen(key="<key>")
  -> handle, status: running, estimated_cost_eur,
     estimated_minutes, retry_after_seconds

result(handle="<handle>")
  -> status: running, waiting_seconds, retry_after_seconds

result(handle="<handle>")
  -> the transcript, once it is ready

cancel(handle="<handle>")
  -> cancelled, refunded_eur, reason
```

## What it costs

Queueing is not a different price. `listen` costs 0.005 EUR per minute in `balanced` mode and 0.008 EUR in `exact`, whether the answer comes back inside the call or hours later, so a fifty-six minute recording is about 0.28 EUR. Measured on a real run: fifty-six minutes of recording, queued, transcribed and delivered in thirty-five seconds.

**The same request is charged once.** It is recognised by the contents of the file rather than by its name, so the retry that follows a timeout returns the first job instead of starting a second one, and this is what makes a timing-out agent harmless rather than expensive. If the operation fails, nothing is charged at all.

Duration is measured from the file, not estimated from its size, and you pay on the real duration whether or not you ever collect the result. Waiting is free: `result` costs nothing however many times you call it while the job runs, and so does `cancel`.

## What this does not do

- `cancel` returns the credit only while the job is still waiting. Once an engine has started, `cancelled` comes back false with a `reason`, the job runs to the end, and you collect it with `result` as usual.
- `retry_after_seconds` is a hint about when to look again, not a promise about when the work will be done.
- Nothing is pushed to you. There is no callback and no webhook: you come back with the handle.
- Four hours is the ceiling on a single recording. Longer than that, split the file.
