Skip to main content
Vorel’s chat agent handles inbound WhatsApp conversations end-to-end. Same router → sub-agent shape as voice; same per-tenant persona, vertical pack, and guardrails. Customer identity binds across voice + chat by phone number.

What chat does today

WhatsApp inbound

Customers message your business’s WhatsApp number. Our WhatsApp provider forwards the message to our webhook receiver; the worker queues it; the agent replies through the same dispatch path voice uses.

Cross-channel context

A customer who called and now WhatsApps continues the same conversation. customers.phone is the identity anchor; conversations are looked up by (tenant, channel, customer_identifier) with phone-number normalization across wa_id and E.164 forms.

Bilingual (Arabic + English)

Same prompt-driven language detection as voice. The agent responds in the language the customer used on the latest turn.

Operator inbox reply

Your team can take over a thread from app.vorel.ai/inbox; the agent stops auto-replying once a human reply is sent on that conversation. Re-enable auto-reply via the inbox toggle.

Webhook events

Subscribe to lead.created, lead.qualified, conversation.created, conversation.handoff_requested, conversation.closed: fire-on-state-change for your own integrations. Bodies are HMAC-SHA256 signed. Six-attempt retry ladder.

Dashboard send

The dashboard reply path uses the same send_whatsapp_message tool; your operator’s reply is persisted into messages and lands in the conversation transcript.

What’s paused

WhatsApp outbound network send is being finalized as Meta Business Manager verification clears per tenant. The send_whatsapp_message tool persists the agent reply to the conversation transcript and writes the outbox row; the network send to your WhatsApp provider activates per-tenant once verification is through. Inbound messages flow normally; the dashboard inbox shows everything.
In the meantime, a customer’s message lands in your inbox and an agent reply is generated and persisted, and your team can take over the conversation manually from the inbox.

How a chat turn flows

Customer WhatsApp

WhatsApp provider

POST /api/webhooks/whatsapp  (HMAC-verified, IP rate-limited 500/min)

BullMQ → message-processor queue

worker:
  1. Persist customer turn into `messages` (with hallucination_flags=null)
  2. Upsert `customers` row by phone-number
  3. Customer-number rate limit (30 events/min per (tenant, phone))
  4. POST /api/internal/agent/dispatch  (signed worker JWT, 2-min TTL)

runChatDispatch:
  1. Hydrate context (history + customer + persona + vertical + pack overrides)
  2. classifyIntent → one of ~10 intent slugs
  3. Sub-agent (qualification / FAQ / booking / handoff) with its tool whitelist
  4. send_whatsapp_message terminal: persist agent_bot turn + grade hallucinations + outbox
Voice and chat run the same runChatDispatch / runVoiceDispatch shape internally; only the ingress and the terminal differ. See How it works for the full picture.

Tool whitelist by sub-agent (chat)

Sub-agentTools
qualificationsearch_offerings · update_lead · crm_lookup_customer · crm_update_record · request_handoff
bookingcheck_availability · book_appointment · update_lead · request_handoff
handoffrequest_handoff
faqget_faq_answer · search_offerings · crm_lookup_customer · request_handoff
Per-tenant tool whitelists trim this further: your operator can disable crm_update_record for a tenant whose CRM is read-only, etc.

Channel-aware reply formatting

Chat replies use a different output style than voice. The channel-rules block appended to every chat sub-agent prompt allows:
  • Up to four sentences per turn (vs. voice’s two-sentence cap).
  • Bullet / numbered lists when listing options or steps.
  • Numerals are readable (AED 1,200,000, no need to spell out).
  • Emojis sparingly OK for warmth (one per message at most; never as decoration).
  • Links are fine when relevant; the customer can tap them.
These rules are appended to every sub-agent prompt by a channel-rules block; the prompts themselves are channel-neutral.

Anti-spam posture

Inbound abuse is gated at three layers:
  1. Per-IP webhook limit (500 req/min): applied before signature verification, so a flood from a single IP doesn’t compromise the verification path’s cost.
  2. HMAC signature verification: the WhatsApp provider signs every webhook with a shared secret; we reject on signature mismatch with a 401 and a webhook.signature_invalid log line.
  3. Per-(tenant, customer phone) limit (30 events/min): applied after parse, so a single compromised customer number can’t burn through the tenant’s quota or generate bot work.
Sustained 30/min from a single number is suspicious; the agent path goes silent and the operator inbox surfaces a customer_rate_limited flag.

Webhooks emitted on chat events

Subscribe at /(dashboard)/settings/integrations/webhooks. Chat-relevant events:
EventWhen
conversation.createdFirst inbound from a new (tenant, customer) pair
lead.createdQualification sub-agent created the lead
lead.updatedSlot changes via update_lead
lead.qualifiedLead status moved off 'new'
conversation.handoff_requestedHandoff sub-agent fired or request_handoff tool ran
conversation.closedInbox closed by an operator action
Webhook bodies carry {id, event, created_at, tenant_id, data}; id doubles as the consumer’s idempotency key. HMAC-SHA256 signature in X-Webhook-Signature. Retry ladder: [60s, 300s, 1800s, 7200s, 43200s] with a 6-attempt cap; failures past 6 attempts raise a tenant-admin alert and land in webhook_deliveries.status='dead_letter'.

What’s NOT supported on chat today

  • Outbound network send to WhatsApp: paused per the note above.
  • Outbound proactive messaging (campaigns): depends on Phase 4b; even after that lands, outbound campaigns are scoped to opted-in customers per Meta policy.
  • Other messaging channels (Telegram, Instagram DM, SMS): WhatsApp only today.

Per-tenant configuration that matters for chat

Set via the operator console, no code deploy:
  • Persona: agent name, tone, signature phrases, forbidden phrases (Arabic + English).
  • Working hours + holidays: rejects outside-hours inbound at the agent layer with a per-vertical “we’re back at” message; falls through to a human if your handoff rules say so.
  • Vertical pack: drives the qualification slots, FAQ redirect copy, handoff triggers (see Verticals).
  • Tool whitelist: disable crm_update_record (read-only CRM), book_appointment (no online booking yet), etc.
  • Guardrails: hallucination threshold + action; forbidden-phrase action. See Guardrails.