Chat agents
Multilingual chat agent for Mews: a Bruges hotel build
It is 23:00 at a Bruges hotel reception. Three guests waiting, one in German asking about a no-show fee. The chat agent we wired into Mews answers two of them first.

The night that justified the build
It is past 23:00 at a four-property hotel group in Bruges. One receptionist is on the front desk. Three guests stand in line: a Dutch couple who want to extend their stay by one night, a French solo traveller arguing about a late-checkout charge, and a German guest staring at his phone trying to work out why he was billed for a no-show on a booking he says he cancelled.
The group runs nineteen people across four small hotels. Receptionists, housekeepers, an operations lead, the owner. Front desk coverage at night collapses to one person per property. That person can do one conversation at a time. The other two guests in the line lose patience and write a one-star review later in the week.
The brief we got was short and specific. Build a chat widget on the hotel website that can handle three things, in three languages, end to end, without a receptionist touching the keyboard. Rebook a stay (date change within the existing reservation). Request late checkout (departure-time change with the right fee applied). Pay a no-show fee on a missed booking. Anything outside those three workflows gets handed to a human, calmly, with the full conversation attached.
Below is the build, step by step.
Three workflows, mapped to Mews calls
We started by listing the exact Mews endpoints each workflow would touch. Mews exposes its Connector API at the property level. Each property issues an access token to the partner integration, scoped to that property's data. The three flows broke down like this.
Rebook (date change). The agent reads the reservation, checks rate-plan availability for the new dates, quotes the price delta, asks the guest to confirm, then updates the reservation. The endpoints involved are reservations/getAll to find the booking, services/getPriceUpdates to quote, reservations/update to apply. The agent never invents a price. If the quote call fails for any reason, the agent escalates.
Late checkout. The agent reads the reservation, checks the property's late-checkout policy for that rate plan, applies the fee if the policy requires one, and posts it to the customer's bill. Endpoints: reservations/getAll, accountingItems/addAdjustments for the fee, then reservations/update to extend EndUtc.
No-show fee. The agent reads the no-show clause from the reservation's cancellation policy, quotes the exact amount, takes consent, processes the card payment against the customer's stored payment method, then records the payment against the account. Endpoints: reservations/getAll, payments/charge for the stored card, payments/add to record.
For each flow we wrote a one-page acceptance script: what the agent should say, what it should do, what the receptionist must see in Mews afterwards. Acceptance was tested by the operations lead, not by us. Same lesson as every other agent we have shipped: if your client cannot run the acceptance test alone, the agent is not ready.
The Mews tool surface
Mews authenticates every Connector API call with two tokens. The ClientToken identifies the integration. The AccessToken identifies the property granting access. Both go inside the JSON body of every request, not in HTTP headers, which surprises people used to OAuth bearer tokens.
curl -sS https://api.mews.com/api/connector/v1/reservations/getAll \
-H 'Content-Type: application/json' \
-d '{
"ClientToken": "'"$MEWS_CLIENT_TOKEN"'",
"AccessToken": "'"$MEWS_ACCESS_TOKEN"'",
"Client": "ABN Chat Agent 1.0",
"Extent": { "Reservations": true, "Customers": true },
"Numbers": ["BR-260608-0042"]
}'
The pattern is the same for every endpoint: POST, JSON body, two tokens, payload. There are no path parameters and no GETs. Once you accept that, the SDK question solves itself: you do not need one. A thin wrapper of three functions (get, update, charge) is enough for an agent of this size. The full Mews Connector API reference documents the rest.
Two gotchas we ran into.
First, time. Mews wants UTC strings everywhere. The hotel runs on Europe/Brussels. The agent shows guests their local time. We did every conversion inside the orchestrator, never in the prompt. Letting the model do timezone arithmetic is how you ship bugs no QA pass will catch.
Second, idempotency. The Connector API does not deduplicate requests for you. If the guest double-taps the confirm button and your orchestrator fires two reservations/update calls, you get two updates. We added a per-conversation idempotency key that gates any duplicate tool call within a thirty-second window. One line of state, and it has prevented every double-write in production so far.
The language layer
Three languages, one widget. Detection happens twice.
First pass is the browser. We read navigator.language and the Accept-Language header for an opening guess. That decides whether the widget's first message goes out in Dutch, French, German, or English (fallback). The guess is right about eight times out of ten.
Second pass is the first guest message. A small classification call locks the conversation language. Once locked, the language does not switch unless the guest explicitly asks for another one.
The reason for the lock: guests in Bruges often write in their own language with a few English words mixed in ("hallo, I want to extend my stay"). Without the lock, you get an agent that flips mid-sentence and reads like a phrasebook. With the lock, it stays in one voice.
The third piece is a glossary. Hotel-specific terms (late checkout, no-show fee, OTA, rate plan, deposit, city tax) have agreed translations per language, pinned in the system prompt. This matters more than people think. "No-show fee" in literal Dutch is awkward. The owner wanted vergoeding bij niet-verschijnen. We hard-coded the glossary and the agent stopped inventing variants.
The glossary lives in a small JSON file. We wired the editor into the ops dashboard so the operations lead can add or rename a term without us. When a guest asked about a pax refund (a per-person term Booking.com uses) the agent escalated, the ops lead added the Dutch and French entries the same afternoon, and the next guest who used the word got an answer. That feedback loop is the part that keeps a multilingual agent honest. If only engineers can update the glossary, it goes stale within a month.
Agent tools and the prompt
Six tools, no fewer.
const tools = [
{ name: 'lookup_reservation', /* by confirmation number or last name + arrival */ },
{ name: 'quote_date_change', /* services/getPriceUpdates */ },
{ name: 'apply_date_change', /* reservations/update */ },
{ name: 'apply_late_checkout', /* accountingItems/addAdjustments + reservations/update */ },
{ name: 'charge_no_show_fee', /* payments/charge + payments/add */ },
{ name: 'escalate_to_human', /* posts transcript to ops Slack */ },
];
The system prompt is two screens long and most of it is rules, not personality. Personality is one paragraph at the top. The rule section covers: when to call which tool, what to never invent (prices, policies, availability), how to escalate, what to say when a tool returns an error, and how to confirm consent before any payment or any reservation mutation.
Every tool definition is strict about what the model must produce. Here is the late-checkout tool, trimmed:
{
"name": "apply_late_checkout",
"description": "Extend the guest's departure time. Always quote the fee before calling. Never call without an explicit yes from the guest.",
"input_schema": {
"type": "object",
"required": ["reservation_id", "new_departure_local", "fee_eur_cents", "guest_consent"],
"properties": {
"reservation_id": { "type": "string" },
"new_departure_local": { "type": "string", "description": "ISO local time, Europe/Brussels" },
"fee_eur_cents": { "type": "integer", "minimum": 0 },
"guest_consent": { "type": "boolean" }
}
}
}
We chose tool-use over a multi-step prompt chain because every action that touches the booking system has to be visible to the receptionist in real time. The orchestrator logs every tool call into a small Postgres table and pushes a notification into the night-desk Slack when a flow finishes or fails. The receptionist can scroll the audit log without opening the agent's transcript.
Handing off to humans
The escalation path is the most important non-feature in the build.
The agent escalates when any of these is true:
- The guest asks for anything outside the three workflows.
- The guest uses language flagged by a small classifier (complaint, refund, fire alarm, illness, dispute, lawyer).
- The language detector disagrees with itself twice in the same session.
- A Mews call fails twice in a row.
- The guest explicitly asks for a human.
Escalation is a warm transfer, not a dump. The agent posts the full transcript, the matched reservation ID, the guest's locked language, and a one-line summary into a Slack channel the night receptionist watches. Then it tells the guest, in their language, that a colleague will join the chat in a moment. The receptionist replies from the same Slack thread and the message is forwarded back into the widget. The guest never sees the channel hop.
If escalation is even slightly inconvenient for the receptionist, the agent will be blamed for every awkward conversation it correctly handed over. Make the handover the easiest action in the entire stack.
Going live without a wreck
Shadow mode first. Two weeks. The agent ran in parallel with the receptionist but did not message guests. Every conversation generated a draft response that was logged. The operations lead reviewed the drafts each morning. We tuned the prompt and the tool descriptions twice on the back of that review.
Then a soft launch on one property only, between 22:00 and 07:00. The receptionists on the other three properties were the safety net. The agent handled twenty-three conversations the first week. Eighteen resolved without a human. Two were escalations the agent caught correctly. Three were edge cases the prompt had not seen, and they became new test fixtures the following Monday.
Two patterns came out of week one we did not predict. First, guests who opened in English switched to Dutch as soon as the agent answered in Dutch. The browser-language guess was wrong more often than we expected, but the first-message classifier corrected it inside the opening exchange every time. Second, late checkout outran rebook by roughly four to one. The owner had told us rebook would dominate. We trimmed the rebook examples out of the system prompt and added two more for late-checkout edge cases, which cut prompt tokens and improved tool-call accuracy in the same change.
Full rollout came in week four. The metric the owner cared about was not deflection rate. It was minutes-per-incident at the front desk between 22:00 and 02:00. That number dropped to a level that paid for the project inside the first month. The owner asked us not to publish the figure. The shape of it: a single overnight receptionist now covers four properties without backup on a Tuesday in March, and the queue does not form.
Whatever you make of the recent argument that large language models are slowing down, the agentic work that sits on top of stable APIs like Mews has not slowed down at all. The model is the easy part. The hard part is the glossary, the escalation rules, and the acceptance script the operations lead can run on a Sunday morning by themselves.
The five-minute audit
If you operate a small hotel and you want to know whether this is worth doing for you, the audit is this. Pull last month's after-hours emails, WhatsApp messages, and chat threads. Tally the requests by type. If more than half are some version of rebook, late checkout, or payment-related, you have an agent-shaped problem, and a Mews API key is enough to start.
When we built the chat agent for the Bruges group, the longest piece of work was not the Mews integration. It was the glossary in three languages and the escalation rules that the night receptionist trusted enough to leave the agent alone. We do this kind of work as AI agents for operators who are not large enough to staff a 24-hour front desk and not small enough to handle the queue on a Friday at midnight.
Key takeaway
A hotel chat agent earns its keep through escalation rules and a tight glossary, not the model. The Mews API is the easy part; the warm handover is the hard part.
FAQ
Why not just use the chat widget that ships with Mews?
Mews has a guest-messaging product and it is fine for messaging. It does not call the API to mutate reservations or take payments, which is the whole point of this build.
How do you stay out of PCI scope on the no-show payment?
The agent never sees the card number. It charges the stored payment method on the customer profile through the Mews payments endpoint. Card data stays inside Mews and the receptionist confirms the chain in Mews afterwards.
What does it cost to run per month?
Token spend per resolved conversation is well under one euro at current API rates. Hosting is a small Postgres database and a single worker. The dominant cost is engineering hours to keep the glossary and the escalation classifier current.
Which model did you use?
We compared two general-purpose models and chose the one that called tools more reliably on Dutch and French inputs. Model choice changes every six months. The tool definitions and escalation rules do not.
Can the same agent handle languages beyond Dutch, French, German?
Yes, once the glossary is translated and the escalation classifier has seed phrases in that language. Adding a fourth language to the same agent took us about a day per property.