Skip to content

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 upSent to ZoomInfoAdded to the entitySignal
An Organization with a domain or websitePOST /enrich/company with the domain and the entity's namezoominfo_* firmographic properties (below)company
A Person with an email alias who is not a Corveil userPOST /enrich/contact with the email onlyzoominfo_* job properties, plus Person MEMBER_OF Organization when the contact's ZoomInfo company is one of your Organizationscontact

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:

PropertyFrom ZoomInfo
zoominfo_company_id, zoominfo_urlCompany id, and a link to its ZoomInfo profile
zoominfo_name, zoominfo_websiteName and website as ZoomInfo has them
zoominfo_revenue_usd, zoominfo_revenue_rangeRevenue (ZoomInfo reports thousands of USD; stored in USD) and its band
zoominfo_employee_count, zoominfo_employee_rangeHeadcount and its band
zoominfo_industry, zoominfo_industriesPrimary industry and the full list
zoominfo_founded_year, zoominfo_ticker, zoominfo_hq_locationFounding 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:

json
{
  "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.

bash
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 ​

KeyRequiredNotes
enrichnocompanies, contacts, or both, comma-separated. Blank means both.
refresh_daysnoLook an entity up again after this many days. 7–365, default 90.
max_per_pollnoThe most companies, and separately the most contacts, looked up in one poll. 1–1000, default 100. This caps credit use per poll.
skip_domainsnoComma-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.

  1. Sign in to the ZoomInfo Admin Portal as an admin.
  2. Open Integrations → API (on some editions, DevPortal). Create an API app or client for Corveil.
  3. Choose PKI (private key) authentication. Copy the Client ID, and download or copy the private key. ZoomInfo shows the private key once.
  4. Note the username of the ZoomInfo user the client belongs to. That user's API entitlements and credits are what the sensor uses.
  5. 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.
  6. Store username, client_id, and private_key as one zoominfo_pki credential in Corveil.

There's no admin consent or webhook to configure. No separate runbook is needed.

Wire-up ​

POST /api/sensors

json
{
  "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 than refresh_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_OF edge.
  • A match replaces the ZoomInfo view; a miss does not erase it. On a match, zoominfo_* keys ZoomInfo no longer reports are removed. A no_match on 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 429 waits for Retry-After (up to 60 s) and retries up to 3 times. When ZoomInfo answers LIMIT_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 → Organization or contact → Person binding on the sensor drops that half's enrichments. Turning off contact → Person also drops the MEMBER_OF edges 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, and skip_domains to bound spend.
  • Only entities with a usable key are looked up. Organizations need a domain or website property, 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.