@absolutejs/engagement-apollo 0.0.11 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -32,3 +32,21 @@ Company enrichment returns `null` for no match (404), throws `RateLimitError` fo
32
32
  ## License
33
33
 
34
34
  Apache-2.0.
35
+
36
+ ### Company research
37
+
38
+ `searchCompanies({ domain?, name?, limit?, signal? })` reads one bounded page
39
+ (default 5, maximum 100) without contact reveals or implicit enrichment. Apollo
40
+ charges one credit per page; meter each successful call. Errors are explicit,
41
+ including scope/authentication failures, so an unavailable search is not “no data.”
42
+ `enrichCompany` also accepts `website` and `linkedinUrl`; requests have a 30-second
43
+ timeout and accept cancellation.
44
+
45
+ Company `intelligence` retains provider-reported ownership, subsidiaries, retail
46
+ locations, industries, technologies, funding, source URL and observation time.
47
+ Missing fields remain null/empty, never inferred. Funding is not revenue, locations
48
+ are not customers, and organization revenue is not necessarily subscription ARR.
49
+ Apollo does not establish a vendor's pricing cadence or comparable business scope.
50
+ Use `companyIdentityMatch` / `selectCompanyIdentity` from the engagement contract
51
+ to require an independently returned exact domain and a compatible brand name;
52
+ ambiguous provider records must not be merged based on revenue size.
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import type { EngagementSource } from "@absolutejs/engagement";
2
2
  export type ApolloOptions = {
3
- apiKey: string;
4
- /** Override for testing or a proxy. Defaults to https://api.apollo.io. */
5
- baseUrl?: string;
6
- /** Inject a fetch (tests, instrumentation). Defaults to global fetch. */
7
- fetchImpl?: typeof fetch;
3
+ apiKey: string;
4
+ /** Override for testing or a proxy. Defaults to https://api.apollo.io. */
5
+ baseUrl?: string;
6
+ /** Inject a fetch (tests, instrumentation). Defaults to global fetch. */
7
+ fetchImpl?: typeof fetch;
8
8
  };
9
9
  export declare const apolloSource: (options: ApolloOptions) => EngagementSource;
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ var APOLLO_BASE_URL = "https://api.apollo.io";
8
8
  var DEFAULT_ACTIVITY_LIMIT = 50;
9
9
  var DEFAULT_SEARCH_LIMIT = 6;
10
10
  var LOCKED_EMAIL = /email_not_unlocked/iu;
11
- var isRecord = (value) => typeof value === "object" && value !== null;
11
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
12
12
  var str = (value) => typeof value === "string" && value.length > 0 ? value : null;
13
13
  var num = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
14
14
  var companyDomain = (org) => {
@@ -29,6 +29,26 @@ var joinLocation = (parts) => {
29
29
  const present = parts.filter((part) => part !== null);
30
30
  return present.length > 0 ? present.join(", ") : null;
31
31
  };
32
+ var strings = (value) => Array.isArray(value) ? value.flatMap((item) => typeof item === "string" && item.trim() ? [item.trim()] : []) : [];
33
+ var count = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
34
+ var publicUrl = (value) => {
35
+ if (typeof value !== "string")
36
+ return null;
37
+ try {
38
+ const url = new URL(value);
39
+ return ["https:", "http:"].includes(url.protocol) && !url.username && !url.password ? url.href : null;
40
+ } catch {
41
+ return null;
42
+ }
43
+ };
44
+ var reference = (value) => isRecord(value) ? {
45
+ providerId: str(value.id),
46
+ name: str(value.name),
47
+ domain: companyDomain(value),
48
+ country: str(value.country),
49
+ employeeCount: count(value.estimated_num_employees),
50
+ industries: strings(value.industries)
51
+ } : null;
32
52
  var apolloSource = (options) => {
33
53
  const baseUrl = options.baseUrl ?? APOLLO_BASE_URL;
34
54
  const doFetch = options.fetchImpl ?? fetch;
@@ -49,19 +69,6 @@ var apolloSource = (options) => {
49
69
  const json = await res.json();
50
70
  return isRecord(json) ? json : null;
51
71
  };
52
- const get = async (path) => {
53
- const res = await doFetch(`${baseUrl}${path}`, { headers, method: "GET" });
54
- if (res.status === 429)
55
- throw new RateLimitError("apollo");
56
- if (res.status === 404)
57
- return null;
58
- if (!res.ok)
59
- throw new EngagementProviderError("apollo", res.status, "company enrichment");
60
- const json = await res.json();
61
- if (!isRecord(json))
62
- throw new EngagementProviderError("apollo", 502, "invalid company response");
63
- return json;
64
- };
65
72
  const normalizePerson = (person) => {
66
73
  const org = isRecord(person.organization) ? person.organization : null;
67
74
  const contact = isRecord(person.contact) ? person.contact : null;
@@ -130,16 +137,37 @@ var apolloSource = (options) => {
130
137
  searchRequests
131
138
  };
132
139
  };
133
- const enrichCompany = async (query) => {
134
- if (!query.domain)
135
- return null;
136
- const json = await get(`/v1/organizations/enrich?domain=${encodeURIComponent(query.domain)}`);
137
- const org = json && isRecord(json.organization) ? json.organization : null;
138
- if (!org)
139
- return null;
140
+ const normalizeCompany = (org) => {
140
141
  const revenue = num(org.annual_revenue);
141
142
  const revenueDisplay = str(org.annual_revenue_printed);
142
143
  return {
144
+ intelligence: {
145
+ provider: "apollo",
146
+ observedAt: new Date().toISOString(),
147
+ sourceUrl: /^[a-f0-9]{24}$/iu.test(str(org.id) ?? "") ? `https://app.apollo.io/#/organizations/${org.id}` : null,
148
+ websiteUrl: publicUrl(org.website_url),
149
+ location: joinLocation([
150
+ str(org.city),
151
+ str(org.state),
152
+ str(org.country)
153
+ ]),
154
+ industries: strings(org.industries),
155
+ keywords: strings(org.keywords),
156
+ technologies: strings(org.technology_names),
157
+ retailLocationCount: count(org.retail_location_count),
158
+ activeRetailLocationCount: count(org.num_retail_locations),
159
+ parent: reference(org.owned_by_organization),
160
+ ultimateParent: reference(org.ultimate_parent_organization),
161
+ parentIds: strings(org.owned_by_chain),
162
+ subsidiaries: Array.isArray(org.suborganizations) ? org.suborganizations.flatMap((value) => {
163
+ const item = reference(value);
164
+ return item ? [item] : [];
165
+ }) : [],
166
+ subsidiaryCount: count(org.num_suborganizations),
167
+ totalFunding: count(org.total_funding),
168
+ latestFundingStage: str(org.latest_funding_stage),
169
+ latestFundingDate: str(org.latest_funding_round_date)
170
+ },
143
171
  providerId: str(org.id),
144
172
  annualRevenue: revenue !== null && revenue > 0 || revenueDisplay ? {
145
173
  amount: revenue !== null && revenue > 0 ? revenue : null,
@@ -159,6 +187,64 @@ var apolloSource = (options) => {
159
187
  raw: org
160
188
  };
161
189
  };
190
+ const companyRequest = async (path, signal, body) => {
191
+ signal?.throwIfAborted();
192
+ const res = await doFetch(`${baseUrl}${path}`, {
193
+ headers,
194
+ method: body ? "POST" : "GET",
195
+ ...body ? { body: JSON.stringify(body) } : {},
196
+ signal: AbortSignal.any([
197
+ AbortSignal.timeout(30000),
198
+ ...signal ? [signal] : []
199
+ ])
200
+ });
201
+ if (res.status === 429)
202
+ throw new RateLimitError("apollo");
203
+ if (res.status === 404 && !body)
204
+ return null;
205
+ if (!res.ok)
206
+ throw new EngagementProviderError("apollo", res.status, body ? "company search" : "company enrichment");
207
+ const value = await res.json();
208
+ if (!isRecord(value))
209
+ throw new EngagementProviderError("apollo", 502, "invalid company response");
210
+ return value;
211
+ };
212
+ const enrichCompany = async (query) => {
213
+ if (!query.domain && !query.website && !query.linkedinUrl)
214
+ return null;
215
+ const params = new URLSearchParams;
216
+ if (query.domain)
217
+ params.set("domain", query.domain);
218
+ if (query.website)
219
+ params.set("website", query.website);
220
+ if (query.linkedinUrl)
221
+ params.set("linkedin_url", query.linkedinUrl);
222
+ if (query.name?.trim())
223
+ params.set("name", query.name.trim());
224
+ const json = await companyRequest(`/api/v1/organizations/enrich?${params}`, query.signal);
225
+ if (!json || json.organization === null)
226
+ return null;
227
+ if (!isRecord(json.organization))
228
+ throw new EngagementProviderError("apollo", 502, "invalid company enrichment response");
229
+ return normalizeCompany(json.organization);
230
+ };
231
+ const searchCompanies = async (query) => {
232
+ if (!query.domain && !query.name?.trim())
233
+ return { companies: [], searchRequests: 0 };
234
+ const limit = Number.isFinite(query.limit) ? Math.max(1, Math.min(100, Math.floor(query.limit))) : 5;
235
+ const json = await companyRequest("/api/v1/mixed_companies/search", query.signal, {
236
+ page: 1,
237
+ per_page: limit,
238
+ ...query.domain ? { q_organization_domains_list: [query.domain] } : {},
239
+ ...!query.domain && query.name ? { q_organization_name: query.name.trim() } : {}
240
+ });
241
+ if (!json || !Array.isArray(json.organizations) || json.organizations.some((value) => !isRecord(value)))
242
+ throw new EngagementProviderError("apollo", 502, "invalid company search response");
243
+ return {
244
+ companies: json.organizations.filter(isRecord).slice(0, limit).map(normalizeCompany),
245
+ searchRequests: 1
246
+ };
247
+ };
162
248
  const listActivities = async (query) => {
163
249
  const json = await post("/v1/emailer_messages/search", {
164
250
  per_page: query.limit ?? DEFAULT_ACTIVITY_LIMIT,
@@ -198,7 +284,8 @@ var apolloSource = (options) => {
198
284
  enrichPerson,
199
285
  id: "apollo",
200
286
  listActivities,
201
- searchPeople
287
+ searchPeople,
288
+ searchCompanies
202
289
  };
203
290
  };
204
291
  export {
@@ -1,10 +1,27 @@
1
1
  import type { ApolloOptions } from "./index";
2
- export declare const manifest: Omit<import("@absolutejs/manifest").PackageManifest<ApolloOptions, never>, "contract" | "settings" | "tools"> & (({
3
- contract: 1;
4
- tools?: Record<string, import("@absolutejs/manifest").LegacyManifestTool<never>> | undefined;
5
- } | {
6
- contract: 2;
7
- tools?: Record<string, import("@absolutejs/manifest").AuthorizedManifestTool<never>> | undefined;
8
- }) & {
2
+ export declare const manifest: Omit<
3
+ import("@absolutejs/manifest").PackageManifest<ApolloOptions, never>,
4
+ "contract" | "settings" | "tools"
5
+ > &
6
+ ((
7
+ | {
8
+ contract: 1;
9
+ tools?:
10
+ | Record<
11
+ string,
12
+ import("@absolutejs/manifest").LegacyManifestTool<never>
13
+ >
14
+ | undefined;
15
+ }
16
+ | {
17
+ contract: 2;
18
+ tools?:
19
+ | Record<
20
+ string,
21
+ import("@absolutejs/manifest").AuthorizedManifestTool<never>
22
+ >
23
+ | undefined;
24
+ }
25
+ ) & {
9
26
  settings: import("@sinclair/typebox").TObject<{}>;
10
- });
27
+ });
package/dist/manifest.js CHANGED
@@ -10,6 +10,7 @@ var manifest = defineManifest()({
10
10
  "enrich a person with Apollo",
11
11
  "enrich a company with Apollo",
12
12
  "search people by role",
13
+ "research company revenue and ownership",
13
14
  "read Apollo outreach activity"
14
15
  ],
15
16
  keywords: ["apollo", "engagement", "enrichment", "outreach", "sales"],
@@ -40,7 +41,7 @@ var manifest = defineManifest()({
40
41
  peers: [
41
42
  {
42
43
  name: "@absolutejs/engagement",
43
- range: ">=0.0.7 <0.1",
44
+ range: ">=0.0.10 <0.1",
44
45
  reason: "Shared engagement contracts and rate-limit identity"
45
46
  }
46
47
  ]
@@ -1,85 +1,73 @@
1
1
  {
2
- "contract": 2,
3
- "discovery": {
4
- "audiences": [
5
- "agent-hosts",
6
- "application-developers"
7
- ],
8
- "intents": [
9
- "enrich a person with Apollo",
10
- "enrich a company with Apollo",
11
- "search people by role",
12
- "read Apollo outreach activity"
13
- ],
14
- "keywords": [
15
- "apollo",
16
- "engagement",
17
- "enrichment",
18
- "outreach",
19
- "sales"
20
- ],
21
- "protocols": [
22
- "HTTPS"
23
- ]
24
- },
25
- "identity": {
26
- "accent": "#5c4cff",
27
- "category": "sales",
28
- "description": "Apollo-backed EngagementSource for person and company enrichment, role-based people search, and outreach activity with explicit provider-credit accounting.",
29
- "docsUrl": "https://github.com/absolutejs/engagement-adapters/tree/main/apollo",
30
- "name": "@absolutejs/engagement-apollo",
31
- "tagline": "Connect Apollo enrichment and outreach to your site."
32
- },
33
- "implements": [
34
- {
35
- "contract": "engagement/source",
36
- "factory": "apolloSource",
37
- "from": "@absolutejs/engagement-apollo",
38
- "requires": {
39
- "env": [
40
- {
41
- "description": "Apollo API key",
42
- "docsUrl": "https://developer.apollo.io/keys/",
43
- "key": "APOLLO_API_KEY",
44
- "secret": true
45
- }
46
- ],
47
- "peers": [
48
- {
49
- "name": "@absolutejs/engagement",
50
- "range": ">=0.0.7 <0.1",
51
- "reason": "Shared engagement contracts and rate-limit identity"
52
- }
53
- ]
54
- },
55
- "settings": {
56
- "type": "object",
57
- "properties": {
58
- "baseUrl": {
59
- "description": "Apollo-compatible API origin. Leave empty for the official Apollo API.",
60
- "format": "uri",
61
- "title": "API origin",
62
- "type": "string"
63
- }
64
- }
65
- },
66
- "title": "Apollo",
67
- "wiring": {
68
- "code": "apolloSource({ apiKey: ${env.APOLLO_API_KEY}, ...${settings} })",
69
- "imports": [
70
- {
71
- "from": "@absolutejs/engagement-apollo",
72
- "names": [
73
- "apolloSource"
74
- ]
75
- }
76
- ]
77
- }
78
- }
79
- ],
80
- "settings": {
81
- "type": "object",
82
- "properties": {}
83
- },
84
- "wiring": []
2
+ "contract": 2,
3
+ "discovery": {
4
+ "audiences": ["agent-hosts", "application-developers"],
5
+ "intents": [
6
+ "enrich a person with Apollo",
7
+ "enrich a company with Apollo",
8
+ "search people by role",
9
+ "research company revenue and ownership",
10
+ "read Apollo outreach activity"
11
+ ],
12
+ "keywords": ["apollo", "engagement", "enrichment", "outreach", "sales"],
13
+ "protocols": ["HTTPS"]
14
+ },
15
+ "identity": {
16
+ "accent": "#5c4cff",
17
+ "category": "sales",
18
+ "description": "Apollo-backed EngagementSource for person and company enrichment, role-based people search, and outreach activity with explicit provider-credit accounting.",
19
+ "docsUrl": "https://github.com/absolutejs/engagement-adapters/tree/main/apollo",
20
+ "name": "@absolutejs/engagement-apollo",
21
+ "tagline": "Connect Apollo enrichment and outreach to your site."
22
+ },
23
+ "implements": [
24
+ {
25
+ "contract": "engagement/source",
26
+ "factory": "apolloSource",
27
+ "from": "@absolutejs/engagement-apollo",
28
+ "requires": {
29
+ "env": [
30
+ {
31
+ "description": "Apollo API key",
32
+ "docsUrl": "https://developer.apollo.io/keys/",
33
+ "key": "APOLLO_API_KEY",
34
+ "secret": true
35
+ }
36
+ ],
37
+ "peers": [
38
+ {
39
+ "name": "@absolutejs/engagement",
40
+ "range": ">=0.0.10 <0.1",
41
+ "reason": "Shared engagement contracts and rate-limit identity"
42
+ }
43
+ ]
44
+ },
45
+ "settings": {
46
+ "type": "object",
47
+ "properties": {
48
+ "baseUrl": {
49
+ "description": "Apollo-compatible API origin. Leave empty for the official Apollo API.",
50
+ "format": "uri",
51
+ "title": "API origin",
52
+ "type": "string"
53
+ }
54
+ }
55
+ },
56
+ "title": "Apollo",
57
+ "wiring": {
58
+ "code": "apolloSource({ apiKey: ${env.APOLLO_API_KEY}, ...${settings} })",
59
+ "imports": [
60
+ {
61
+ "from": "@absolutejs/engagement-apollo",
62
+ "names": ["apolloSource"]
63
+ }
64
+ ]
65
+ }
66
+ }
67
+ ],
68
+ "settings": {
69
+ "type": "object",
70
+ "properties": {}
71
+ },
72
+ "wiring": []
85
73
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "author": "Alex Kahn",
3
3
  "description": "Apollo.io EngagementSource adapter for @absolutejs/engagement — person/company enrichment and outreach activity (emails sent/opened/replied) from the Apollo API.",
4
4
  "devDependencies": {
5
- "@absolutejs/engagement": "0.0.9",
5
+ "@absolutejs/engagement": "0.0.10",
6
6
  "@types/bun": "1.3.14",
7
7
  "prettier": "3.8.1",
8
8
  "typescript": "5.9.3"
@@ -23,7 +23,7 @@
23
23
  "main": "./dist/index.js",
24
24
  "name": "@absolutejs/engagement-apollo",
25
25
  "peerDependencies": {
26
- "@absolutejs/engagement": ">=0.0.9 <0.1"
26
+ "@absolutejs/engagement": ">=0.0.10 <0.1"
27
27
  },
28
28
  "publishConfig": {
29
29
  "access": "public"
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "type": "module",
46
46
  "types": "./dist/index.d.ts",
47
- "version": "0.0.11",
47
+ "version": "0.0.13",
48
48
  "exports": {
49
49
  ".": {
50
50
  "types": "./dist/index.d.ts",