ZoomInfo Sensor
What it ingests
ZoomInfo is an enrichment source. It never lists or bulk-imports ZoomInfo's database. Each poll takes Organizations and People that your other sensors (HubSpot, Salesforce, shell-crm, Intercom, Gmail, …) already put in the ontology, looks them up in the ZoomInfo Enterprise API, and adds ZoomInfo's answer to the existing entity:
| Looked up | Sent to ZoomInfo | Added to the entity | Signal |
|---|---|---|---|
An Organization with a domain or website | POST /enrich/company with the domain and the entity's name | zoominfo_* firmographic properties (below) | company |
A Person with an email alias who is not a Corveil user | POST /enrich/contact with the email only | zoominfo_* job properties, plus Person MEMBER_OF Organization when the contact's ZoomInfo company is one of your Organizations | contact |
No new entities are created. A contact whose employer is not already an Organization in your ontology gets job properties but no edge. The CRM sensor that created an entity still owns its other properties. ZoomInfo writes only the zoominfo_* keys, and a later CRM sync keeps them.
Properties written on an Organization:
| Property | From ZoomInfo |
|---|---|
zoominfo_company_id, zoominfo_url | Company id, and a link to its ZoomInfo profile |
zoominfo_name, zoominfo_website | Name and website as ZoomInfo has them |
zoominfo_revenue_usd, zoominfo_revenue_range | Revenue (ZoomInfo reports thousands of USD; stored in USD) and its band |
zoominfo_employee_count, zoominfo_employee_range | Headcount and its band |
zoominfo_industry, zoominfo_industries | Primary industry and the full list |
zoominfo_founded_year, zoominfo_ticker, zoominfo_hq_location | Founding year, ticker, and "city, state, country" |
Properties written on a Person: zoominfo_contact_id, zoominfo_url, zoominfo_job_title, zoominfo_job_function, zoominfo_management_level, zoominfo_company_id, zoominfo_company_name, zoominfo_location, zoominfo_accuracy_score, zoominfo_last_updated.
Every looked-up entity also gets zoominfo_match_status (matched or no_match), zoominfo_match_input (the domain or email that was sent), and zoominfo_enriched_at (when it was looked up).
The sensor does not request phone numbers or email addresses from ZoomInfo. It describes people the org already knows; it does not harvest contact details.
Credential
References a zoominfo_pki credential by id. ZoomInfo's Enterprise API uses PKI authentication: the sensor signs a short-lived RS256 client JWT with your API client's private key, exchanges it at https://api.zoominfo.com/authenticate for an access token (valid 60 minutes), and sends that token on each enrich call.
The secret is a JSON object:
{
"username": "api.user@yourcompany.com",
"client_id": "<client id from the ZoomInfo Admin Portal>",
"private_key": "-----BEGIN PRIVATE KEY-----\n…\n-----END PRIVATE KEY-----"
}private_key is the PEM RSA key (PKCS#8 BEGIN PRIVATE KEY or PKCS#1 BEGIN RSA PRIVATE KEY). The vault refuses a blob with a password. The sensor never uses username and password authentication.
curl -sS -X POST https://your-corveil-host/api/credentials \
-H "Authorization: Bearer <admin>" -H "Content-Type: application/json" \
-d '{"type":"zoominfo_pki","name":"zoominfo","secret":"{\"username\":\"api.user@yourcompany.com\",\"client_id\":\"…\",\"private_key\":\"-----BEGIN PRIVATE KEY-----\\n…\"}"}'Config fields
| Key | Required | Notes |
|---|---|---|
enrich | no | companies, contacts, or both, comma-separated. Blank means both. |
refresh_days | no | Look an entity up again after this many days. 7–365, default 90. |
max_per_poll | no | The most companies, and separately the most contacts, looked up in one poll. 1–1000, default 100. This caps credit use per poll. |
skip_domains | no | Comma-separated domains that are never sent to ZoomInfo, such as your own company's. Consumer mail domains (gmail.com, outlook.com, yahoo.com, icloud.com, …) are always skipped. |
There is no default_entity_type. The sensor only adds properties to existing Organization and Person entities.
Provider-side setup
You need a ZoomInfo plan that includes Enterprise API access with the Enrich endpoints, and an admin who can create API credentials. Menu names vary by ZoomInfo edition.
- Sign in to the ZoomInfo Admin Portal as an admin.
- Open Integrations → API (on some editions, DevPortal). Create an API app or client for Corveil.
- Choose PKI (private key) authentication. Copy the Client ID, and download or copy the private key. ZoomInfo shows the private key once.
- Note the username of the ZoomInfo user the client belongs to. That user's API entitlements and credits are what the sensor uses.
- Confirm the user has access to Company Enrich and Contact Enrich, and to the output fields listed above. If a field is not in your entitlement, ZoomInfo rejects the whole request. The error shows on the sensor.
- Store
username,client_id, andprivate_keyas onezoominfo_pkicredential in Corveil.
There's no admin consent or webhook to configure. No separate runbook is needed.
Wire-up
POST /api/sensors
{
"sensor_type": "zoominfo",
"name": "ZoomInfo enrichment",
"config": {
"enrich": "companies, contacts",
"refresh_days": "90",
"max_per_poll": "100",
"skip_domains": "yourcompany.com"
},
"credentials": { "api_client": "<zoominfo_pki credential id>" }
}Verify
Run Test connection. It only authenticates: it mints an access token from the client JWT and makes no enrich call, so testing never spends credits. A bad username, client id, or key fails here.
After the first poll, open an Organization from your CRM in the ontology. Its properties include zoominfo_match_status and zoominfo_enriched_at, and the ZoomInfo fields when it matched. A matched contact at one of your Organizations shows a MEMBER_OF edge to it.
How incremental sync works
- The ontology is the work queue. Each poll reads, through the org-scoped database handle, the Organizations and People with no
zoominfo_enriched_at, or one older thanrefresh_days. Never-looked-up entities come first, then the oldest. A second poll with nothing new sends nothing to ZoomInfo. - The stamp lands when the event is mapped, a few minutes after the poll. Until then, the sensor's cursor holds each looked-up entity, for up to 7 days, so a poll never sends it twice. An entity whose stamp never lands (because it was deleted, say) is tried again after that week.
- Ineligible entities are held too. An Organization whose website is not a domain, or whose domain is a consumer or skipped domain, or a Person with only a consumer email, is noted in the cursor and skipped for a week instead of being read on every poll.
- Batches of 25. ZoomInfo accepts up to 25 match inputs per request. Requests are paced 250 ms apart. Companies are looked up before contacts, so a contact at a company matched in the same poll still gets its
MEMBER_OFedge. - A match replaces the ZoomInfo view; a miss does not erase it. On a match,
zoominfo_*keys ZoomInfo no longer reports are removed. Ano_matchon a later refresh updates only the status and timestamp, so a transient miss does not wipe an earlier match. - Each lookup is its own event (
zoominfo:<company|contact>:<entity id>@<unix time>), so a refresh is recorded rather than deduplicated away. The entity keeps its own id. - Rate limits and credits. A
429waits forRetry-After(up to 60 s) and retries up to 3 times. When ZoomInfo answersLIMIT_EXCEEDED, the poll stops, keeps the lookups it already made, and skips ZoomInfo for 24 hours. The Sensors tab shows why. Any other error after some lookups have completed also keeps those lookups, and the reason shows on the sensor. - Your bindings decide what is written. Turning off the
company → Organizationorcontact → Personbinding on the sensor drops that half's enrichments. Turning offcontact → Personalso drops theMEMBER_OFedges those contacts would add.
Known limits
- Credits. Each lookup of a record can use ZoomInfo credits under your contract. Whether re-looking up a record within your contract period costs again depends on your ZoomInfo agreement. Use
max_per_poll,refresh_days, andskip_domainsto bound spend. - Only entities with a usable key are looked up. Organizations need a
domainorwebsiteproperty, so most come from CRM sensors. An Organization only mentioned in a document or meeting has neither and is not sent. People need an email alias and must not be Corveil users. - Contacts are matched by email alone. Names are not sent, so a person ZoomInfo knows only under a different address does not match.
- Contact details are out of scope. Phone numbers, emails, intent, scoops, and technographics are not requested.
- No bulk import or search. The sensor does not discover new companies or people from ZoomInfo. For lead generation, use ZoomInfo's own tools.
- Username and password authentication is not supported. Use a PKI API client.