Contacts and CRM
Resolving a recipient
Use find-contact before asking the user for someone's email address, and
before guessing a pattern like firstinitiallastname@company.com.
find-contact is backed by loadContactsForEmail, which merges Google People
API "connections" (boosted count += 5) and "other contacts" with real
send/receive history. Because it sees actual interaction history, it is strictly
better than guessing an address pattern.
find-contact --query="<name or partial email>" --limit=5 matches
case-insensitively against name and email, splitting the query into
whitespace-separated terms that must ALL match ("jacqueline lamb" requires
both terms present).
- Results are sorted by
count, an interaction-frequency score. Google People
API "connections" (explicitly saved contacts) start at count: 5; "other
contacts" (people the user has emailed but not saved) start lower. Sending
or receiving mail increments a real sendCount/receiveCount in the
contact_frequency SQL table, so the top match is usually the person the
user actually means.
- If
find-contact returns zero matches, tell the user — do not invent an
email address. A wrong guess either bounces or, worse, silently reaches the
wrong inbox.
- Results are cached per-owner for a few minutes (
contactCache in
server/handlers/emails.ts). A contact added in Google Contacts moments ago
may not appear immediately; that's expected, not a bug to route around.
CRM enrichment
get-hubspot-contact --email=<address> is the only first-class CRM action.
It returns contact fields (name, phone, company, title, lifecycle stage,
lead status) plus up to 5 associated deals and up to 5 associated tickets,
reading the user's own HubSpot API key configured in Settings. If no key is
configured it returns { error: "HubSpot API key not configured" } —
surface that plainly rather than treating it as "no CRM data exists."
- Gong, Pylon, and Apollo are UI-only in Mail. They have server route
handlers (
server/handlers/gong.ts, pylon.ts, apollo.ts) that power the
CRM sidebar panel in the email view, each reading its own per-session API
key from appStateGet. None of the three is registered in Mail's
MAIL_PROVIDER_API_IDS (see listProviderApiIdsForTemplateUse("mail") in
server/lib/provider-api.ts, which currently resolves to gmail,
google_calendar, and hubspot only). This means:
- There is no agent action for Gong calls, Pylon tickets, or Apollo person
lookups in Mail.
provider-api-request will refuse provider: "gong", "pylon", or
"apollo" in this app even though those provider ids exist in the shared
catalog — they're enabled for other templates (e.g. Analytics), not Mail.
- If the user asks the agent to pull a Gong call or Pylon ticket from Mail,
say plainly that integration is visible in the UI sidebar only today; do
not fabricate a result or imply
provider-api-request can reach it.
- Provider API-key connections are Settings-UI only. Gong, Pylon, Apollo,
and HubSpot keys are saved through raw
/api/* routes from the Settings UI —
there are no save-*-key actions. The agent can read HubSpot data via
get-hubspot-contact but cannot configure any of these connections on the
user's behalf; ask the user to add the key in Settings.
- For HubSpot data outside contact lookup (deals search, ticket creation,
property metadata), use
provider-api-catalog / provider-api-docs with
provider: "hubspot", then provider-api-request — get-hubspot-contact
only covers the single "lookup by email" shape.
Related Skills
email-drafts — use resolved contacts and CRM context when composing.
draft-queue — resolving the right ownerEmail teammate uses
list-org-members, a separate lookup from find-contact.
actions — the shared provider API pattern for provider-api-request.
1---2name: contacts-and-crm3description: Resolve a name or partial address to a real email and enrich a thread with CRM/contact context (HubSpot, Apollo) before drafting or triaging. Use when the user names a person instead of an email, or asks "who is this" / "what deals/tickets does this contact have".4---56# Contacts and CRM78## Resolving a recipient910Use `find-contact` before asking the user for someone's email address, and11before guessing a pattern like `firstinitiallastname@company.com`.1213`find-contact` is backed by `loadContactsForEmail`, which merges Google People14API "connections" (boosted `count += 5`) and "other contacts" with real15send/receive history. Because it sees actual interaction history, it is strictly16better than guessing an address pattern.1718- `find-contact --query="<name or partial email>" --limit=5` matches19 case-insensitively against name and email, splitting the query into20 whitespace-separated terms that must ALL match (`"jacqueline lamb"` requires21 both terms present).22- Results are sorted by `count`, an interaction-frequency score. Google People23 API "connections" (explicitly saved contacts) start at `count: 5`; "other24 contacts" (people the user has emailed but not saved) start lower. Sending25 or receiving mail increments a real `sendCount`/`receiveCount` in the26 `contact_frequency` SQL table, so the top match is usually the person the27 user actually means.28- If `find-contact` returns zero matches, tell the user — do not invent an29 email address. A wrong guess either bounces or, worse, silently reaches the30 wrong inbox.31- Results are cached per-owner for a few minutes (`contactCache` in32 `server/handlers/emails.ts`). A contact added in Google Contacts moments ago33 may not appear immediately; that's expected, not a bug to route around.3435## CRM enrichment3637- `get-hubspot-contact --email=<address>` is the only first-class CRM action.38 It returns contact fields (name, phone, company, title, lifecycle stage,39 lead status) plus up to 5 associated deals and up to 5 associated tickets,40 reading the user's own HubSpot API key configured in Settings. If no key is41 configured it returns `{ error: "HubSpot API key not configured" }` —42 surface that plainly rather than treating it as "no CRM data exists."43- **Gong, Pylon, and Apollo are UI-only in Mail.** They have server route44 handlers (`server/handlers/gong.ts`, `pylon.ts`, `apollo.ts`) that power the45 CRM sidebar panel in the email view, each reading its own per-session API46 key from `appStateGet`. None of the three is registered in Mail's47 `MAIL_PROVIDER_API_IDS` (see `listProviderApiIdsForTemplateUse("mail")` in48 `server/lib/provider-api.ts`, which currently resolves to `gmail`,49 `google_calendar`, and `hubspot` only). This means:50 - There is no agent action for Gong calls, Pylon tickets, or Apollo person51 lookups in Mail.52 - `provider-api-request` will refuse `provider: "gong"`, `"pylon"`, or53 `"apollo"` in this app even though those provider ids exist in the shared54 catalog — they're enabled for other templates (e.g. Analytics), not Mail.55 - If the user asks the agent to pull a Gong call or Pylon ticket from Mail,56 say plainly that integration is visible in the UI sidebar only today; do57 not fabricate a result or imply `provider-api-request` can reach it.58- **Provider API-key connections are Settings-UI only.** Gong, Pylon, Apollo,59 and HubSpot keys are saved through raw `/api/*` routes from the Settings UI —60 there are no `save-*-key` actions. The agent can read HubSpot data via61 `get-hubspot-contact` but cannot configure any of these connections on the62 user's behalf; ask the user to add the key in Settings.63- For HubSpot data outside contact lookup (deals search, ticket creation,64 property metadata), use `provider-api-catalog` / `provider-api-docs` with65 `provider: "hubspot"`, then `provider-api-request` — `get-hubspot-contact`66 only covers the single "lookup by email" shape.6768## Related Skills6970- `email-drafts` — use resolved contacts and CRM context when composing.71- `draft-queue` — resolving the right `ownerEmail` teammate uses72 `list-org-members`, a separate lookup from `find-contact`.73- `actions` — the shared provider API pattern for `provider-api-request`.