contactLookupLook up contact
Resolves the caller's phone number against your address book and hydrates {{contact.*}} for the rest of the call — display name, company, email, tags, not…
type: contactLookup status: stable sinceVersion: 0.1.0 seeAlso: [integration, httpCall, customScript] keywords: ["contactLookup", "look", "contact", "integrations"]
What it does
Resolves a phone number against your org's address book and hydrates
vars._contact for the rest of the call — display name, company, email,
notes, and tags. Once the lookup succeeds, every downstream {{contact.x}}
template in TTS prompts, screenPop HTML/URLs, and integration
inputs resolves through that record without you needing to re-fetch.
The match is exact on E.164 against the contact's numbers JSONB array, so
a single contact with a mobile and a desk number resolves from either. By
default the node looks up {{event.from}} (the caller's number); override
with any template that resolves to E.164 if you'd rather match on a number
your flow already gathered (e.g. {{vars.gatheredAccountNumber}}). The
node always routes — it has no error path. A miss is a routing decision,
not a failure: the notFound branch fires, vars._contact is cleared so
stale data from an earlier lookup can't leak through, and the flow
continues.
When to use it
- Personalize a greeting ("Welcome back, {{contact.displayName}}") only when the caller is recognized
- Drive an
agentNotifyorscreenPopto surface contact context the moment an agent picks up - Branch to a different sub-flow for known vs. unknown callers
- Use the contact's tags (e.g. "vip", "flagged") to skip a menu and route directly to a queue
- Hydrate
{{contact.id}}so a downstreamscreenPopdeep-links straight into your CRM
Configuration
Resolves the caller's phone number against your address book and hydrates {{contact.*}} for the rest of the call — display name, company, email, tags, notes. Routes on "Found" or "Not found" so you can pop a personalized record for known contacts and a generic greeting for new ones. The lookup is exact-match on E.164.
| Field | Label | Type | Required | Default | Notes |
|---|---|---|---|---|---|
phoneField | Phone number to look up | text | Optional | {{event.from}} | Defaults to the caller's number. Override with any template that resolves to E.164 (e.g. {{vars.gatheredAccountNumber}}). |
Outgoing events: found, notFound
Examples
Caller-aware greeting
Look up the inbound number, then route to a personalized greeting on
found and a generic one on notFound.
{
"id": "lookup",
"type": "contactLookup",
"config": {
"phoneField": "{{event.from}}"
},
"on": {
"found": "say-known",
"notFound": "say-unknown"
}
}
Lookup on a gathered account number
Take a number the caller typed into a gatherDigits step and resolve
it against the address book instead of the calling number.
{
"id": "lookup-by-account",
"type": "contactLookup",
"config": {
"phoneField": "{{vars.accountNumber}}"
},
"on": {
"found": "agent-pop",
"notFound": "voicemail"
}
}
Gotchas
- Match is exact-E.164. The query uses a JSONB containment match
(
numbers @> '[{"e164":"..."}]'::jsonb). A contact stored as+12025550123won't be hit by a lookup of2025550123— same number, different string. The node normalizes the input before querying, but contacts with non-E.164 stored values won't resolve. notFoundis the routing default if no edge is wired. When neitherfoundnornotFoundhas an outgoing edge, the worker falls back to the first transition onnode.on. That makes a half-wired flow advance instead of stalling, but it can also send unknown callers down the "found" path if you only wired one edge.vars._contactis cleared on a miss. A previous lookup's record doesn't linger — everynotFoundbranch starts with empty contact vars. If you cache an earlier contact id elsewhere, save it on a separatevars.xfirst.- Tags are an array.
{{contact.tags}}renders the array; if you want to test for a specific tag, use abranchnode with acontainscase oncontact.tagsrather than a string-equals check. - Anonymous callers fall straight to
notFound. When the carrier doesn't passevent.from(private/withheld number), the normalized phone is empty and the lookup short-circuits before hitting the database. Wire thenotFoundbranch defensively. - No error path — only
found/notFound. Database errors are caught upstream and surface as anotFound(with a worker log entry). The flow never halts on a contact lookup, even if the query throws.
