/ 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.
Mave Beauty
online
HARI INI
Agent gateway
AGENT_MODETool calls
{ phone: "+62811…141" }{ service: "brows-ombre", date: "2026-07-17" }{ service: "brows-ombre", studio: "gading-serpong" }{ 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-gated —
handoverToHumanon 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
/ 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.
Customer asks
“Is there a slot for ombré tomorrow?”
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_MODEis 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_MODEgates 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.
Masalah
Percakapan pelanggan hidup di nomor WhatsApp pribadi — tidak ada yang dibagi antar staf, tidak ada yang diaudit, dan volume pertanyaan berulang yang meningkat (jam buka, harga, booking, reschedule) yang tak sempat dijawab siapa pun dengan cepat. Chatbot polos tidak akan cukup: agar berguna, benda ini harus benar-benar booking dan reschedule, bukan sekadar ngobrol.
Pendekatan
Sebuah agen yang bertindak lewat API nyata milik bisnis. Sebuah gateway generik menjalankan agennya, dan tiap kemampuan yang dimilikinya adalah tool ber-type yang didukung sebuah Port ke kode produksi — cek ketersediaan, tahan slot, cari pelanggan. Penyedia pesan dan orkestrator duduk di balik sebuah adapter, jadi agen yang sama jalan di bawah Business Agent milik Meta atau penyedia lain tanpa menyentuh domainnya. Dan semuanya diam sampai sebuah flag menyalakannya.
- Tool Runner → Ports → API produksi: agennya booking dan reschedule dengan memanggil endpoint yang sama dengan yang dipakai staf.
- Sebuah ingress agnostik-provider —
verify → parse → gateway → formatResult— di mana route-nya tidak memuat logika bisnis maupun format-kabel provider. - Digerbangi flag: diam kecuali
AGENT_MODEdisetel (default mati). Jabat-tangan verifikasi webhook tetap menjawab, jadi URL callback bisa didaftarkan sebelum peluncuran — tapi tidak ada tool yang pernah jalan saat mati. - Observabilitas terpasang sejak awal: trace ID per-panggilan, dead-letter queue, metrik beta — dan penyerahan ke manusia untuk apa pun yang sensitif.
Chatbot berbicara; agen bertindak. Bedanya adalah tool ber-type yang tersambung ke API nyata — dan sebuah flag yang kamu kendalikan.
Arsitektur
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)
Keputusan kunci & trade-off
- Tool ketimbang omongan. Nilai agennya ada pada melakukan, bukan mengobrol — jadi tiap kemampuan adalah tool ber-type yang didukung Port ke kode nyata, bukan trik prompt.
- Agnostik-provider sejak dirancang. Ingress-nya tidak membawa logika bisnis maupun format-kabel provider; mengganti Meta dengan orkestrator lain adalah soal adapter, bukan tulis ulang.
- Mati secara default, bisa diverifikasi saat mati.
AGENT_MODEmenggerbangi tiap tool; satu-satunya yang dijawab saat mati adalah jabat-tangan webhook, jadi callback-nya bisa didaftarkan sebelum peluncuran. Tidak ada agen tanpa pengawasan pada pelanggan sungguhan. - Terobservasi sejak hari pertama. Trace ID, dead-letter queue, dan metrik beta — sebuah agen yang memanggil API produksi butuh jejak.
Hasil
Sebuah agen AI yang benar-benar bisa bertindak pada bisnis booking — lewat tool ber-type ke API produksi, di bawah adapter yang agnostik-provider, sepenuhnya terobservasi, dan mati sampai terbukti siap.
/ 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.
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.
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.
Tiap kemampuan adalah tool ber-type yang didukung sebuah Port ke kode nyata. Agennya booking dan reschedule dengan memanggil endpoint yang sudah dibuka aplikasi — bukan dengan berpura-pura lewat prosa.
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.
Ingress-nya tidak memuat logika bisnis maupun format-kabel provider: verify → parse → gateway → formatResult. Mengganti Business Agent milik Meta dengan orkestrator lain adalah soal adapter, bukan tulis ulang.
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.
AGENT_MODE menggerbangi tiap tool; hanya jabat-tangan webhook yang menjawab saat mati, jadi callback-nya bisa didaftarkan sebelum peluncuran. Tidak ada agen tanpa pengawasan pada pelanggan sungguhan.
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.
Trace ID per-panggilan dan dead-letter queue memberi jejak; apa pun yang sensitif atau berkepercayaan-rendah dieskalasi ke manusia di inbox yang sama.