Skip to content

/ Case study — Messaging

AI Customer Service

An AI agent that does more than chat — it books and reschedules by calling the same APIs the staff use. Built so you can swap the model without touching the tools, and kept switched off until it’s ready.

RoleSystems
Year2025
TypeMessaging
Next.jsTypeScriptDrizzleTursoWeb Push
mave · whatsapp-agent Live recreation
Mave Beauty

Mave Beauty

online

📞

HARI INI

Ketik pesan

Agent gateway

AGENT_MODE

Tool calls

findCustomer
{ phone: "+62811…141" }
checkAvailability
{ service: "brows-ombre", date: "2026-07-17" }
getPrice
{ service: "brows-ombre", studio: "gading-serpong" }
createBooking
{ slot: "2026-07-17T10:00", dp: 500000 }

Why it holds up

  • Typed ports — tools are contracts, not prompts.
  • Provider-agnostic — swap the model, keep the tools.
  • Flag-gatedhandoverToHuman on any doubt.

What the customer sees, and what the agent actually calls — the real tool names.

mave · whatsapp-agentFIG.00 — Overview
  • Provider-agnostic adapter — swap orchestrator, not domain
  • Tool Runner → typed Ports → real production APIs
  • Flag-gated (AGENT_MODE off) · trace IDs · dead-letter queue
MessagingAI Customer Service
Tool-calling
Agent
Provider-agnostic
Adapter
Flag-gated (off)
Safety
Human-in-the-loop
Handover

/ What it does

Answers your customers on WhatsApp, day and night

An AI assistant that replies instantly on WhatsApp — answering questions, checking availability, even booking — and quietly hands over to a real person whenever it should.

A customer messages inStep 1 / 4

Customer asks

“Is there a slot for ombré tomorrow?”

WhatsApp
  • Never miss a message

    Every customer gets an instant reply, even after hours.

  • Frees up your staff

    The repetitive questions answer themselves; people handle the rest.

  • Always in control

    It only goes live when you switch it on, and a human can always step in.

Problem

Customer conversations lived on personal WhatsApp numbers — nothing shared between staff, nothing audited, and a rising volume of repetitive questions (hours, prices, booking, rescheduling) no one had time to answer fast. A bare chatbot wouldn’t cut it: to be useful, the thing has to actually book and reschedule, not just talk.

Approach

An agent that acts through the business’s real APIs. A generic gateway runs the agent, and every capability it has is a typed tool backed by a Port into production code — check availability, hold a slot, look up a customer. Messaging providers and orchestrators sit behind an adapter, so the same agent runs under Meta’s Business Agent or another provider without touching the domain. And the whole thing is inert until a flag turns it on.

  • Tool Runner → Ports → production APIs: the agent books and reschedules by calling the same endpoints staff use.
  • A provider-agnostic ingress — verify → parse → gateway → formatResult — where the route holds no business logic and no provider wire-format.
  • Flag-gated: inert unless AGENT_MODE is set (default off). The webhook-verification handshake still answers, so the callback URL can be registered ahead of rollout — but no tool ever runs while off.
  • Observability built in: per-invocation trace IDs, a dead-letter queue, beta metrics — and human handover for anything sensitive.

A chatbot talks; an agent acts. The difference is a typed tool wired to a real API — and a flag you control.

Architecture

WhatsApp / orchestrator (Meta Business Agent · …)
  → provider.verify → provider.parse
    → gateway: runAgentInvocation
        → Tool Runner → Ports → production APIs (book · reschedule · lookup)
    → provider.formatResult → reply
Cross-cutting: AGENT_MODE flag (off) · trace IDs · dead-letter queue · handover
Notify: Web Push (VAPID)

Key decisions & trade-offs

  • Tools over talk. The agent’s value is doing, not chatting — so every capability is a typed tool backed by a Port into real code, not a prompt trick.
  • Provider-agnostic by construction. The ingress carries no business logic and no provider wire-format; swapping Meta for another orchestrator is an adapter, not a rewrite.
  • Off by default, verifiable while off. AGENT_MODE gates every tool; the only thing answered while off is the webhook handshake, so the callback can be registered before launch. No unsupervised agent on live customers.
  • Observable from day one. Trace IDs, a dead-letter queue, and beta metrics — an agent calling production APIs needs a paper trail.

Result

An AI agent that can genuinely act on a booking business — through typed tools into production APIs, under a provider-agnostic adapter, fully observable, and off until it’s proven ready.

/ How it’s done

The agent, acting

Pick an intent and watch the agent call a typed tool — the same production API staff use — instead of just replying.

whatsapp · inboundAGENT_MODE · off (demo)
Ada slot ombre besok pagi di Gading Serpong?
agent → tool availability.check( {"studio":"gading-serpong","service":"ombre","date":"2026-07-17"} )
Port → production API → { slots: ["10:00", "11:30", "14:00"] }

A chatbot talks; an agent acts. Each capability is a typed tool backed by a Port into the same production APIs staff use — so it really books and reschedules. Providers and the model sit behind an adapter, and it stays inert until AGENT_MODE is on.

01

Tools over talk

Every capability is a typed tool backed by a Port into real code. The agent books and reschedules by calling the endpoints the app already exposes — not by pretending in prose.

02

Provider-agnostic by construction

The ingress holds no business logic and no provider wire-format: verify → parse → gateway → formatResult. Swapping Meta’s Business Agent for another orchestrator is an adapter, not a rewrite.

03

Off by default, verifiable while off

AGENT_MODE gates every tool; only the webhook handshake answers while off, so the callback can be registered before launch. No unsupervised agent on live customers.

04

Observable + human-in-the-loop

Per-invocation trace IDs and a dead-letter queue give a paper trail; anything sensitive or low-confidence escalates to a human in the same inbox.