WhatsApp lead agent · Dubai property

Sakan

Answers a buyer at 11pm in their own language, quotes only prices the brokerage actually filed, and hands the sales floor a graded lead.

It will not invent a price and it will not tell anyone what something is worth. Both refusals are enforced in code, not asked for in a prompt.

Arabic · Urdu · Hindi · English Meta WhatsApp Cloud API live demo below — type anything 106 tests
S
Dubai brokerage
● online · replies in seconds
2 bed in JVC how much?
Binghatti Aurora, JVC — 2 bed from AED 1,450,000
Belgravia Heights III, JVC — 2 bed from AED 1,850,000
whats the payment plan
Binghatti Aurora — 70/30, 70% during construction, 30% on handover
and in Palm Jumeirah?
We don't cover that one. Which area did you have in mind?
is it a good investment? what roi
That's a conversation worth having properly — one of our agents will call you and go through the numbers.
handover → human · advice
try: 2 bed in JVC how much? · payment plan · is it a good investment? · and in Palm Jumeirah? · budget 1.5m, this month, cash
What the sales floor would get: nothing yet
Coverage

What it does, and what it doesn't.

The honest version. Three of these are integrations into systems I haven't seen yet, and one is a dashboard nobody has asked for.

✓
WhatsApp agent: lead qualification and conversations
Meta Cloud API direct — webhook signature verification, interactive replies, four languages. Five qualification signals, graded with the reason. Conversations persisted as the model's memory and the audit trail.
✓
Meta Marketing API: campaigns, budget rules, creative tests, reporting
All four. Objects are created paused with no override, budgets are integers in minor units, and the budget rules evaluate as a pure function then apply as a dry run by default.
✓
Pixel / Conversions API tracking
Server-side events with PII normalised and SHA-256'd before sending, and an event id on every one so the pixel and the server deduplicate. Without that, cost per lead reads at half its real value.
✓
Cost control for LLM usage
Every call metered, attributed to the conversation that caused it. Three ceilings — per conversation per day, and per month — checked before the call. Going over hands the customer to a human rather than erroring.
✓
Logging and error handling
Writes to the ads API are logged before they are sent, redacted, so "did we do that?" always has an answer. A model that fails, misbehaves or goes over budget degrades to a handover — never to a number.
✓
Documentation: architecture, credentials, runbooks
Written as the work was done rather than afterwards. Every prompt is a file, every guarantee has a test naming the failure it prevents.
~
Follow-ups
Agent-side is live: unclaimed hot leads are chased after two hours, and a summary goes out at six. Customer-side reminders need WhatsApp templates approved against the brokerage's own number, which cannot happen before that number is connected.
~
Connect the stack: webhooks, REST APIs, event tracking
Webhooks in, Graph API out, conversion events back. The web platform and analytics ends are standard once there is something to point them at.
✗
CRM write-back
The graded lead is produced and served over an endpoint; nothing pushes it into a CRM yet. That is one function once I can see which CRM and what its lead object looks like.
✗
Monitoring beyond health checks
There is a health endpoint, a spend endpoint and a lead endpoint. No metrics pipeline and no alerting — worth adding when somebody other than me is on call.
✗
Product analytics, and Lead Ads form ingestion
No PostHog, and the leadgen webhook that would drop a Meta form submission straight into the agent's queue is not built.
✓ built and running ~ partly, and the rest is blocked on access ✗ not built
The two refusals

A chatbot that is usually right is no use here.

A buyer repeats your number to their bank. One invented figure is the brokerage's problem, not the model's — so the model is never the source of one.

It cannot invent a price

Every figure is read from the brokerage's own inventory file. An outbound guard holds the set of numbers those facts support, and a reply carrying anything else never leaves the process — it becomes a handover instead.

model wrote
"around AED 1,250,000, you'll see 9% yield"
customer received
"Let me have an agent confirm the exact figures and call you."

It gives no investment advice

Yield, appreciation, whether an area is going up, whether to buy — always a person. Checked before the price lookup, so an ROI question hiding inside a price question still reaches a human. That is RERA exposure, not politeness.

asked
"what's the ROI on a 2 bed in JVC and how much is it?"
routed
handover · reason: advice
What the sales floor gets

One line, with the reason attached.

Five signals, read out of whatever the buyer volunteers. Asking again for something they just said is the fastest way to lose a chat, so one message can fill all five. A grade nobody can predict is one the floor stops trusting, so every grade carries why.

HOT +971 5• ••• ••47
Budget
AED 1.5m
Size
2 bedroom
Area
JVC
Timeline
This month
Purpose
Investment
Payment
Cash
Graded hot: buying now, cash. Said "cash" after asking about mortgages earlier — the later statement wins, so nobody rings them about financing.
The loop

The ads and the agent are one system.

Split them and the optimiser never learns which campaign produced a buyer rather than a click.

01

The ad

Built on the Marketing API — campaign, ad set, creative test. Created paused, always.

02

The chat

The click opens WhatsApp. The agent answers in seconds, in the language they typed.

03

The agent

A graded lead reaches a human, with the whole conversation attached.

04

Back to the ad

The booking is reported through the Conversions API, hashed and deduplicated, so the campaign optimises on customers.

The ads side

It decides, then it asks.

Last week's ad sets, and what the budget rules propose. Move the target and watch the decisions change — this is the real rule engine running, not a picture of one.

Last 7 days

Ad setSpendLeadsCPLCTR

What the rules propose

AED 40
Nothing has been changed. evaluate is a pure function and apply is a dry run unless told otherwise — a budget rule that acts the moment it forms an opinion is how an account gets emptied overnight.
Under it

Built so the dangerous things are impossible.

campaigns
Nothing is created active. Campaigns, ad sets and ads are written PAUSED with no override; activating is a separate call that logs a warning.
budgets
Minor units, checked. A float raises TypeError — 50.0 means AED 50 to a person and AED 0.50 to Meta.
budget rules
Deciding is separate from doing. Evaluation is a pure function; applying defaults to a dry run, needs a minimum sample before forming an opinion, and moves a budget by one bounded step at most.
conversions
Personal data is SHA-256'd after normalising, with no flag to skip it, and every event carries an id so the pixel and the server deduplicate. Without that, cost per lead reads at half its real value.
retries
Selective. Meta answers a rate limit and your own malformed request with the same HTTP 400, so only the documented transient codes are retried.
stack
Python 3.11 · FastAPI · Claude and OpenAI behind one layer · WhatsApp Cloud API · Meta Marketing & Conversions API · PostgreSQL · Docker