Skip to content

Logistics · AI

Vezira

A haulage company with a mixed fleet: the assistant reads a dispatcher’s request, gathers data from the CRM, the TMS and the telematics, and does only what is permitted.

Role
Backend, integrations, AI assistant
Timeline
6 weeks
Stack
NestJS · PostgreSQL · OpenAI API

What changed

11 → 4.2 minutes
to handle a new request. Gathering the data for it is no longer the dispatcher’s job.
14% → 3.8%
of requests need correcting by hand. Copying errors go away along with the copying.
43%
of routine operations are done through the assistant, without moving between systems.
+21%
more requests handled by one dispatcher in a shift.

The figures come from the pilot week — five dispatchers out of seventeen. The company stayed on the systems it had: the CRM, the TMS and the telematics run where they always did, and nobody was moved into a new product. The AI interface sits over the existing infrastructure, so its limits came through to the assistant along with the data.

Before

A haulage request started in one window and finished across several: the dispatcher entered it in the CRM, looked for a free lorry in the TMS, checked where that lorry actually was in the telematics, and wrote the dates and load details into email and messaging apps. Data crossed between those systems by hand, with the address, the date and the weight typed a second and a third time.

  • More than 4,800 haulage requests a month went through the dispatch office. None of the systems on a request’s path held it whole: it came together only with the person working it.
  • Seventeen dispatchers moved data between the CRM, the TMS, email and messaging apps. That is not work on a load and not a conversation with a customer, but the same thing typed again in the next window.
  • Eleven minutes was the average for handling one new request. At 4,800 requests a month that is not one person’s delay, it is the timetable of the whole dispatch office.
  • 14% of requests needed correcting: an address, a date, a price or a load parameter reached the next system as something else. Those are errors of copying rather than of judgement — each one appeared at the moment the data was typed in a second time.

What we built

  • One API over three systems: the CRM, the TMS and the telematics answer a dispatcher’s question together rather than one window at a time.

  • A tool layer instead of database access: the model asks for a call, and permissions, parameters and business rules are checked by our code.

  • A log of every action: who asked, which tool ran, with which parameters and what came back — the prompts were tuned against it.

Outcome

A dispatcher asks, and the assistant builds the answer out of the CRM, the TMS and the telematics, checks the constraints and carries out what that person is allowed to do. A new request takes 4.2 minutes rather than eleven, and 3.8% of requests need correcting rather than 14%. The systems themselves did not move: what changed is not the record-keeping but the way to reach it. Choosing between the options is still a person’s job.

The client

A haulage company carrying freight on its own lorries and on hired ones. Requests, drivers and trips are kept in a CRM and a TMS, vehicle positions arrive from telematics. The dispatch office is seventeen people and more than 4,800 requests a month.

With a mixed fleet, matching a lorry to a request is not a lookup in a table but several constraints at once: the payload, where the vehicle will be tomorrow, how much empty running it adds. The answer comes from several systems, and no one of them holds it whole.

The brief

What the client asked for
A GPT chat for dispatchers: a box you type “find me a lorry for tomorrow, Moscow to Kazan” into and get an answer back. Put that way, the project looked like plugging a ready-made chat into a workstation.
What the brief turned into
The chat itself settles almost nothing. A real request reads less like “find me a lorry” and more like “find a free lorry up to 20 tonnes that will be within 100 km of Podolsk tomorrow, and give me three options with the least empty running”. An answer like that is built from several systems: read the request, fetch the data, check the constraints, offer options, do what is permitted. Not a chat, but an AI layer over infrastructure that already runs.

Why the obvious route was closed

The layer was not built on empty ground: the CRM, the TMS and the telematics were already running, the facts were scattered between them, and some actions could not be handed to a model at all. Six conditions, each of them visible in the architecture.

The TMS has no external API
There was no usable external API on the TMS, and trips and vehicles are kept precisely there. There was nothing for an assistant to call: a backend of our own had to come first, and it is the only thing that talks to the TMS.
The facts sit in three systems
Part of a request existed only in the CRM, part of it in the TMS, and current vehicle positions came from a separate telematics system. Not one dispatcher’s question could be answered from a single source, so the common layer was needed before the assistant was.
Some arrangements live in chat
What had been agreed about a trip stayed in Telegram chats: text, not fields. No query condition selects data like that — it has to be found by meaning, and the same tool layer had to be able to do it.
A model cannot be trusted with writes
Critical data — the request, the trip, the price — could not change because a model decided so. The AI therefore got no direct access to the database at all: it can ask for a tool, and what the tool then does to the database is written by us rather than suggested by the model.
Permissions are not a property of the conversation
The assistant acts on behalf of a member of staff and may do exactly what that person may do. A permission check cannot live in a prompt: what the model asked for and what it is allowed to run are two different lists, and the tool layer reconciles them before the call.
Load and trip limits are company rules
Tonnage, distance and load parameters are not preferences inside a sentence but conditions that have to hold. A model can be wrong about a number; a tool cannot — it checks the parameters and the business rules itself, before anything happens.

How the work ran

  1. 01

    Auditing the process and the integrations

    Week 1

    Working out what a dispatcher actually does: which systems get opened, in what order, and for which answer. The week produced a map of dispatcher actions — a list of operations, each of which later became either a tool for the assistant or something left to a person.

  2. 02

    One backend over the systems

    Weeks 2–3

    Two weeks went on an API that had never existed: the CRM, the TMS and the telematics behind one interface, with shared notions of a request, a vehicle and a trip. No assistant yet — the layer first learned to answer questions on its own, over plain REST.

  3. 03

    The assistant, its tools, its permissions

    Week 4

    The model was handed a set of tools rather than a database: find a vehicle, check a constraint, see where it will be tomorrow, carry out an action. Every call goes through a check of permissions and parameters. Roles arrived here too — who is allowed to invoke what.

  4. 04

    Automated scenarios and the action log

    Week 5

    Some operations stopped needing to be asked for: the routine ones run by themselves. Everything the assistant does is written to a log — who asked, which tool ran, with which parameters and what came back. Without that log there is nothing to examine afterwards.

  5. 05

    Pilot and prompt corrections

    Week 6

    Five dispatchers worked with the assistant on live requests. Prompts and rules were corrected against what went wrong: a request read the wrong way, a clarifying question that need not have been asked, the wrong tool invoked. That is not polish but the condition for the thing being used.

Technical decisions

The stack answers one condition: the model never reaches into another system itself. NestJS holds the tool layer and the boundary the model does not cross, PostgreSQL keeps the data and the log, and the OpenAI API answers for understanding the request and nothing else.

  • One API instead of three doors

    The assistant knows nothing of the CRM, the TMS or the telematics: it knows our backend, and the backend works out who to ask for what. Three systems gained one vocabulary — request, vehicle, trip — and one answer to a question that used to be assembled from three windows.

  • A tool layer, not database access

    Between the model and the corporate systems sits a tool layer of our own. The model can only ask for a call with parameters; NestJS makes it, checking the person’s permissions, the parameters themselves and the business rules. A mistake by the model comes out as a rejected call rather than an altered request.

  • A log of the calls

    Every action of the assistant is a row: the member of staff, the request, the tool, the parameters, the result. It is the only way to answer why a trip looks as it does once part of the work was done by a model. The same log shows what gets called most, and the pilot’s prompt corrections were made against it.

  • What runs without being asked

    Some operations are identical from one request to the next, and there is no point putting them to a dispatcher. Those scenarios were moved to a background queue: they run by themselves and land in the same log. The conversation is left for what needs a person’s decision.

  • Chat text next to the tables

    Some facts about trips live in chats, so PostgreSQL carries a vector index (pgvector) alongside the ordinary tables: the search tool returns the fragments that bear on the question rather than the whole thread. The model receives them as data, never as permission to change anything.

What stayed outside the scope

The layer answers for reading a request, gathering the data and carrying out permitted actions. Everything around it stayed where it was.

  • Replacing the corporate systems. The CRM, the TMS and the telematics were neither rewritten nor migrated: the work ended at the boundary of the layer that calls them.

  • Writing straight from the model. No tool hands the database outward: a new action is written and permitted by hand rather than explained in a prompt.

  • Choosing between the options. The assistant assembles the options and shows them; the dispatcher decides. Six weeks did not produce a rule that could choose on their behalf.

  • The whole flow. Week six was a pilot on five dispatchers, and the prompts were tuned against their work. What all 4,800 requests a month will show is not something six weeks answers.

The work that does not end with the project: rules and prompts are corrected against the call log — it is where a request the assistant read wrongly shows up, and there is no other source for those corrections.

Contact

Send a description of the task

A reply with the scope, the timeline and a budget estimate comes within 24 hours.

The first call is 30 minutes, with no commitment on your side.