@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 +18 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +109 -22
- package/dist/manifest.d.ts +25 -8
- package/dist/manifest.js +2 -1
- package/dist/manifest.json +71 -83
- package/package.json +3 -3
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
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 {
|
package/dist/manifest.d.ts
CHANGED
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
import type { ApolloOptions } from "./index";
|
|
2
|
-
export declare const manifest: Omit<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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.
|
|
44
|
+
range: ">=0.0.10 <0.1",
|
|
44
45
|
reason: "Shared engagement contracts and rate-limit identity"
|
|
45
46
|
}
|
|
46
47
|
]
|
package/dist/manifest.json
CHANGED
|
@@ -1,85 +1,73 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
47
|
+
"version": "0.0.13",
|
|
48
48
|
"exports": {
|
|
49
49
|
".": {
|
|
50
50
|
"types": "./dist/index.d.ts",
|