Quick Start
1
Enable with a single agent
One number, one agent. Normalisation means the formatting you assign with does not have to match the inbound format.
2
Multiple agents, multiple numbers
Add a
default_agent so unknown numbers still get answered.3
Wire it into a WhatsApp bot
A real gateway calls
registry.resolve(inbound_number) on every message, then hands the chat to the chosen agent. See WhatsApp Bot for the full bot setup.How It Works
The gateway resolves the destination number to an agent, runs the chat, and replies on the same channel.
Concurrent dispatch. The gateway runs many inbound messages at once. Share a single
AgentRegistry across all of them โ assign and resolve are protected by an internal lock, so simultaneous turns can never see a half-updated routing table.
Number Normalisation
"+1 (415) 555-0123" and "+14155550123" resolve to the same agent because normalize_number strips every non-digit and preserves a single leading +. Blank, non-string, or digit-free inputs normalise to None.
API Reference
AgentRegistry(default_agent=None)
In-memory phone-number โ agent routing table. No I/O, no dependencies beyond the stdlib. Thread-safe: every method is guarded by a per-instance threading.Lock, and __iter__ returns a stable snapshot taken under the lock.
Methods
normalize_number(number)
normalize_number(number: Optional[str]) -> Optional[str] returns the canonical key, or None when blank / non-string / no digits.
- Strips whitespace and formatting characters (spaces, dashes, parens, dots).
- Preserves a single leading
+.
AgentRegistry vs ChannelDirectory
Pick by message direction:AgentRegistry routes inbound by phone number; ChannelDirectory routes outbound by target name.
Common Patterns
Fallback agent โ set adefault_agent so unknown numbers are never left unanswered.
Best Practices
Prefer E.164 numbers
Prefer E.164 numbers
Store numbers as
+<country><digits> (E.164). Normalisation still handles human-typed variants like "+1 (415) 555-0123", but E.164 is unambiguous and matches what most webhooks send.Build one registry per process
Build one registry per process
The registry holds references to already-constructed agents and does no I/O. Create it once at startup and consult it on every inbound message.
Always set a default_agent
Always set a default_agent
Set
AgentRegistry(default_agent=triage) if you never want to leave an inbound message unanswered. Without it, unknown numbers resolve to None and the caller must handle that case.AgentRegistry is thread-safe
AgentRegistry is thread-safe
Every method is guarded by a per-instance
threading.Lock, so you can call assign, unassign, and resolve from concurrent inbound turns without adding your own lock. Iterating with for key, agent in registry or list(registry) returns a stable snapshot taken under the lock โ a concurrent mutation can never raise โdictionary changed size during iterationโ.Related
WhatsApp Bot
Connect an agent to WhatsApp โ Cloud API or Web mode.
Channels Gateway
Route agents across Telegram, Discord, Slack, and WhatsApp.
Messaging Bots
All supported messaging platforms in one place.
Platform-Aware Agents
Outbound routing with ChannelDirectory and ReachableTarget.

