@memberjunction/actions-apollo 4.0.0 → 4.1.0
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 +346 -206
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -4,12 +4,108 @@ Apollo.io data enrichment action classes for MemberJunction that enable automate
|
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
This package provides two
|
|
7
|
+
This package provides two server-side action classes that integrate with Apollo.io's data enrichment services to automatically populate account and contact records with company information, social profiles, technology stacks, employment history, and education data. Both actions extend `BaseAction` from `@memberjunction/actions` and are registered via `@RegisterClass` for automatic discovery by the MemberJunction Actions engine.
|
|
8
|
+
|
|
9
|
+
Key capabilities:
|
|
10
|
+
|
|
11
|
+
- **Account enrichment** -- company address, phone, description, social URLs, technology stacks, and associated contacts discovered via organization domain lookup
|
|
12
|
+
- **Contact enrichment** -- bulk email verification, social profile URLs, employment history, and education history via people matching
|
|
13
|
+
- **Configurable field mappings** -- JSON-based parameter configuration maps Apollo.io fields to your custom entity fields
|
|
14
|
+
- **Rate limit handling** -- automatic retry with intelligent backoff for both per-minute and hourly Apollo.io rate limits
|
|
15
|
+
- **Batch processing** -- concurrent group processing with configurable batch sizes and pagination for large datasets
|
|
16
|
+
|
|
17
|
+
For general Actions framework architecture and design philosophy, see the [parent Actions README](../README.md) and [Actions CLAUDE.md](../CLAUDE.md).
|
|
18
|
+
|
|
19
|
+
## Architecture
|
|
20
|
+
|
|
21
|
+
```mermaid
|
|
22
|
+
flowchart TB
|
|
23
|
+
subgraph Engine["MemberJunction Actions Engine"]
|
|
24
|
+
AE["ActionEngineServer"]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
subgraph Apollo["@memberjunction/actions-apollo"]
|
|
28
|
+
AccAction["ApolloEnrichmentAccountsAction"]
|
|
29
|
+
ConAction["ApolloEnrichmentContactsAction"]
|
|
30
|
+
Config["Configuration\n(API key, batch sizes)"]
|
|
31
|
+
Types["Apollo Type Definitions"]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
subgraph ApolloAPI["Apollo.io REST API"]
|
|
35
|
+
OrgEnrich["/organizations/enrich"]
|
|
36
|
+
BulkMatch["/people/bulk_match"]
|
|
37
|
+
PeopleSearch["/mixed_people/search"]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
subgraph MJCore["MemberJunction Core"]
|
|
41
|
+
Meta["Metadata"]
|
|
42
|
+
RV["RunView"]
|
|
43
|
+
BE["BaseEntity"]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
AE -->|executes| AccAction
|
|
47
|
+
AE -->|executes| ConAction
|
|
48
|
+
AccAction --> Config
|
|
49
|
+
ConAction --> Config
|
|
50
|
+
AccAction --> Types
|
|
51
|
+
ConAction --> Types
|
|
52
|
+
AccAction -->|HTTP via axios| OrgEnrich
|
|
53
|
+
AccAction -->|HTTP via axios| PeopleSearch
|
|
54
|
+
ConAction -->|HTTP via axios| BulkMatch
|
|
55
|
+
ConAction -->|HTTP via axios| PeopleSearch
|
|
56
|
+
AccAction -->|read/write entities| MJCore
|
|
57
|
+
ConAction -->|read/write entities| MJCore
|
|
58
|
+
|
|
59
|
+
style Engine fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
60
|
+
style Apollo fill:#7c5295,stroke:#563a6b,color:#fff
|
|
61
|
+
style ApolloAPI fill:#b8762f,stroke:#8a5722,color:#fff
|
|
62
|
+
style MJCore fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
63
|
+
```
|
|
8
64
|
|
|
9
|
-
|
|
10
|
-
|
|
65
|
+
### Account Enrichment Data Flow
|
|
66
|
+
|
|
67
|
+
```mermaid
|
|
68
|
+
flowchart LR
|
|
69
|
+
Start["Load accounts\nmatching filter"] --> OrgAPI["Call /organizations/enrich\nper domain"]
|
|
70
|
+
OrgAPI --> UpdateAcct["Update account\nfields"]
|
|
71
|
+
OrgAPI --> TechRec["Create/update\ntechnology records"]
|
|
72
|
+
OrgAPI --> PeopleAPI["Call /mixed_people/search\nfor domain contacts"]
|
|
73
|
+
PeopleAPI --> CreateContact["Create/update\ncontact records"]
|
|
74
|
+
CreateContact --> History["Create education\nhistory records"]
|
|
75
|
+
UpdateAcct --> Next["Next account"]
|
|
76
|
+
TechRec --> Next
|
|
77
|
+
History --> Next
|
|
78
|
+
|
|
79
|
+
style Start fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
80
|
+
style OrgAPI fill:#b8762f,stroke:#8a5722,color:#fff
|
|
81
|
+
style UpdateAcct fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
82
|
+
style TechRec fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
83
|
+
style PeopleAPI fill:#b8762f,stroke:#8a5722,color:#fff
|
|
84
|
+
style CreateContact fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
85
|
+
style History fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
86
|
+
style Next fill:#64748b,stroke:#475569,color:#fff
|
|
87
|
+
```
|
|
11
88
|
|
|
12
|
-
|
|
89
|
+
### Contact Enrichment Data Flow
|
|
90
|
+
|
|
91
|
+
```mermaid
|
|
92
|
+
flowchart LR
|
|
93
|
+
Start["Page contacts\nmatching filter"] --> Batch["Batch into groups\nof 10"]
|
|
94
|
+
Batch --> BulkAPI["Call /people/bulk_match\nper batch"]
|
|
95
|
+
BulkAPI --> Update["Update contact\nfields from matches"]
|
|
96
|
+
Update --> EmpHist["Upsert employment\nhistory"]
|
|
97
|
+
Update --> EduHist["Upsert education\nhistory"]
|
|
98
|
+
EmpHist --> NextBatch["Next batch"]
|
|
99
|
+
EduHist --> NextBatch
|
|
100
|
+
|
|
101
|
+
style Start fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
102
|
+
style Batch fill:#64748b,stroke:#475569,color:#fff
|
|
103
|
+
style BulkAPI fill:#b8762f,stroke:#8a5722,color:#fff
|
|
104
|
+
style Update fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
105
|
+
style EmpHist fill:#7c5295,stroke:#563a6b,color:#fff
|
|
106
|
+
style EduHist fill:#7c5295,stroke:#563a6b,color:#fff
|
|
107
|
+
style NextBatch fill:#64748b,stroke:#475569,color:#fff
|
|
108
|
+
```
|
|
13
109
|
|
|
14
110
|
## Installation
|
|
15
111
|
|
|
@@ -20,9 +116,9 @@ npm install @memberjunction/actions-apollo
|
|
|
20
116
|
## Prerequisites
|
|
21
117
|
|
|
22
118
|
1. An active Apollo.io account with API access
|
|
23
|
-
2. Apollo.io API key
|
|
24
|
-
3. MemberJunction framework properly configured
|
|
25
|
-
4. Target entities for storing enriched data (
|
|
119
|
+
2. Apollo.io API key set as the environment variable `APOLLO_API_KEY`
|
|
120
|
+
3. MemberJunction framework properly configured with server-side action engine
|
|
121
|
+
4. Target entities configured for storing enriched data (accounts, contacts, technologies, etc.)
|
|
26
122
|
|
|
27
123
|
## Configuration
|
|
28
124
|
|
|
@@ -34,257 +130,301 @@ APOLLO_API_KEY=your_apollo_api_key_here
|
|
|
34
130
|
|
|
35
131
|
### Configuration Constants
|
|
36
132
|
|
|
37
|
-
The package
|
|
133
|
+
The package defines the following defaults in `config.ts`:
|
|
38
134
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
135
|
+
| Constant | Default | Description |
|
|
136
|
+
|---|---|---|
|
|
137
|
+
| `ApolloAPIEndpoint` | `https://api.apollo.io/v1` | Apollo.io API base URL |
|
|
138
|
+
| `EmailSourceName` | `Apollo.io` | Source label applied to enriched emails |
|
|
139
|
+
| `GroupSize` | `10` | Records per API batch request (Apollo max is 10) |
|
|
140
|
+
| `ConcurrentGroups` | `1` | Number of concurrent API request groups |
|
|
141
|
+
| `MaxPeopleToEnrichPerOrg` | `500` | Maximum contacts to enrich per organization |
|
|
142
|
+
| `ApolloAPIKey` | `process.env.APOLLO_API_KEY` | Read from environment at startup |
|
|
45
143
|
|
|
46
144
|
## Usage
|
|
47
145
|
|
|
48
|
-
###
|
|
146
|
+
### Account Enrichment
|
|
49
147
|
|
|
50
|
-
|
|
148
|
+
The `ApolloEnrichmentAccountsAction` enriches account/organization records by looking up company information using domain names. It can optionally discover contacts at the organization, track technology stacks, and create education history records.
|
|
51
149
|
|
|
52
150
|
#### Parameters
|
|
53
151
|
|
|
54
|
-
|
|
152
|
+
| Parameter | Required | Type | Description |
|
|
153
|
+
|---|---|---|---|
|
|
154
|
+
| `AccountEntityFieldMappings` | Yes | JSON string | Maps account entity fields (see `AccountEntityFields` below) |
|
|
155
|
+
| `AccountTechnologyEntityFieldMappings` | No | JSON string | Maps technology relationship fields |
|
|
156
|
+
| `TechnologyCategoryEntityFieldMappings` | No | JSON string | Maps technology category fields |
|
|
157
|
+
| `ContactEntityFieldMappings` | No | JSON string | Maps contact entity fields for discovered contacts |
|
|
158
|
+
| `ContactEducationHistoryEntityFieldMappings` | No | JSON string | Maps education history fields |
|
|
55
159
|
|
|
56
|
-
|
|
57
|
-
- `AccountEntityFieldNameJSON` - Maps account entity fields
|
|
58
|
-
|
|
59
|
-
**Optional:**
|
|
60
|
-
- `AccountTechnologyEntityFieldNameJSON` - Maps account technology relationship fields
|
|
61
|
-
- `TechnologyCategoryEntityFieldNameJSON` - Maps technology category fields
|
|
62
|
-
- `ContactEntityFieldNameJSON` - Maps contact entity fields
|
|
63
|
-
- `ContactEducationHistoryEntityFieldNameJSON` - Maps contact education history fields
|
|
64
|
-
|
|
65
|
-
**AccountEntityFieldNameJSON Structure:**
|
|
66
|
-
```typescript
|
|
67
|
-
{
|
|
68
|
-
EntityName: string; // Target entity name (e.g., "Accounts")
|
|
69
|
-
DomainParamName: string; // Field containing company domain
|
|
70
|
-
AccountIDName: string; // Primary key field name
|
|
71
|
-
EnrichedAtField: string; // Timestamp field for tracking enrichment
|
|
72
|
-
ExtraFilter?: string; // SQL filter for selecting records to process
|
|
73
|
-
|
|
74
|
-
// Optional mapping fields
|
|
75
|
-
AddressFieldName?: string; // Street address field
|
|
76
|
-
CityFieldNameName?: string; // City field
|
|
77
|
-
StateProvinceFieldName?: string; // State/province field
|
|
78
|
-
PostalCodeFieldName?: string; // Postal code field
|
|
79
|
-
DescriptionFieldName?: string; // Company description field
|
|
80
|
-
PhoneNumberFieldName?: string; // Phone number field
|
|
81
|
-
CountryFieldName?: string; // Country field
|
|
82
|
-
LinkedInFieldName?: string; // LinkedIn URL field
|
|
83
|
-
LogoURLFieldName?: string; // Company logo URL field
|
|
84
|
-
FacebookFieldName?: string; // Facebook URL field
|
|
85
|
-
TwitterFieldName?: string; // Twitter URL field
|
|
86
|
-
}
|
|
87
|
-
```
|
|
160
|
+
#### AccountEntityFields Structure
|
|
88
161
|
|
|
89
|
-
**AccountTechnologyEntityFieldNameJSON Structure:**
|
|
90
162
|
```typescript
|
|
91
163
|
{
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
164
|
+
EntityName: string; // Target entity name (e.g., "Accounts")
|
|
165
|
+
DomainField: string; // Field containing company domain
|
|
166
|
+
AccountIDField: string; // Primary key field name
|
|
167
|
+
EnrichedAtField: string; // Timestamp field for tracking enrichment
|
|
168
|
+
Filter: string; // SQL filter for selecting records to process
|
|
169
|
+
AddressField?: string; // Street address
|
|
170
|
+
CityField?: string; // City
|
|
171
|
+
StateProvinceField?: string; // State/province
|
|
172
|
+
PostalCodeField?: string; // Postal code
|
|
173
|
+
DescriptionField?: string; // Company description
|
|
174
|
+
PhoneNumberField?: string; // Phone number
|
|
175
|
+
CountryField?: string; // Country
|
|
176
|
+
LinkedInField?: string; // LinkedIn URL
|
|
177
|
+
LogoURLField?: string; // Company logo URL
|
|
178
|
+
FacebookField?: string; // Facebook URL
|
|
179
|
+
TwitterField?: string; // Twitter URL
|
|
97
180
|
}
|
|
98
181
|
```
|
|
99
182
|
|
|
100
|
-
#### Example
|
|
183
|
+
#### Example
|
|
101
184
|
|
|
102
185
|
```typescript
|
|
103
|
-
import {
|
|
104
|
-
import { ActionEngine } from '@memberjunction/actions';
|
|
186
|
+
import { ActionEngineServer } from '@memberjunction/actions';
|
|
105
187
|
|
|
106
|
-
|
|
107
|
-
const engine = new ActionEngine();
|
|
108
|
-
const action = new ApolloAccountsEnrichmentAction();
|
|
188
|
+
const engine = ActionEngineServer.Instance;
|
|
109
189
|
|
|
110
|
-
// Execute the action
|
|
111
190
|
const result = await engine.RunAction({
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
191
|
+
ActionName: 'ApolloEnrichmentAccountsAction',
|
|
192
|
+
Params: [
|
|
193
|
+
{
|
|
194
|
+
Name: 'AccountEntityFieldMappings',
|
|
195
|
+
Value: JSON.stringify({
|
|
196
|
+
EntityName: 'Accounts',
|
|
197
|
+
DomainField: 'Domain',
|
|
198
|
+
AccountIDField: 'ID',
|
|
199
|
+
EnrichedAtField: 'LastEnrichedAt',
|
|
200
|
+
Filter: 'Domain IS NOT NULL AND LastEnrichedAt IS NULL',
|
|
201
|
+
CityField: 'City',
|
|
202
|
+
StateProvinceField: 'StateProvince',
|
|
203
|
+
LinkedInField: 'LinkedInURL',
|
|
204
|
+
DescriptionField: 'Description'
|
|
205
|
+
})
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
Name: 'AccountTechnologyEntityFieldMappings',
|
|
209
|
+
Value: JSON.stringify({
|
|
210
|
+
EntityName: 'Account Technologies',
|
|
211
|
+
AccountIDField: 'AccountID',
|
|
212
|
+
TechnologyIDField: 'TechnologyID',
|
|
213
|
+
TechnologyField: 'Technology',
|
|
214
|
+
CategoryField: 'Category',
|
|
215
|
+
EndedUseAtField: 'EndedUseAt'
|
|
216
|
+
})
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
Name: 'ContactEntityFieldMappings',
|
|
220
|
+
Value: JSON.stringify({
|
|
221
|
+
EntityName: 'Contacts',
|
|
222
|
+
EmailField: 'Email',
|
|
223
|
+
AccountIDField: 'AccountID',
|
|
224
|
+
EnrichedAtField: 'LastEnrichedAt',
|
|
225
|
+
FirstNameField: 'FirstName',
|
|
226
|
+
LastNameField: 'LastName',
|
|
227
|
+
TitleField: 'Title',
|
|
228
|
+
EmailSourceField: 'EmailSource',
|
|
229
|
+
ActivityCountField: 'ActivityCount'
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
],
|
|
233
|
+
ContextUser: contextUser
|
|
126
234
|
});
|
|
127
235
|
```
|
|
128
236
|
|
|
129
|
-
###
|
|
237
|
+
### Contact Enrichment
|
|
130
238
|
|
|
131
|
-
|
|
239
|
+
The `ApolloEnrichmentContactsAction` enriches existing contact records by matching on name and email combinations through Apollo's bulk people matching API.
|
|
132
240
|
|
|
133
241
|
#### Parameters
|
|
134
242
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
243
|
+
| Parameter | Required | Type | Description |
|
|
244
|
+
|---|---|---|---|
|
|
245
|
+
| `EntityName` | Yes | string | Target entity containing contacts |
|
|
246
|
+
| `EmailField` | Yes | string | Field name for email addresses |
|
|
247
|
+
| `FirstNameField` | Yes | string | Field name for first names |
|
|
248
|
+
| `LastNameField` | Yes | string | Field name for last names |
|
|
249
|
+
| `TitleField` | Yes | string | Field name for job titles |
|
|
250
|
+
| `EnrichedAtField` | Yes | string | Field name for enrichment timestamp |
|
|
251
|
+
| `Filter` | Yes | string | SQL filter to select contacts for enrichment |
|
|
252
|
+
| `ProfilePictureURLField` | No | string | Field for profile picture URLs |
|
|
253
|
+
| `AccountNameField` | No | string | Field for account/company names |
|
|
254
|
+
| `DomainField` | No | string | Field for company domains |
|
|
255
|
+
| `LinkedInField` | No | string | Field for LinkedIn profile URLs |
|
|
256
|
+
| `TwitterField` | No | string | Field for Twitter profile URLs |
|
|
257
|
+
| `FacebookField` | No | string | Field for Facebook profile URLs |
|
|
258
|
+
| `EmploymentHistoryFieldMappings` | No | JSON string | Employment history entity field mappings |
|
|
259
|
+
| `EducationHistoryFieldMappings` | No | JSON string | Education history entity field mappings |
|
|
260
|
+
|
|
261
|
+
#### EmploymentHistoryFieldMappings Structure
|
|
145
262
|
|
|
146
|
-
**Optional:**
|
|
147
|
-
- `domainParam` - Field name containing company domain
|
|
148
|
-
- `linkedinParam` - Field name for storing LinkedIn URLs
|
|
149
|
-
- `EmploymentHistoryFieldMappings` - JSON string with employment history field mappings
|
|
150
|
-
- `EducationHistoryFieldMappings` - JSON string with education history field mappings
|
|
151
|
-
|
|
152
|
-
**EmploymentHistoryFieldMappings Structure:**
|
|
153
263
|
```typescript
|
|
154
264
|
{
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
265
|
+
EmploymentHistoryEntityName: string; // Employment history entity
|
|
266
|
+
EmploymentHistoryContactIDFieldName: string; // Foreign key to contact
|
|
267
|
+
EmploymentHistoryOrganizationFieldName: string; // Organization name field
|
|
268
|
+
EmploymentHistoryTitleFieldName: string; // Job title field
|
|
159
269
|
}
|
|
160
270
|
```
|
|
161
271
|
|
|
162
|
-
|
|
272
|
+
#### EducationHistoryFieldMappings Structure
|
|
273
|
+
|
|
163
274
|
```typescript
|
|
164
275
|
{
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
276
|
+
EducationHistoryEntityName: string; // Education history entity
|
|
277
|
+
EducationHistoryContactIDFieldName: string; // Foreign key to contact
|
|
278
|
+
EducationHistoryInstitutionFieldName: string; // Institution name field
|
|
279
|
+
EducationHistoryDegreeFieldName: string; // Degree field
|
|
169
280
|
}
|
|
170
281
|
```
|
|
171
282
|
|
|
172
|
-
#### Example
|
|
283
|
+
#### Example
|
|
173
284
|
|
|
174
285
|
```typescript
|
|
175
|
-
import {
|
|
286
|
+
import { ActionEngineServer } from '@memberjunction/actions';
|
|
287
|
+
|
|
288
|
+
const engine = ActionEngineServer.Instance;
|
|
176
289
|
|
|
177
290
|
const result = await engine.RunAction({
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
291
|
+
ActionName: 'ApolloEnrichmentContactsAction',
|
|
292
|
+
Params: [
|
|
293
|
+
{ Name: 'EntityName', Value: 'Contacts' },
|
|
294
|
+
{ Name: 'EmailField', Value: 'Email' },
|
|
295
|
+
{ Name: 'FirstNameField', Value: 'FirstName' },
|
|
296
|
+
{ Name: 'LastNameField', Value: 'LastName' },
|
|
297
|
+
{ Name: 'TitleField', Value: 'Title' },
|
|
298
|
+
{ Name: 'EnrichedAtField', Value: 'LastEnrichedAt' },
|
|
299
|
+
{ Name: 'Filter', Value: 'Email IS NOT NULL AND LastEnrichedAt IS NULL' },
|
|
300
|
+
{ Name: 'DomainField', Value: 'Domain' },
|
|
301
|
+
{ Name: 'LinkedInField', Value: 'LinkedInURL' },
|
|
302
|
+
{
|
|
303
|
+
Name: 'EmploymentHistoryFieldMappings',
|
|
304
|
+
Value: JSON.stringify({
|
|
305
|
+
EmploymentHistoryEntityName: 'Contact Employment Histories',
|
|
306
|
+
EmploymentHistoryContactIDFieldName: 'ContactID',
|
|
307
|
+
EmploymentHistoryOrganizationFieldName: 'Organization',
|
|
308
|
+
EmploymentHistoryTitleFieldName: 'Title'
|
|
309
|
+
})
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
Name: 'EducationHistoryFieldMappings',
|
|
313
|
+
Value: JSON.stringify({
|
|
314
|
+
EducationHistoryEntityName: 'Contact Education Histories',
|
|
315
|
+
EducationHistoryContactIDFieldName: 'ContactID',
|
|
316
|
+
EducationHistoryInstitutionFieldName: 'Institution',
|
|
317
|
+
EducationHistoryDegreeFieldName: 'Degree'
|
|
318
|
+
})
|
|
319
|
+
}
|
|
320
|
+
],
|
|
321
|
+
ContextUser: contextUser
|
|
200
322
|
});
|
|
201
323
|
```
|
|
202
324
|
|
|
203
|
-
##
|
|
204
|
-
|
|
205
|
-
###
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
-
|
|
214
|
-
-
|
|
215
|
-
-
|
|
216
|
-
-
|
|
217
|
-
-
|
|
218
|
-
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
-
|
|
226
|
-
-
|
|
227
|
-
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
325
|
+
## API Reference
|
|
326
|
+
|
|
327
|
+
### Exported Classes
|
|
328
|
+
|
|
329
|
+
#### `ApolloEnrichmentAccountsAction`
|
|
330
|
+
|
|
331
|
+
Registered as `"ApolloEnrichmentAccountsAction"` via `@RegisterClass(BaseAction)`. Extends `BaseAction`.
|
|
332
|
+
|
|
333
|
+
**Processing behavior:**
|
|
334
|
+
- Queries accounts matching the configured filter
|
|
335
|
+
- For each account, calls `/organizations/enrich` with the domain
|
|
336
|
+
- Updates account fields with enriched organization data
|
|
337
|
+
- Optionally creates/updates technology stack records with historical tracking (marks ended technologies)
|
|
338
|
+
- Optionally discovers and creates contact records via `/mixed_people/search`
|
|
339
|
+
- Processes recursively up to 5 times to handle remaining records
|
|
340
|
+
- Supports concurrent domain processing (configurable via `ConcurrentGroups`)
|
|
341
|
+
|
|
342
|
+
#### `ApolloEnrichmentContactsAction`
|
|
343
|
+
|
|
344
|
+
Registered as `"ApolloEnrichmentContactsAction"` via `@RegisterClass(BaseAction)`. Extends `BaseAction`.
|
|
345
|
+
|
|
346
|
+
**Processing behavior:**
|
|
347
|
+
- Pages through contact records matching the configured filter (500 per page)
|
|
348
|
+
- Batches contacts into groups of 10 for Apollo's `/people/bulk_match` endpoint
|
|
349
|
+
- Updates matching contacts with enriched social profiles and company data
|
|
350
|
+
- Optionally creates/updates employment and education history records
|
|
351
|
+
- Supports secondary enrichment via `/mixed_people/search` for organization-level lookups
|
|
352
|
+
|
|
353
|
+
### Exported Types
|
|
354
|
+
|
|
355
|
+
All types are exported from `src/generic/apollo.types.ts`:
|
|
356
|
+
|
|
357
|
+
| Type | Description |
|
|
358
|
+
|---|---|
|
|
359
|
+
| `ProcessPersonRecordGroupParams` | Parameters for batch contact group processing |
|
|
360
|
+
| `ApolloBulkPeopleRequest` | Request payload for `/people/bulk_match` |
|
|
361
|
+
| `ApolloBulkPeopleRequestDetail` | Individual person detail within a bulk request |
|
|
362
|
+
| `ApolloBulkPeopleResponse` | Response from `/people/bulk_match` |
|
|
363
|
+
| `ContactEntityFields` | Field mapping configuration for contact entities |
|
|
364
|
+
| `ContactEducationHistoryEntityFields` | Field mapping for education history entities |
|
|
365
|
+
| `TechnologyCategoryEntityFields` | Field mapping for technology category entities |
|
|
366
|
+
| `AccountTechnologyEntityFields` | Field mapping for account-technology relationship entities |
|
|
367
|
+
| `AccountEntityFields` | Field mapping configuration for account entities |
|
|
368
|
+
| `ProcessSingleDomainParams` | Parameters for processing a single domain enrichment |
|
|
369
|
+
| `OrganizationEnrichmentRequest` | Request for `/organizations/enrich` |
|
|
370
|
+
| `OrganizationEnrichmentResponse` | Response from organization enrichment |
|
|
371
|
+
| `OrganizationEnrichmentOrganization` | Detailed organization data from Apollo |
|
|
372
|
+
| `OrganizationEnrichmentOrganizationAccount` | Account data within organization response |
|
|
373
|
+
| `TechnologyMap` | Technology record with name, category, and UID |
|
|
374
|
+
| `SearchPeopleResponse` | Response from `/mixed_people/search` |
|
|
375
|
+
| `SearchPeopleResponsePerson` | Individual person data from search response |
|
|
376
|
+
| `EmploymentHistory` | Employment/education history entry |
|
|
377
|
+
|
|
378
|
+
## Rate Limiting and Error Handling
|
|
379
|
+
|
|
380
|
+
Both action classes include a `WrapApolloCall` method that provides:
|
|
381
|
+
|
|
382
|
+
- **Automatic retry** on HTTP 429 (Too Many Requests) responses
|
|
383
|
+
- **Per-minute backoff**: 60-second wait on standard rate limit responses
|
|
384
|
+
- **Hourly backoff**: 60-minute wait when Apollo's hourly rate limit is detected (contact action only)
|
|
385
|
+
- **Exception handling**: Catches both Axios response errors and thrown exceptions for 429 status codes
|
|
386
|
+
- **Comprehensive logging** via MemberJunction's `LogError` and `LogStatus` utilities
|
|
387
|
+
|
|
388
|
+
### Title Filtering
|
|
389
|
+
|
|
390
|
+
Both actions automatically exclude contacts with the following job titles to maintain data quality:
|
|
391
|
+
- `member`
|
|
392
|
+
- `student member`
|
|
393
|
+
- `student`
|
|
394
|
+
- `volunteer`
|
|
395
|
+
|
|
396
|
+
## Apollo.io API Endpoints Used
|
|
397
|
+
|
|
398
|
+
| Endpoint | HTTP Method | Used By | Purpose |
|
|
399
|
+
|---|---|---|---|
|
|
400
|
+
| `/organizations/enrich` | GET | Accounts action | Organization data by domain |
|
|
401
|
+
| `/people/bulk_match` | POST | Contacts action | Bulk contact matching (up to 10 per request) |
|
|
402
|
+
| `/mixed_people/search` | POST | Both actions | People search by organization domain |
|
|
236
403
|
|
|
237
404
|
## Dependencies
|
|
238
405
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
1. **Batch Processing**: The actions automatically batch records to optimize API usage and respect rate limits.
|
|
248
|
-
|
|
249
|
-
2. **Field Mapping**: Carefully map Apollo.io fields to your entity fields to ensure data consistency.
|
|
250
|
-
|
|
251
|
-
3. **Filtering**: Use the filter parameters to process only records that need enrichment, avoiding unnecessary API calls.
|
|
252
|
-
|
|
253
|
-
4. **Error Monitoring**: Monitor the action logs for failed enrichments and rate limit issues.
|
|
254
|
-
|
|
255
|
-
5. **Data Quality**: The actions include validation for email domains and exclude certain titles to maintain data quality.
|
|
406
|
+
| Package | Purpose |
|
|
407
|
+
|---|---|
|
|
408
|
+
| `@memberjunction/actions` | Base action class (`BaseAction`) and action engine |
|
|
409
|
+
| `@memberjunction/actions-base` | Action parameter types (`ActionParam`, `ActionResultSimple`, `RunActionParams`) |
|
|
410
|
+
| `@memberjunction/core` | `Metadata`, `RunView`, `BaseEntity`, logging utilities, `UserInfo`, `CompositeKey` |
|
|
411
|
+
| `@memberjunction/core-entities` | MemberJunction entity definitions |
|
|
412
|
+
| `@memberjunction/global` | `@RegisterClass` decorator for action registration |
|
|
413
|
+
| `axios` | HTTP client for Apollo.io API requests |
|
|
256
414
|
|
|
257
415
|
## Limitations
|
|
258
416
|
|
|
259
|
-
- Maximum 10 records per bulk API request (Apollo.io limitation)
|
|
260
|
-
- Rate limits apply based on your Apollo.io subscription (handled automatically with retries)
|
|
417
|
+
- Maximum 10 records per bulk API request (Apollo.io API limitation)
|
|
418
|
+
- Rate limits apply based on your Apollo.io subscription tier (handled automatically with retries)
|
|
261
419
|
- Personal emails may not be revealed in GDPR-compliant regions
|
|
262
|
-
-
|
|
263
|
-
-
|
|
264
|
-
-
|
|
420
|
+
- Account enrichment processes domains sequentially within each concurrent group
|
|
421
|
+
- Contact enrichment paginates with a maximum of 500 contacts per organization
|
|
422
|
+
- Account enrichment recurses up to 5 times to prevent infinite loops
|
|
265
423
|
- Excluded job titles (member, student member, student, volunteer) are automatically skipped
|
|
266
424
|
|
|
267
|
-
##
|
|
268
|
-
|
|
269
|
-
### Common Issues
|
|
270
|
-
|
|
271
|
-
1. **Missing API Key**: Ensure `APOLLO_API_KEY` environment variable is set
|
|
272
|
-
2. **Rate Limits**: The action will automatically retry after waiting for rate limit windows
|
|
273
|
-
3. **No Matches Found**: Check that input data (domains, emails) are valid and formatted correctly
|
|
274
|
-
4. **Field Mapping Errors**: Verify that all mapped fields exist in your target entities
|
|
275
|
-
|
|
276
|
-
### Debug Logging
|
|
277
|
-
|
|
278
|
-
The actions use MemberJunction's logging system. Monitor logs for:
|
|
279
|
-
- Enrichment progress
|
|
280
|
-
- API response details
|
|
281
|
-
- Error messages
|
|
282
|
-
- Rate limit notifications
|
|
283
|
-
|
|
284
|
-
## Contributing
|
|
285
|
-
|
|
286
|
-
This package is part of the MemberJunction open-source project. Contributions are welcome following the project's contribution guidelines.
|
|
287
|
-
|
|
288
|
-
## License
|
|
425
|
+
## Related Packages
|
|
289
426
|
|
|
290
|
-
|
|
427
|
+
- [@memberjunction/actions-base](../Base) -- Base classes and types used by all action packages
|
|
428
|
+
- [@memberjunction/actions](../Engine) -- Server-side action engine that discovers and executes actions
|
|
429
|
+
- [@memberjunction/core-actions](../CoreActions) -- Collection of 40+ pre-built MemberJunction actions
|
|
430
|
+
- [@memberjunction/actions-content-autotag](../ContentAutotag) -- Content tagging and vectorization actions
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/actions-apollo",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.1.0",
|
|
5
5
|
"description": "Action classes that wrap the Apollo.io data enrichment API for contacts and accounts",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
"typescript": "^5.9.3"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@memberjunction/actions": "4.
|
|
24
|
-
"@memberjunction/actions-base": "4.
|
|
25
|
-
"@memberjunction/core": "4.
|
|
26
|
-
"@memberjunction/core-entities": "4.
|
|
27
|
-
"@memberjunction/global": "4.
|
|
23
|
+
"@memberjunction/actions": "4.1.0",
|
|
24
|
+
"@memberjunction/actions-base": "4.1.0",
|
|
25
|
+
"@memberjunction/core": "4.1.0",
|
|
26
|
+
"@memberjunction/core-entities": "4.1.0",
|
|
27
|
+
"@memberjunction/global": "4.1.0",
|
|
28
28
|
"axios": "^1.13.4"
|
|
29
29
|
},
|
|
30
30
|
"repository": {
|