This package provides ZoomInfo integration for Gadgets via ZoomInfo's GTM (go-to-market) API, using OAuth2 (Authorization Code + PKCE).
It exposes a single whole-account resource (https://app.zoominfo.com/): a session scoped to one
connected ZoomInfo account's OAuth grant and package entitlements. Through it a gadget can:
- Lookup — resolve controlled filter values (industries, intent topics, scoop types, …) and discover the enrich fields the account is entitled to.
- Search (free) — companies, contacts, intent signals, scoops, and news, with full firmographic and technographic filter fidelity.
- Enrich (consumes credits) — turn matched records into full detail: companies, contacts, corporate hierarchy, hashtags, intent, scoops, and news.
- Copilot — company/contact lookalikes, contact recommendations, AI account summaries (+ ask), and curated company insight signals.
- Usage — read the account's credit/limit counters.
A connected account corresponds to the ZoomInfo user who authorizes the OAuth consent flow. ZoomInfo
is not offered as a "Continue with…" sign-in method (getAuthenticatedEmail() returns null).
-
In the ZoomInfo Developer Portal, create an OAuth application.
-
Set the Redirect URI to your deployment's callback (replace the host with your base URL):
${BASE_URL}/gatekeeper/zoominfo/oauthFor local development that is
http://localhost:8787/gatekeeper/zoominfo/oauth. -
Enable all API scopes the account is entitled to. The gatekeeper requests exactly the scopes ZoomInfo's authorization server recognizes:
api:data:company api:data:contact api:data:intent api:data:news api:data:scoops api:recommendations:read api:account-summary:read api:insights:readThere is no
lookupscope — lookup/enrich-field reference data comes with the data scopes. -
Copy the Client ID and Client Secret.
Use the OAuth app's Client ID + Client Secret. The
authorization_codeexchange posts them to the token endpoint as HTTP Basic auth (form-urlencoded), with PKCE (S256) and refresh-token rotation.
The gatekeeper Worker reads CLIENT_ID and CLIENT_SECRET. In local development these are seeded
from the root .dev.vars by run-dev-server.js:
ZOOMINFO_CLIENT_ID=<oauth app client id>
ZOOMINFO_CLIENT_SECRET=<oauth app client secret>
Optional overrides read by the Worker:
BASE_URL— the gatekeeper's public base (defaulthttp://localhost:8787/gatekeeper/zoominfo); the OAuth redirect is${BASE_URL}/oauth.ZOOMINFO_API_BASE_URL— the ZoomInfo API root (defaulthttps://api.zoominfo.com/gtm).
- Credits. Search, lookup, recommendations, insights, and account summaries are free. Enrichment
consumes ZoomInfo credits (roughly one per newly-enriched record; already-owned records and
no-match/error results are free).
getCreditUsage()is the authoritative source for accounting — the per-recordcreditChargedflag is a display-only upper bound, because ZoomInfo often omits management status from enrich responses and so over-reports records served for free. - Approvals. Reads are logged as observations. Each
enrich*call is submitted to the approval queue and spends credits only once a human approves it; results are fetched afterward viagetEnrichmentResult(ticket). Enrichments are not simulated — the action carriesawaitDecision, so the agent turn suspends until the decision rather than reading back un-enriched state. - Intent/scoop company targeting. Intent and scoop search filter by firmographics only —
ZoomInfo has no company-identity inputs there. To pull intent or scoops for a specific known
company, use
enrichIntent/enrichScoops(which take a company identifier). The API surface and a runtime guard both enforce this with a clear message instead of ZoomInfo's opaque 400. statevscountry. These are mutually exclusive: ZoomInfo silently ignoresstatewhencountryis also set and returns the whole country. The gatekeeper rejects the combination up front rather than returning broadened results with no signal.statealready scopes to the US/Canada, so pass just one.- Entitlements. Every call is scoped to the account's package: ZoomInfo returns only the fields and datasets the account is entitled to; unentitled fields come back empty.
CLIENT_ID or CLIENT_SECRET is missing. Ensure ZOOMINFO_CLIENT_ID / ZOOMINFO_CLIENT_SECRET
are set in the root .dev.vars, then restart the dev server.
A requested scope isn't recognized by ZoomInfo's authorization server. Confirm the app has the eight scopes listed above enabled and that no others are being requested.
The redirect URI sent must exactly match one registered on the app. Confirm the portal has
${BASE_URL}/gatekeeper/zoominfo/oauth (for local dev,
http://localhost:8787/gatekeeper/zoominfo/oauth, no trailing slash).
The OAuth app isn't issuing refresh tokens. Enable the refresh-token grant for the app in the Developer Portal and reconnect — the gatekeeper needs a refresh token to keep the long-lived session alive after the access token expires.