Skip to content
megapromotingLet's talk

Solutions · Transport & logistics

When the client asks where the parcel is, the agent does not guess: it calls the courier, asks them, and comes back with the answer.

Voice dispatching, built as a mechanism, not as a promise: a table with explicit states, three attempts two minutes apart, the call result written in a structured way, and a filter that removes the driver’s number from everything that reaches the model, so it cannot be dictated to the client.

Already builtMecanismul de dispecerizare e scris și migrat în platforma noastră vocală: tabela `courier_calls` cu șapte stări posibile, trei încercări implicite, pauză de 120.000 ms între ele și rezultatul apelului păstrat structurat, plus cinci funcții de server care o folosesc — inițierea apelului către curier, verificarea rezultatului, mătura pentru apelurile rămase agățate, identificarea celui care sună înapoi și webhookul de după apel. A doua implementare e filtrul de ieșire scris pentru un client din transport, care elimină recursiv câmpurile de contact ale șoferului din rezultatul uneltei, păstrând numerele dispeceratului. Rezerva, spusă înainte să întrebi: partea de telefon trece printr-o gazdă SIP, iar gazda prin care merg liniile noastre de test nu răspunde la data scrierii — nu-ți dăm un număr de demonstrație pe care nu l-am putea ridica în fața ta.

In a transport or delivery company, most incoming calls are the same question asked by different people: where is the cargo, what time will it arrive, why has it not arrived. The answer is not in a document and not in a system — it is in the driver’s head, while driving. The dispatcher connects the dots: takes the client’s call, calls the driver, returns to the client. This work takes one whole person and is done a hundred times a day, with the same three sentences.

We built exactly this chain, as a mechanism with states, not as a marketing function. An agent talks to the client. When it needs an answer it does not have, it calls a function that starts a second call — to the courier — linked to the conversation it came from. That call has its own row in a table, with a status that moves through `calling`, `in_progress`, `retrying`, `completed`, `failed`, `no_answer`, `timeout`, with the attempt number, with a maximum of three attempts and a two-minute pause between them. The result is written in a structured way: a summary of what the courier said, the estimated time, the position.

The part that is easy to miss and which we treat as a requirement, not as a precaution: the driver’s phone number must not reach the client. It is not an instruction written in the agent prompt, because an instruction in a prompt can be bypassed. It is a filter that recursively walks the tool result and removes the driver’s contact fields before that result reaches the model — while keeping the dispatch numbers. What has been removed cannot be dictated, no matter how the agent is asked.

And the most important limit is one of infrastructure, not intelligence: a voice agent on the phone depends on a SIP host between your operator and the platform. When that host goes down, calls do not go out, and the defect signature is recognisable — request expired, empty call identifier, zero duration. The host through which our test lines pass does not respond as of the writing of this page. On the web, the voice route is verifiable today; on the phone, the first step of any work is to establish which host your numbers enter through.

What it covers

What changes concretely in transport & logistics

The agent speaking with the client can start a second call

When the answer is not in the information it has, the agent calls a server function that rings the courier. The call to the courier keeps its link with the conversation it came from, through a parent conversation identifier — so it is always known for whom the call was made and why. The order context is transmitted as a structure, from your order system, not dictated again.

The call to the courier has states, not just “it was called”

Seven states written as a restriction in the database: `calling`, `in_progress`, `retrying`, `completed`, `failed`, `no_answer`, `timeout`. Plus the current attempt number. The difference between “did not answer” and “the system went down” is visible in the data, not guessed from logs — and that matters when someone asks tomorrow why the client did not receive an answer.

Retries are configured, not improvised

Default: three attempts, with 120,000 milliseconds — two minutes — between them, both written as default values in the table and in the process configuration. A driver driving on a road with no signal does not answer on the first try. A system that calls only once and declares failure is useless exactly in the case for which it was built.

The call result comes back structured, not as a story

What the courier said is written as a summary, estimated time and position, in a structured field linked to the call row. From there it can go further: to the agent speaking with the client, to dispatch, to your system. A post-call webhook and a sweep function for calls left hanging complete the chain, so that a missed call does not remain in the `calling` state forever.

The driver's number cannot be dictated, because it does not reach the model

The output filter recursively goes through the tool result and removes the driver's contact fields, keeping the dispatcher's numbers. It is written in code, applied specifically to the integration where the problem appears. The reason is simple: the provider interface was returning the drivers' numbers in the response, and the company policy forbade the agent from giving them to clients. An instruction in the prompt could have been bypassed with a well-phrased question; a removed field cannot be bypassed.

Who calls back is recognised by number

The courier who comes back with a call is identified by a dedicated function, so it does not enter the general customer flow and does not hear the menu from the start. It seems like a detail; it is the difference between a system that drivers use and one they bypass by calling the dispatcher directly, which cancels the whole work.

Hand-off to dispatch, within reach of the one speaking

Blind transfer through `##` and assisted transfer through `*2`, with landing context in the calling plan that distinguishes internal four-digit extensions from external numbers. When the client insists on a human — and in transport they do, because their cargo is delayed — the transfer should not go through a menu.

The agent can be interrupted, and this is adjusted with numbers

On the OpenAI Realtime route: semantic speech detection with a 0,5 threshold, a 300 ms cushion, 500 ms of silence, interruption allowed by default. On the ElevenLabs route, the interruption event is sent to the switchboard so it can cut playback. `turn_timeout` is set per agent. In a transport call, where the interlocutor is often in a noisy cab, these thresholds are half of the perceived quality.

What a voice agent does not do in transport

It does not decide the route and does not re-optimise deliveries. It does not give a rate if it is not connected to the source that calculates it. It does not know where the goods are if nobody has told it — neither the courier nor your system. And it does not replace the dispatcher: it takes repetitive calls and leaves exceptions to them, which is exactly the part they are paid for.

Traseul

How a request passes through the system.

01

We first establish which SIP host your numbers enter through

The first stage is not the agent scenario, but telephony: through which trunk the number enters, who controls it, what happens when the host does not answer. We deliver a written check of the route and, if the route is not secure, we say so before building anything on top of it. An excellent agent on a host that goes down is an agent that does not answer.

02

We write the dispatch scenario, with its exceptions

What the agent asks the courier, in what order, what happens when the courier does not answer on the third attempt, what happens when the answer is unclear, when it transfers to the human dispatcher. We deliver the written scenario and the list of states a call can reach — including the ugly ones.

03

We connect the order source and put the filters at the output

The order context comes from your system, and before the result of a tool reaches the model we pass it through the list of fields that are not allowed out. We deliver the integration, the written list of filtered fields and proof that the filter is applied to the result, not to the instructions.

04

We run on a small volume, with transcripts read by a person

We start on part of the calls, with transfer to the dispatcher configured broadly, and we read the transcripts. From there, the interruption thresholds, `turn_timeout`, the wording of the questions and the threshold from which the agent gives up are adjusted. We deliver the report on these calls, with the adjustment decisions, not just the agent started.

1Clientul întreabă2agentul cheamăfuncția de apel3apel către curier, custare proprie4până la treiîncercări, la douăminute5rezultat structurat(rezumat, timpestimat, poziție)6răspuns la client, cunumărul șoferuluifiltrat din drum
Traseul, în 6 pași

The data

What we touch, where it lives, and how long it stays

The rules differ from one industry to another. These are the ones that apply in transport & logistics.

The driver’s number is a person’s personal data, not a technical field
A courier or subcontracted driver is a natural person. Their number, their location and their voice recording are their data. That is why the output filter is not a security whim, but minimisation applied at source: the field does not reach the model, so it cannot reach the conversation or the transcript.
What is kept from a call
The caller’s number and the called number, the time, duration, result, audio recording, transcript and post-call analysis. For unanswered calls there is the number, time and reason for no answer — there is no audio, because it did not happen. All of it is tied to your company’s workspace; there is no shared repository across clients.
The announcement at the start of the call is your decision, not a setting
That the interlocutor is speaking with an automated system, that the call is recorded and on what basis — these are established together with you and enter the scenario, in both directions: both towards the customer and towards the courier. The courier called by an agent needs to know what they are talking to just as much as the customer does.
Deletion on request exists; automatic deletion at term does not yet
We say this as it is, because it is the difference between a promise and a function. Deletion on request is implemented: a dedicated function deletes the objects from the file repository, calls the database deletion procedure and invalidates sessions. Configurable retention per workspace is not implemented — it appears in a design document, in no migration. Until it is, deletion at term is done by procedure.
Where the data lives
PostgreSQL through Supabase on own infrastructure, with versioned migrations, part of which activate row isolation. Files — recordings, knowledge documents, voice samples — are stored in separate repositories, with the path beginning with the workspace identifier.

A case

A call that has state, not just a result

The situation

In a delivery flow, the question “where is my order” has no answer in any system: the answer is with the courier. The simple version — the agent writes the courier a message and hopes — fails at the first courier who is driving and not looking at the phone.

What we built

We built dispatching as a table, not as a function. Each call to the courier has a row with seven possible states, the attempt number, a maximum of three attempts, a two-minute pause between them, and the result stored in a structured way — summary, estimated time, position. The row keeps the link with the customer conversation it came from. Around the table: the function that starts the call, the one that checks the result, the sweeper for the calls left hanging, the identification of the courier who calls back, and the webhook after the call.

What came out

It is possible to answer, at any time and from data, the questions a dispatch office receives daily: was the call made, how many times, what did it answer, why was it not called. A missed call no longer remains blocked in an intermediate state, because there is someone to sweep it up.

What the case does not say

The mechanism is written and migrated into the platform; the telephony part depends on a SIP host, and the one through which our test lines pass does not answer as of the time of writing. We do not present dispatching as something you can try by calling one of our numbers today.

Questions

What someone in transport & logistics asks

Does the agent call the courier itself or just send them a message?

It calls. There is a server function that starts the call to the courier, linked to the conversation it came from, with the order context passed as a structure. The call has its own row in a table, with state, attempt number and result — so it is possible at any time to answer the question “was it called, and what did it say”.

What happens if the driver does not answer?

It is retried. By default three times, two minutes apart, values written in the table and in the process configuration. If it still does not answer on the third try, the call remains in the `no_answer` state — a distinct state from `failed`, which means something failed on our side. That distinction is why a table is worth it, not a log.

Can the agent give the customer the driver’s number?

No, and not because we told it not to. The driver’s contact fields are removed recursively from the tool result before that result reaches the model. The dispatch office numbers remain. What does not reach the model cannot be dictated, no matter how cleverly the question is phrased.

Can it say where the parcel is right now?

It can say what the courier told it on the last call, with the time of that call, or what it reads from your system if it is connected to it. What it does not do — and that is a decision, not a limitation — is estimate on its own. An estimate invented by an agent becomes a promise that the driver bears at the door.

Can I call a demo number now?

Not today. The telephony part goes through a SIP host, and the host through which our test lines go does not answer as of the time of writing this page. We do not put a number on the site that we could not pick up in front of you. The voice path on the web is another story: there it can be checked immediately.

Does it speak Russian with the customer and Romanian with the dispatcher?

The agent configuration supports 32 languages, and Romanian is the default language of a new agent. The language is set per agent, so the agent that speaks with the customer and the one that calls the courier can be configured differently. What should be known: for speech synthesis in languages other than English, the default model is the fast one, and the higher-quality version is visibly slower — the trade-off is chosen consciously.

What happens when the client interrupts it?

It stops. Semantic speech detection has a 0.5 threshold, a 300 ms buffer and 500 ms of silence, and interruption is allowed by default; on the other path, the interruption event is sent to the core to cut playback. If an agent cannot be interrupted, it is not a tone issue, but a configuration issue — and responses that are too long.

How long are driver call recordings kept?

As long as you decide, because you are the data controller. What you need to know about the current state of the platform: deletion on request is implemented as a feature, and automatic deletion on срок, configurable per workspace, is not — it is done by procedure. We would rather you hear this from us, not from an audit.

Does it replace the dispatcher?

No. It takes on the repetitive part — the same question, the same call, the same answer — and leaves the exceptions to them. Transfer to them remains under a keypad code, and in transport exceptions are frequent: refused goods, wrong address, client not answering. That is where a person is needed, and it is better if it is a person who has not already answered a hundred 'where is the parcel?' questions.

What would you want to work better?

Tell us about your process. Together we decide what is worth building, what we can connect, and how we check the result.

Let's talk