improvement(apollo): align tools and block with Apollo API docs (#4487)

* improvement(apollo): align tools and block with Apollo API docs

* improvement(apollo): fix tool outputs to match Apollo API response shapes

* chore(apollo): regenerate docs for output changes

* fix(apollo): address PR review comments

* fix(apollo): allow skipped_contact_ids as hash per Apollo docs

* docs

* fix(apollo): add runtime guard for account_bulk_update empty body

* fix(apollo): require contact_attributes for bulk_update

* fix(apollo): add subblock id migrations for renamed opportunity fields

* fix(apollo): tighten account_bulk_update guard and accept object attrs

* fix(apollo): require contact_ids with object-form contact_attributes

* docs(apollo): clarify contact_bulk_update parameter requirements

* fix(apollo): handle flat and wrapped contact response shapes

* validate

* fix(apollo): mirror bulk_update guard, preserve update fields in migration, expose account_bulk_create options

* fix(apollo): don't clobber user contact_attributes in migration; simplify task_create created flag

* fix(apollo): drop undocumented task type, preserve mixed-array IDs, migrate note→task_notes

* fix(apollo): align tools and block with live API docs

Final pass over the Apollo integration after a per-tool forensic audit
against Apollo.io docs. Notable fixes:

- organization_enrich: GET+querystring -> POST+JSON body (canonical, non
  master-key)
- organization_bulk_enrich: ?domains[]= -> JSON body { organizations }
- people_search: declare/forward organization_num_employees_ranges; fix
  contact_email_status placeholder ("likely to engage", with spaces)
- account_bulk_create: surface failed_accounts and failed count
- contact_bulk_create: expand documented per-contact fields (CRM IDs,
  phone_numbers, contact_emails, typed_custom_fields, etc.)
- sequence_add_contacts: surface remaining documented filter params
- task_create: confirm wire field name (note) and remap from task_notes
- types: tighten params/responses for the above

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs

* fix(apollo): add _removed_* migrations for retired opportunity subblocks

* fix(apollo): expose webhook_url subblock for people enrich phone reveal

* fix(apollo): drop colliding account_ids migration, enforce contact bulk limit, expose async toggle for accounts

* fix(apollo): cap account_attributes at 1000 in bulk update

* fix(apollo): drop bare-id merging in bulk update migration to avoid empty attribute objects

* fix(apollo): reject ambiguous account/contact_ids + array-form attributes

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Waleed
2026-05-07 19:39:48 -07:00
committed by GitHub
co-authored by Claude Opus 4.7
parent 408669dd85
commit 6a006851fa
30 changed files with 1973 additions and 710 deletions
+141 -72
View File
@@ -49,9 +49,15 @@ Search Apollo
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Apollo API key |
| `person_titles` | array | No | Job titles to search for \(e.g., \["CEO", "VP of Sales"\]\) |
| `include_similar_titles` | boolean | No | Whether to return people with job titles similar to person_titles |
| `person_locations` | array | No | Locations to search in \(e.g., \["San Francisco, CA", "New York, NY"\]\) |
| `person_seniorities` | array | No | Seniority levels \(e.g., \["senior", "executive", "manager"\]\) |
| `organization_names` | array | No | Company names to search within |
| `person_seniorities` | array | No | Seniority levels \(one of: owner, founder, c_suite, partner, vp, head, director, manager, senior, entry, intern\) |
| `organization_ids` | array | No | Apollo organization IDs to filter by \(e.g., \["5e66b6381e05b4008c8331b8"\]\) |
| `organization_names` | array | No | Company names to search within \(legacy filter\) |
| `organization_locations` | array | No | Headquarters locations of the people's current employer \(e.g., \['texas', 'tokyo', 'spain'\]\) |
| `q_organization_domains_list` | array | No | Employer domain names \(e.g., \["apollo.io", "microsoft.com"\]\) — up to 1,000, no www. or @ |
| `organization_num_employees_ranges` | array | No | Employee count ranges for the person\'s current employer. Each entry is "min,max" \(e.g., \["1,10", "250,500", "10000,20000"\]\) |
| `contact_email_status` | array | No | Email statuses to filter by: "verified", "unverified", "likely to engage", "unavailable" |
| `q_keywords` | string | No | Keywords to search for |
| `page` | number | No | Page number for pagination, default 1 \(e.g., 1, 2, 3\) |
| `per_page` | number | No | Results per page, default 25, max 100 \(e.g., 25, 50, 100\) |
@@ -76,12 +82,16 @@ Enrich data for a single person using Apollo
| `apiKey` | string | Yes | Apollo API key |
| `first_name` | string | No | First name of the person |
| `last_name` | string | No | Last name of the person |
| `name` | string | No | Full name of the person \(alternative to first_name/last_name\) |
| `id` | string | No | Apollo ID for the person |
| `hashed_email` | string | No | MD5 or SHA-256 hashed email |
| `email` | string | No | Email address of the person |
| `organization_name` | string | No | Company name where the person works |
| `domain` | string | No | Company domain \(e.g., "apollo.io", "acme.com"\) |
| `linkedin_url` | string | No | LinkedIn profile URL |
| `reveal_personal_emails` | boolean | No | Reveal personal email addresses \(uses credits\) |
| `reveal_phone_number` | boolean | No | Reveal phone numbers \(uses credits\) |
| `reveal_phone_number` | boolean | No | Reveal phone numbers \(uses credits, requires webhook_url\) |
| `webhook_url` | string | No | Webhook URL for async phone number delivery \(required when reveal_phone_number is true\) |
#### Output
@@ -101,15 +111,18 @@ Enrich data for up to 10 people at once using Apollo
| `apiKey` | string | Yes | Apollo API key |
| `people` | array | Yes | Array of people to enrich \(max 10\) |
| `reveal_personal_emails` | boolean | No | Reveal personal email addresses \(uses credits\) |
| `reveal_phone_number` | boolean | No | Reveal phone numbers \(uses credits\) |
| `reveal_phone_number` | boolean | No | Reveal phone numbers \(uses credits, requires webhook_url\) |
| `webhook_url` | string | No | Webhook URL for async phone number delivery \(required when reveal_phone_number is true\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `people` | json | Array of enriched people data |
| `total` | number | Total number of people processed |
| `enriched` | number | Number of people successfully enriched |
| `matches` | json | Array of enriched people \(null entries indicate no match\) |
| `total_requested_enrichments` | number | Total number of records submitted for enrichment |
| `unique_enriched_records` | number | Number of records successfully enriched |
| `missing_records` | number | Number of records that could not be enriched |
| `credits_consumed` | number | Number of Apollo credits consumed by this request |
### `apollo_organization_search`
@@ -120,10 +133,13 @@ Search Apollo
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Apollo API key |
| `organization_locations` | array | No | Company locations to search |
| `organization_num_employees_ranges` | array | No | Employee count ranges \(e.g., \["1-10", "11-50"\]\) |
| `organization_locations` | array | No | Company HQ locations \(cities, US states, or countries\) |
| `organization_not_locations` | array | No | Exclude companies whose HQ is in these locations |
| `organization_num_employees_ranges` | array | No | Employee count ranges as "min,max" strings \(e.g., \["1,10", "250,500", "10000,20000"\]\) |
| `q_organization_keyword_tags` | array | No | Industry or keyword tags |
| `q_organization_name` | string | No | Organization name to search for \(e.g., "Acme", "TechCorp"\) |
| `organization_ids` | array | No | Apollo organization IDs to include \(e.g., \["5e66b6381e05b4008c8331b8"\]\) |
| `q_organization_domains_list` | array | No | Domain names to filter by \(no www. or @, up to 1,000\) |
| `page` | number | No | Page number for pagination \(e.g., 1, 2, 3\) |
| `per_page` | number | No | Results per page, max 100 \(e.g., 25, 50, 100\) |
@@ -145,8 +161,7 @@ Enrich data for a single organization using Apollo
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Apollo API key |
| `organization_name` | string | No | Name of the organization \(e.g., "Acme Corporation"\) - at least one of organization_name or domain is required |
| `domain` | string | No | Company domain \(e.g., "apollo.io", "acme.com"\) - at least one of domain or organization_name is required |
| `domain` | string | Yes | Company domain \(e.g., "apollo.io", "acme.com"\) |
#### Output
@@ -164,15 +179,17 @@ Enrich data for up to 10 organizations at once using Apollo
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Apollo API key |
| `organizations` | array | Yes | Array of organizations to enrich \(max 10\) |
| `organizations` | array | Yes | Array of organizations to enrich \(max 10\). Each item requires `name` and may include `domain` \(e.g., \[\{"name": "Example Corp", "domain": "example.com"\}\]\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `organizations` | json | Array of enriched organization data |
| `total` | number | Total number of organizations processed |
| `enriched` | number | Number of organizations successfully enriched |
| `total` | number | Total number of domains requested |
| `enriched` | number | Number of unique enriched records |
| `missing_records` | number | Number of domains that could not be enriched |
| `unique_domains` | number | Number of unique domains processed |
### `apollo_contact_create`
@@ -188,7 +205,19 @@ Create a new contact in your Apollo database
| `email` | string | No | Email address of the contact |
| `title` | string | No | Job title \(e.g., "VP of Sales", "Software Engineer"\) |
| `account_id` | string | No | Apollo account ID to associate with \(e.g., "acc_abc123"\) |
| `owner_id` | string | No | User ID of the contact owner |
| `owner_id` | string | No | User ID of the contact owner \(accepted by Apollo but not officially documented for POST /contacts\) |
| `organization_name` | string | No | Name of the contact\'s employer \(e.g., "Apollo"\) |
| `website_url` | string | No | Corporate website URL \(e.g., "https://www.apollo.io/"\) |
| `label_names` | array | No | Lists/labels to add the contact to \(e.g., \["Prospects"\]\) |
| `contact_stage_id` | string | No | Apollo ID for the contact stage |
| `present_raw_address` | string | No | Personal location for the contact \(e.g., "Atlanta, United States"\) |
| `direct_phone` | string | No | Primary phone number |
| `corporate_phone` | string | No | Work/office phone number |
| `mobile_phone` | string | No | Mobile phone number |
| `home_phone` | string | No | Home phone number |
| `other_phone` | string | No | Alternative phone number |
| `typed_custom_fields` | json | No | Custom field values keyed by custom field ID |
| `run_dedupe` | boolean | No | When true, Apollo deduplicates against existing contacts |
#### Output
@@ -212,7 +241,18 @@ Update an existing contact in your Apollo database
| `email` | string | No | Email address |
| `title` | string | No | Job title \(e.g., "VP of Sales", "Software Engineer"\) |
| `account_id` | string | No | Apollo account ID \(e.g., "acc_abc123"\) |
| `owner_id` | string | No | User ID of the contact owner |
| `owner_id` | string | No | User ID of the contact owner \(accepted by Apollo but not officially documented for PATCH /contacts/\{id\}\) |
| `organization_name` | string | No | Name of the contact\'s employer \(e.g., "Apollo"\) |
| `website_url` | string | No | Corporate website URL \(e.g., "https://www.apollo.io/"\) |
| `label_names` | array | No | Lists/labels to add the contact to \(e.g., \["Prospects"\]\) |
| `contact_stage_id` | string | No | Apollo ID for the contact stage |
| `present_raw_address` | string | No | Personal location for the contact \(e.g., "Atlanta, United States"\) |
| `direct_phone` | string | No | Primary phone number |
| `corporate_phone` | string | No | Work/office phone number |
| `mobile_phone` | string | No | Mobile phone number |
| `home_phone` | string | No | Home phone number |
| `other_phone` | string | No | Alternative phone number |
| `typed_custom_fields` | json | No | Custom field values keyed by custom field ID \(accepted by Apollo but not officially documented for PATCH /contacts/\{id\}\) |
#### Output
@@ -232,6 +272,9 @@ Search your team
| `apiKey` | string | Yes | Apollo API key |
| `q_keywords` | string | No | Keywords to search for |
| `contact_stage_ids` | array | No | Filter by contact stage IDs |
| `contact_label_ids` | array | No | Filter by Apollo label IDs \(lists\) |
| `sort_by_field` | string | No | Sort field: contact_last_activity_date, contact_email_last_opened_at, contact_email_last_clicked_at, contact_created_at, or contact_updated_at |
| `sort_ascending` | boolean | No | When true, sort ascending. Must be used together with sort_by_field |
| `page` | number | No | Page number for pagination \(e.g., 1, 2, 3\) |
| `per_page` | number | No | Results per page, max 100 \(e.g., 25, 50, 100\) |
@@ -251,7 +294,8 @@ Create up to 100 contacts at once in your Apollo database. Supports deduplicatio
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Apollo API key \(master key required\) |
| `contacts` | array | Yes | Array of contacts to create \(max 100\). Each contact should include first_name, last_name, and optionally email, title, account_id, owner_id |
| `contacts` | array | Yes | Array of contacts to create \(max 100\). Each contact may include first_name, last_name, email, title, organization_name, account_id, owner_id, contact_stage_id, linkedin_url, phone \(single string\) or phone_numbers \(array of \{raw_number, position\}\), contact_emails, typed_custom_fields, and CRM IDs \(salesforce_contact_id, hubspot_id, team_id\) for cross-system matching |
| `append_label_names` | array | No | Label names to add to all contacts in this request \(e.g., \["Hot Lead"\]\) |
| `run_dedupe` | boolean | No | Enable deduplication to prevent creating duplicate contacts. When true, existing contacts are returned without modification |
#### Output
@@ -273,17 +317,16 @@ Update up to 100 existing contacts at once in your Apollo database. Each contact
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Apollo API key \(master key required\) |
| `contacts` | array | Yes | Array of contacts to update \(max 100\). Each contact must include id field, and optionally first_name, last_name, email, title, account_id, owner_id |
| `contact_ids` | array | No | Array of contact IDs to update. Must be paired with an object-form contact_attributes specifying the fields to apply uniformly to all listed contacts. |
| `contact_attributes` | json | No | Required. Either an array of per-contact updates \(each with id\) — used standalone — or a single object of attributes to apply to all contact_ids. Supported fields: owner_id, email, organization_name, title, first_name, last_name, account_id, present_raw_address, linkedin_url, typed_custom_fields |
| `async` | boolean | No | Force asynchronous processing. Automatically enabled for &gt;100 contacts |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `updated_contacts` | json | Array of successfully updated contacts |
| `failed_contacts` | json | Array of contacts that failed to update |
| `total_submitted` | number | Total number of contacts submitted |
| `updated` | number | Number of contacts successfully updated |
| `failed` | number | Number of contacts that failed to update |
| `message` | string | Confirmation message from Apollo |
| `job_id` | string | Async job ID \(returned for &gt;100 contacts\) |
### `apollo_account_create`
@@ -293,11 +336,14 @@ Create a new account (company) in your Apollo database
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Apollo API key |
| `apiKey` | string | Yes | Apollo API key \(master key required\) |
| `name` | string | Yes | Company name \(e.g., "Acme Corporation"\) |
| `website_url` | string | No | Company website URL |
| `phone` | string | No | Company phone number |
| `owner_id` | string | No | User ID of the account owner |
| `domain` | string | No | Company domain without www. prefix \(e.g., "acme.com"\) |
| `phone` | string | No | Primary phone number for the account |
| `owner_id` | string | No | Apollo user ID of the account owner |
| `account_stage_id` | string | No | Apollo ID for the account stage to assign this account to |
| `raw_address` | string | No | Corporate location \(e.g., "San Francisco, CA, USA"\) |
| `typed_custom_fields` | json | No | Custom field values as \{ custom_field_id: value \} map |
#### Output
@@ -317,9 +363,12 @@ Update an existing account in your Apollo database
| `apiKey` | string | Yes | Apollo API key |
| `account_id` | string | Yes | ID of the account to update \(e.g., "acc_abc123"\) |
| `name` | string | No | Company name \(e.g., "Acme Corporation"\) |
| `website_url` | string | No | Company website URL |
| `domain` | string | No | Company domain \(e.g., "acme.com"\) |
| `phone` | string | No | Company phone number |
| `owner_id` | string | No | User ID of the account owner |
| `owner_id` | string | No | Apollo user ID of the account owner |
| `account_stage_id` | string | No | Apollo ID for the account stage to assign this account to |
| `raw_address` | string | No | Corporate location \(e.g., "San Francisco, CA, USA"\) |
| `typed_custom_fields` | json | No | Custom field values as \{ custom_field_id: value \} map |
#### Output
@@ -337,9 +386,11 @@ Search your team
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Apollo API key \(master key required\) |
| `q_keywords` | string | No | Keywords to search for in account data |
| `owner_id` | string | No | Filter by account owner user ID |
| `q_organization_name` | string | No | Filter accounts by organization name \(partial-match search\) |
| `account_stage_ids` | array | No | Filter by account stage IDs |
| `account_label_ids` | array | No | Filter by account label IDs |
| `sort_by_field` | string | No | Sort field: "account_last_activity_date", "account_created_at", or "account_updated_at" |
| `sort_ascending` | boolean | No | Sort ascending when true. Defaults to descending. |
| `page` | number | No | Page number for pagination \(e.g., 1, 2, 3\) |
| `per_page` | number | No | Results per page, max 100 \(e.g., 25, 50, 100\) |
@@ -352,24 +403,28 @@ Search your team
### `apollo_account_bulk_create`
Create up to 100 accounts at once in your Apollo database. Note: Apollo does not apply deduplication - duplicate accounts may be created if entries share similar names or domains. Master key required.
Create up to 100 accounts at once in your Apollo database. Set run_dedupe=true to deduplicate by domain, organization_id, and name. Master key required.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Apollo API key \(master key required\) |
| `accounts` | array | Yes | Array of accounts to create \(max 100\). Each account should include name \(required\), and optionally website_url, phone, owner_id |
| `accounts` | array | Yes | Array of accounts to create \(max 100\). Each account should include a name, and may optionally include domain, phone, phone_status_cd, raw_address, owner_id, linkedin_url, facebook_url, twitter_url, salesforce_id, and hubspot_id. |
| `append_label_names` | array | No | Array of label names to add to ALL accounts in this request |
| `run_dedupe` | boolean | No | When true, performs aggressive deduplication by domain, organization_id, and name \(defaults to false\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `created_accounts` | json | Array of newly created accounts |
| `failed_accounts` | json | Array of accounts that failed to create |
| `total_submitted` | number | Total number of accounts submitted |
| `existing_accounts` | json | Array of existing accounts returned by Apollo \(when duplicates are detected\) |
| `failed_accounts` | json | Array of accounts that failed to be created, with reasons for failure |
| `total_submitted` | number | Total number of accounts in the response \(created + existing + failed\) |
| `created` | number | Number of accounts successfully created |
| `failed` | number | Number of accounts that failed to create |
| `existing` | number | Number of existing accounts found |
| `failed` | number | Number of accounts that failed to be created |
### `apollo_account_bulk_update`
@@ -380,17 +435,18 @@ Update up to 1000 existing accounts at once in your Apollo database (higher limi
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Apollo API key \(master key required\) |
| `accounts` | array | Yes | Array of accounts to update \(max 1000\). Each account must include id field, and optionally name, website_url, phone, owner_id |
| `account_ids` | array | No | Array of account IDs to update with the same values \(max 1000\). Use with name/owner_id for uniform updates. Use either this OR account_attributes. |
| `name` | string | No | When using account_ids, apply this name to all accounts |
| `owner_id` | string | No | When using account_ids, apply this owner to all accounts |
| `account_attributes` | json | No | Array of account objects with individual updates \(each must include id\). Example: \[\{"id": "acc1", "name": "Acme", "owner_id": "u1", "account_stage_id": "s1", "typed_custom_fields": \{"field_id": "value"\}\}\] |
| `async` | boolean | No | When true, processes the update asynchronously. Only supported when using account_ids; returns 422 if used with account_attributes. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `updated_accounts` | json | Array of successfully updated accounts |
| `failed_accounts` | json | Array of accounts that failed to update |
| `total_submitted` | number | Total number of accounts submitted |
| `updated` | number | Number of accounts successfully updated |
| `failed` | number | Number of accounts that failed to update |
| `message` | string | Confirmation message from Apollo |
| `account_ids` | json | IDs of accounts that were updated |
### `apollo_opportunity_create`
@@ -402,12 +458,12 @@ Create a new deal for an account in your Apollo database (master key required)
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Apollo API key \(master key required\) |
| `name` | string | Yes | Name of the opportunity/deal \(e.g., "Enterprise License - Q1"\) |
| `account_id` | string | Yes | ID of the account this opportunity belongs to \(e.g., "acc_abc123"\) |
| `amount` | number | No | Monetary value of the opportunity |
| `stage_id` | string | No | ID of the deal stage |
| `account_id` | string | No | ID of the account this opportunity belongs to \(e.g., "acc_abc123"\) |
| `amount` | string | No | Monetary value as a plain number string with no commas or currency symbols |
| `opportunity_stage_id` | string | No | ID of the opportunity stage |
| `owner_id` | string | No | User ID of the opportunity owner |
| `close_date` | string | No | Expected close date \(ISO 8601 format\) |
| `description` | string | No | Description or notes about the opportunity |
| `closed_date` | string | No | Expected close date in YYYY-MM-DD format |
| `typed_custom_fields` | json | No | Custom field values as \{ custom_field_id: value \} map |
#### Output
@@ -425,10 +481,7 @@ Search and list all deals/opportunities in your team
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Apollo API key |
| `q_keywords` | string | No | Keywords to search for in opportunity names |
| `account_ids` | array | No | Filter by specific account IDs \(e.g., \["acc_123", "acc_456"\]\) |
| `stage_ids` | array | No | Filter by deal stage IDs |
| `owner_ids` | array | No | Filter by opportunity owner IDs |
| `sort_by_field` | string | No | Sort field: "amount", "is_closed", or "is_won" |
| `page` | number | No | Page number for pagination \(e.g., 1, 2, 3\) |
| `per_page` | number | No | Results per page, max 100 \(e.g., 25, 50, 100\) |
@@ -470,11 +523,11 @@ Update an existing deal/opportunity in your Apollo database
| `apiKey` | string | Yes | Apollo API key |
| `opportunity_id` | string | Yes | ID of the opportunity to update \(e.g., "opp_abc123"\) |
| `name` | string | No | Name of the opportunity/deal \(e.g., "Enterprise License - Q1"\) |
| `amount` | number | No | Monetary value of the opportunity |
| `stage_id` | string | No | ID of the deal stage |
| `amount` | string | No | Monetary value as a plain number string with no commas or currency symbols |
| `opportunity_stage_id` | string | No | ID of the opportunity stage |
| `owner_id` | string | No | User ID of the opportunity owner |
| `close_date` | string | No | Expected close date \(ISO 8601 format\) |
| `description` | string | No | Description or notes about the opportunity |
| `closed_date` | string | No | Expected close date in YYYY-MM-DD format |
| `typed_custom_fields` | json | No | Custom field values as \{ custom_field_id: value \} map |
#### Output
@@ -493,7 +546,6 @@ Search for sequences/campaigns in your team
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Apollo API key \(master key required\) |
| `q_name` | string | No | Search sequences by name \(e.g., "Outbound Q1", "Follow-up"\) |
| `active` | boolean | No | Filter by active status \(true for active sequences, false for inactive\) |
| `page` | number | No | Page number for pagination \(e.g., 1, 2, 3\) |
| `per_page` | number | No | Results per page, max 100 \(e.g., 25, 50, 100\) |
@@ -516,40 +568,58 @@ Add contacts to an Apollo sequence
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Apollo API key \(master key required\) |
| `sequence_id` | string | Yes | ID of the sequence to add contacts to \(e.g., "seq_abc123"\) |
| `contact_ids` | array | Yes | Array of contact IDs to add to the sequence \(e.g., \["con_abc123", "con_def456"\]\) |
| `emailer_campaign_id` | string | No | Optional emailer campaign ID |
| `send_email_from_user_id` | string | No | User ID to send emails from |
| `contact_ids` | array | No | Array of contact IDs to add to the sequence \(e.g., \["con_abc123", "con_def456"\]\). Either contact_ids or label_names must be provided. |
| `label_names` | array | No | Array of label names to identify contacts to add to the sequence. Either contact_ids or label_names must be provided. |
| `send_email_from_email_account_id` | string | Yes | ID of the email account to send from. Use the Get Email Accounts operation to look this up. |
| `send_email_from_email_address` | string | No | Specific email address to send from within the email account. |
| `sequence_no_email` | boolean | No | Add contacts even if they have no email address |
| `sequence_unverified_email` | boolean | No | Add contacts with unverified email addresses |
| `sequence_job_change` | boolean | No | Add contacts who recently changed jobs |
| `sequence_active_in_other_campaigns` | boolean | No | Add contacts active in other campaigns |
| `sequence_finished_in_other_campaigns` | boolean | No | Add contacts who finished other campaigns |
| `sequence_same_company_in_same_campaign` | boolean | No | Add contacts even if others from the same company are in the sequence |
| `contacts_without_ownership_permission` | boolean | No | Add contacts without ownership permission |
| `add_if_in_queue` | boolean | No | Add contacts even if they are in the queue |
| `contact_verification_skipped` | boolean | No | Skip contact verification when adding |
| `user_id` | string | No | ID of the user performing the action |
| `status` | string | No | Initial status for added contacts: "active" or "paused" |
| `auto_unpause_at` | string | No | ISO 8601 datetime to automatically unpause contacts |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `contacts_added` | json | Array of contact IDs added to the sequence |
| `added` | json | Array of contact objects successfully added to the sequence |
| `skipped` | json | Array of contact objects that were skipped, with reasons |
| `skipped_contact_ids` | json | Skipped contact IDs — either an array of IDs or a hash mapping ID → reason code |
| `emailer_campaign` | json | Details of the emailer campaign \(id, name\) |
| `sequence_id` | string | ID of the sequence contacts were added to |
| `total_added` | number | Total number of contacts added |
| `total_skipped` | number | Total number of contacts skipped |
### `apollo_task_create`
Create a new task in Apollo
Create one or more tasks in Apollo (one task per contact_id, master key required)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Apollo API key \(master key required\) |
| `note` | string | Yes | Task note/description |
| `contact_id` | string | No | Contact ID to associate with \(e.g., "con_abc123"\) |
| `account_id` | string | No | Account ID to associate with \(e.g., "acc_abc123"\) |
| `due_at` | string | No | Due date in ISO format |
| `priority` | string | No | Task priority |
| `type` | string | No | Task type |
| `user_id` | string | Yes | ID of the Apollo user the task is assigned to |
| `contact_ids` | array | Yes | Array of contact IDs. One task is created per contact. |
| `priority` | string | No | Task priority: "high", "medium", or "low" \(defaults to "medium"\) |
| `due_at` | string | Yes | Due date/time in ISO 8601 format \(e.g., "2024-12-31T23:59:59Z"\) |
| `type` | string | Yes | Task type: "call", "outreach_manual_email", "linkedin_step_connect", "linkedin_step_message", "linkedin_step_view_profile", "linkedin_step_interact_post", or "action_item" |
| `status` | string | Yes | Task status: "scheduled", "completed", or "skipped" |
| `note` | string | No | Free-form note providing context for the task |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `task` | json | Created task data from Apollo |
| `created` | boolean | Whether the task was successfully created |
| `tasks` | json | Array of created tasks \(when returned by Apollo\) |
| `created` | boolean | Whether the request succeeded |
### `apollo_task_search`
@@ -560,9 +630,8 @@ Search for tasks in Apollo
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Apollo API key \(master key required\) |
| `contact_id` | string | No | Filter by contact ID \(e.g., "con_abc123"\) |
| `account_id` | string | No | Filter by account ID \(e.g., "acc_abc123"\) |
| `completed` | boolean | No | Filter by completion status |
| `sort_by_field` | string | No | Sort field: "task_due_at" or "task_priority" |
| `open_factor_names` | array | No | Filter by status. Common values: \["task_types"\] for open tasks, \["task_completed_at"\] for completed tasks. |
| `page` | number | No | Page number for pagination \(e.g., 1, 2, 3\) |
| `per_page` | number | No | Results per page, max 100 \(e.g., 25, 50, 100\) |
@@ -862,7 +862,7 @@
},
{
"name": "Bulk Create Accounts",
"description": "Create up to 100 accounts at once in your Apollo database. Note: Apollo does not apply deduplication - duplicate accounts may be created if entries share similar names or domains. Master key required."
"description": "Create up to 100 accounts at once in your Apollo database. Set run_dedupe=true to deduplicate by domain, organization_id, and name. Master key required."
},
{
"name": "Bulk Update Accounts",
@@ -894,7 +894,7 @@
},
{
"name": "Create Task",
"description": "Create a new task in Apollo"
"description": "Create one or more tasks in Apollo (one task per contact_id, master key required)"
},
{
"name": "Search Tasks",
File diff suppressed because it is too large Load Diff
@@ -40,6 +40,16 @@ export const SUBBLOCK_ID_MIGRATIONS: Record<string, Record<string, string>> = {
expandApplicationFormDefinition: '_removed_expandApplicationFormDefinition',
expandSurveyFormDefinitions: '_removed_expandSurveyFormDefinitions',
},
apollo: {
contact_ids_bulk: 'contacts',
account_ids_bulk: 'accounts',
close_date: 'closed_date',
stage_id: 'opportunity_stage_id',
note: 'task_notes',
description: '_removed_description',
stage_ids: '_removed_stage_ids',
owner_ids: '_removed_owner_ids',
},
rippling: {
action: '_removed_action',
candidateDepartment: '_removed_candidateDepartment',
+50 -13
View File
@@ -11,7 +11,7 @@ export const apolloAccountBulkCreateTool: ToolConfig<
id: 'apollo_account_bulk_create',
name: 'Apollo Bulk Create Accounts',
description:
'Create up to 100 accounts at once in your Apollo database. Note: Apollo does not apply deduplication - duplicate accounts may be created if entries share similar names or domains. Master key required.',
'Create up to 100 accounts at once in your Apollo database. Set run_dedupe=true to deduplicate by domain, organization_id, and name. Master key required.',
version: '1.0.0',
params: {
@@ -26,7 +26,20 @@ export const apolloAccountBulkCreateTool: ToolConfig<
required: true,
visibility: 'user-or-llm',
description:
'Array of accounts to create (max 100). Each account should include name (required), and optionally website_url, phone, owner_id',
'Array of accounts to create (max 100). Each account should include a name, and may optionally include domain, phone, phone_status_cd, raw_address, owner_id, linkedin_url, facebook_url, twitter_url, salesforce_id, and hubspot_id.',
},
append_label_names: {
type: 'array',
required: false,
visibility: 'user-only',
description: 'Array of label names to add to ALL accounts in this request',
},
run_dedupe: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description:
'When true, performs aggressive deduplication by domain, organization_id, and name (defaults to false)',
},
},
@@ -38,9 +51,16 @@ export const apolloAccountBulkCreateTool: ToolConfig<
'Cache-Control': 'no-cache',
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloAccountBulkCreateParams) => ({
accounts: params.accounts.slice(0, 100),
}),
body: (params: ApolloAccountBulkCreateParams) => {
const body: Record<string, unknown> = {
accounts: params.accounts.slice(0, 100),
}
if (params.append_label_names?.length) {
body.append_label_names = params.append_label_names
}
if (params.run_dedupe !== undefined) body.run_dedupe = params.run_dedupe
return body
},
},
transformResponse: async (response: Response) => {
@@ -50,15 +70,24 @@ export const apolloAccountBulkCreateTool: ToolConfig<
}
const data = await response.json()
const createdAccounts = Array.isArray(data.created_accounts)
? data.created_accounts
: Array.isArray(data.accounts)
? data.accounts
: []
const existingAccounts = Array.isArray(data.existing_accounts) ? data.existing_accounts : []
const failedAccounts = Array.isArray(data.failed_accounts) ? data.failed_accounts : []
return {
success: true,
output: {
created_accounts: data.accounts || data.created_accounts || [],
failed_accounts: data.failed_accounts || [],
total_submitted: data.accounts?.length || 0,
created: data.created_accounts?.length || data.accounts?.length || 0,
failed: data.failed_accounts?.length || 0,
created_accounts: createdAccounts,
existing_accounts: existingAccounts,
failed_accounts: failedAccounts,
total_submitted: createdAccounts.length + existingAccounts.length + failedAccounts.length,
created: createdAccounts.length,
existing: existingAccounts.length,
failed: failedAccounts.length,
},
}
},
@@ -68,21 +97,29 @@ export const apolloAccountBulkCreateTool: ToolConfig<
type: 'json',
description: 'Array of newly created accounts',
},
existing_accounts: {
type: 'json',
description: 'Array of existing accounts returned by Apollo (when duplicates are detected)',
},
failed_accounts: {
type: 'json',
description: 'Array of accounts that failed to create',
description: 'Array of accounts that failed to be created, with reasons for failure',
},
total_submitted: {
type: 'number',
description: 'Total number of accounts submitted',
description: 'Total number of accounts in the response (created + existing + failed)',
},
created: {
type: 'number',
description: 'Number of accounts successfully created',
},
existing: {
type: 'number',
description: 'Number of existing accounts found',
},
failed: {
type: 'number',
description: 'Number of accounts that failed to create',
description: 'Number of accounts that failed to be created',
},
},
}
+81 -29
View File
@@ -21,12 +21,38 @@ export const apolloAccountBulkUpdateTool: ToolConfig<
visibility: 'hidden',
description: 'Apollo API key (master key required)',
},
accounts: {
account_ids: {
type: 'array',
required: true,
required: false,
visibility: 'user-or-llm',
description:
'Array of accounts to update (max 1000). Each account must include id field, and optionally name, website_url, phone, owner_id',
'Array of account IDs to update with the same values (max 1000). Use with name/owner_id for uniform updates. Use either this OR account_attributes.',
},
name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'When using account_ids, apply this name to all accounts',
},
owner_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'When using account_ids, apply this owner to all accounts',
},
account_attributes: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description:
'Array of account objects with individual updates (each must include id). Example: [{"id": "acc1", "name": "Acme", "owner_id": "u1", "account_stage_id": "s1", "typed_custom_fields": {"field_id": "value"}}]',
},
async: {
type: 'boolean',
required: false,
visibility: 'user-only',
description:
'When true, processes the update asynchronously. Only supported when using account_ids; returns 422 if used with account_attributes.',
},
},
@@ -38,9 +64,49 @@ export const apolloAccountBulkUpdateTool: ToolConfig<
'Cache-Control': 'no-cache',
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloAccountBulkUpdateParams) => ({
accounts: params.accounts.slice(0, 1000),
}),
body: (params: ApolloAccountBulkUpdateParams) => {
const body: Record<string, unknown> = {}
if (params.account_ids && params.account_ids.length > 0) {
body.account_ids = params.account_ids.slice(0, 1000)
}
if (params.name) body.name = params.name
if (params.owner_id) body.owner_id = params.owner_id
if (params.account_attributes) {
if (Array.isArray(params.account_attributes)) {
if (params.account_attributes.length > 0) {
body.account_attributes = params.account_attributes.slice(0, 1000)
}
} else if (
typeof params.account_attributes === 'object' &&
Object.keys(params.account_attributes).length > 0
) {
body.account_attributes = params.account_attributes
}
}
const hasUpdateFields = body.account_attributes || body.name || body.owner_id
if (!hasUpdateFields) {
throw new Error(
'Apollo account bulk update requires update fields. Provide account_attributes (array of per-account updates with id, or single object paired with account_ids), or pair account_ids with name/owner_id to apply uniformly.'
)
}
if (!body.account_ids && !body.account_attributes) {
throw new Error(
'Apollo account bulk update requires account_ids (with name/owner_id) or account_attributes (with embedded ids).'
)
}
if (body.account_attributes && !Array.isArray(body.account_attributes) && !body.account_ids) {
throw new Error(
'Apollo account bulk update with object-form account_attributes requires account_ids to identify which accounts to update.'
)
}
if (body.account_ids && Array.isArray(body.account_attributes)) {
throw new Error(
'Apollo account bulk update cannot combine account_ids with array-form account_attributes. Use account_ids with name/owner_id (or object-form account_attributes), or use array-form account_attributes alone (each entry carries its own id).'
)
}
if (params.async !== undefined) body.async = params.async
return body
},
},
transformResponse: async (response: Response) => {
@@ -54,35 +120,21 @@ export const apolloAccountBulkUpdateTool: ToolConfig<
return {
success: true,
output: {
updated_accounts: data.accounts || data.updated_accounts || [],
failed_accounts: data.failed_accounts || [],
total_submitted: data.accounts?.length || 0,
updated: data.updated_accounts?.length || data.accounts?.length || 0,
failed: data.failed_accounts?.length || 0,
message: data.message ?? null,
account_ids: data.account_ids ?? [],
},
}
},
outputs: {
updated_accounts: {
message: {
type: 'string',
description: 'Confirmation message from Apollo',
optional: true,
},
account_ids: {
type: 'json',
description: 'Array of successfully updated accounts',
},
failed_accounts: {
type: 'json',
description: 'Array of accounts that failed to update',
},
total_submitted: {
type: 'number',
description: 'Total number of accounts submitted',
},
updated: {
type: 'number',
description: 'Number of accounts successfully updated',
},
failed: {
type: 'number',
description: 'Number of accounts that failed to update',
description: 'IDs of accounts that were updated',
},
},
}
+31 -9
View File
@@ -15,7 +15,7 @@ export const apolloAccountCreateTool: ToolConfig<
type: 'string',
required: true,
visibility: 'hidden',
description: 'Apollo API key',
description: 'Apollo API key (master key required)',
},
name: {
type: 'string',
@@ -23,23 +23,41 @@ export const apolloAccountCreateTool: ToolConfig<
visibility: 'user-or-llm',
description: 'Company name (e.g., "Acme Corporation")',
},
website_url: {
domain: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Company website URL',
description: 'Company domain without www. prefix (e.g., "acme.com")',
},
phone: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Company phone number',
description: 'Primary phone number for the account',
},
owner_id: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'User ID of the account owner',
description: 'Apollo user ID of the account owner',
},
account_stage_id: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Apollo ID for the account stage to assign this account to',
},
raw_address: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Corporate location (e.g., "San Francisco, CA, USA")',
},
typed_custom_fields: {
type: 'json',
required: false,
visibility: 'user-only',
description: 'Custom field values as { custom_field_id: value } map',
},
},
@@ -52,10 +70,13 @@ export const apolloAccountCreateTool: ToolConfig<
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloAccountCreateParams) => {
const body: any = { name: params.name }
if (params.website_url) body.website_url = params.website_url
const body: Record<string, unknown> = { name: params.name }
if (params.domain) body.domain = params.domain
if (params.phone) body.phone = params.phone
if (params.owner_id) body.owner_id = params.owner_id
if (params.account_stage_id) body.account_stage_id = params.account_stage_id
if (params.raw_address) body.raw_address = params.raw_address
if (params.typed_custom_fields) body.typed_custom_fields = params.typed_custom_fields
return body
},
},
@@ -67,12 +88,13 @@ export const apolloAccountCreateTool: ToolConfig<
}
const data = await response.json()
const account = data.account ?? (data.id ? data : null)
return {
success: true,
output: {
account: data.account ?? null,
created: !!data.account,
account,
created: !!account,
},
}
},
+29 -13
View File
@@ -18,17 +18,11 @@ export const apolloAccountSearchTool: ToolConfig<
visibility: 'hidden',
description: 'Apollo API key (master key required)',
},
q_keywords: {
q_organization_name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Keywords to search for in account data',
},
owner_id: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Filter by account owner user ID',
description: 'Filter accounts by organization name (partial-match search)',
},
account_stage_ids: {
type: 'array',
@@ -36,6 +30,25 @@ export const apolloAccountSearchTool: ToolConfig<
visibility: 'user-only',
description: 'Filter by account stage IDs',
},
account_label_ids: {
type: 'array',
required: false,
visibility: 'user-only',
description: 'Filter by account label IDs',
},
sort_by_field: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Sort field: "account_last_activity_date", "account_created_at", or "account_updated_at"',
},
sort_ascending: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Sort ascending when true. Defaults to descending.',
},
page: {
type: 'number',
required: false,
@@ -59,15 +72,19 @@ export const apolloAccountSearchTool: ToolConfig<
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloAccountSearchParams) => {
const body: any = {
const body: Record<string, unknown> = {
page: params.page || 1,
per_page: Math.min(params.per_page || 25, 100),
}
if (params.q_keywords) body.q_keywords = params.q_keywords
if (params.owner_id) body.owner_id = params.owner_id
if (params.q_organization_name) body.q_organization_name = params.q_organization_name
if (params.account_stage_ids?.length) {
body.account_stage_ids = params.account_stage_ids
}
if (params.account_label_ids?.length) {
body.account_label_ids = params.account_label_ids
}
if (params.sort_by_field) body.sort_by_field = params.sort_by_field
if (params.sort_ascending !== undefined) body.sort_ascending = params.sort_ascending
return body
},
},
@@ -83,7 +100,7 @@ export const apolloAccountSearchTool: ToolConfig<
return {
success: true,
output: {
accounts: data.accounts ?? null,
accounts: data.accounts ?? [],
pagination: data.pagination ?? null,
},
}
@@ -93,7 +110,6 @@ export const apolloAccountSearchTool: ToolConfig<
accounts: {
type: 'json',
description: 'Array of accounts matching the search criteria',
optional: true,
},
pagination: { type: 'json', description: 'Pagination information', optional: true },
},
+30 -8
View File
@@ -29,11 +29,11 @@ export const apolloAccountUpdateTool: ToolConfig<
visibility: 'user-or-llm',
description: 'Company name (e.g., "Acme Corporation")',
},
website_url: {
domain: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Company website URL',
description: 'Company domain (e.g., "acme.com")',
},
phone: {
type: 'string',
@@ -45,13 +45,31 @@ export const apolloAccountUpdateTool: ToolConfig<
type: 'string',
required: false,
visibility: 'user-only',
description: 'User ID of the account owner',
description: 'Apollo user ID of the account owner',
},
account_stage_id: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Apollo ID for the account stage to assign this account to',
},
raw_address: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Corporate location (e.g., "San Francisco, CA, USA")',
},
typed_custom_fields: {
type: 'json',
required: false,
visibility: 'user-only',
description: 'Custom field values as { custom_field_id: value } map',
},
},
request: {
url: (params: ApolloAccountUpdateParams) =>
`https://api.apollo.io/api/v1/accounts/${params.account_id}`,
`https://api.apollo.io/api/v1/accounts/${params.account_id.trim()}`,
method: 'PATCH',
headers: (params: ApolloAccountUpdateParams) => ({
'Content-Type': 'application/json',
@@ -59,11 +77,14 @@ export const apolloAccountUpdateTool: ToolConfig<
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloAccountUpdateParams) => {
const body: any = {}
const body: Record<string, unknown> = {}
if (params.name) body.name = params.name
if (params.website_url) body.website_url = params.website_url
if (params.domain) body.domain = params.domain
if (params.phone) body.phone = params.phone
if (params.owner_id) body.owner_id = params.owner_id
if (params.account_stage_id) body.account_stage_id = params.account_stage_id
if (params.raw_address) body.raw_address = params.raw_address
if (params.typed_custom_fields) body.typed_custom_fields = params.typed_custom_fields
return body
},
},
@@ -75,12 +96,13 @@ export const apolloAccountUpdateTool: ToolConfig<
}
const data = await response.json()
const account = data.account ?? (data.id ? data : null)
return {
success: true,
output: {
account: data.account ?? null,
updated: !!data.account,
account,
updated: !!account,
},
}
},
+18 -7
View File
@@ -26,7 +26,13 @@ export const apolloContactBulkCreateTool: ToolConfig<
required: true,
visibility: 'user-or-llm',
description:
'Array of contacts to create (max 100). Each contact should include first_name, last_name, and optionally email, title, account_id, owner_id',
'Array of contacts to create (max 100). Each contact may include first_name, last_name, email, title, organization_name, account_id, owner_id, contact_stage_id, linkedin_url, phone (single string) or phone_numbers (array of {raw_number, position}), contact_emails, typed_custom_fields, and CRM IDs (salesforce_contact_id, hubspot_id, team_id) for cross-system matching',
},
append_label_names: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Label names to add to all contacts in this request (e.g., ["Hot Lead"])',
},
run_dedupe: {
type: 'boolean',
@@ -46,12 +52,15 @@ export const apolloContactBulkCreateTool: ToolConfig<
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloContactBulkCreateParams) => {
const body: any = {
const body: Record<string, unknown> = {
contacts: params.contacts.slice(0, 100),
}
if (params.run_dedupe !== undefined) {
body.run_dedupe = params.run_dedupe
}
if (params.append_label_names && params.append_label_names.length > 0) {
body.append_label_names = params.append_label_names
}
return body
},
},
@@ -63,15 +72,17 @@ export const apolloContactBulkCreateTool: ToolConfig<
}
const data = await response.json()
const createdContacts = data.created_contacts || data.contacts || []
const existingContacts = data.existing_contacts || []
return {
success: true,
output: {
created_contacts: data.contacts || data.created_contacts || [],
existing_contacts: data.existing_contacts || [],
total_submitted: data.contacts?.length || 0,
created: data.created_contacts?.length || data.contacts?.length || 0,
existing: data.existing_contacts?.length || 0,
created_contacts: createdContacts,
existing_contacts: existingContacts,
total_submitted: createdContacts.length + existingContacts.length,
created: createdContacts.length,
existing: existingContacts.length,
},
}
},
+61 -29
View File
@@ -21,12 +21,25 @@ export const apolloContactBulkUpdateTool: ToolConfig<
visibility: 'hidden',
description: 'Apollo API key (master key required)',
},
contacts: {
contact_ids: {
type: 'array',
required: true,
required: false,
visibility: 'user-or-llm',
description:
'Array of contacts to update (max 100). Each contact must include id field, and optionally first_name, last_name, email, title, account_id, owner_id',
'Array of contact IDs to update. Must be paired with an object-form contact_attributes specifying the fields to apply uniformly to all listed contacts.',
},
contact_attributes: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description:
'Required. Either an array of per-contact updates (each with id) — used standalone — or a single object of attributes to apply to all contact_ids. Supported fields: owner_id, email, organization_name, title, first_name, last_name, account_id, present_raw_address, linkedin_url, typed_custom_fields',
},
async: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Force asynchronous processing. Automatically enabled for >100 contacts',
},
},
@@ -38,9 +51,41 @@ export const apolloContactBulkUpdateTool: ToolConfig<
'Cache-Control': 'no-cache',
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloContactBulkUpdateParams) => ({
contacts: params.contacts.slice(0, 100),
}),
body: (params: ApolloContactBulkUpdateParams) => {
const body: Record<string, unknown> = {}
if (params.contact_ids && params.contact_ids.length > 0) {
body.contact_ids = params.contact_ids.slice(0, 100)
}
if (params.contact_attributes) {
if (Array.isArray(params.contact_attributes)) {
if (params.contact_attributes.length > 0) {
body.contact_attributes = params.contact_attributes.slice(0, 100)
}
} else if (
typeof params.contact_attributes === 'object' &&
Object.keys(params.contact_attributes).length > 0
) {
body.contact_attributes = params.contact_attributes
}
}
if (!body.contact_attributes) {
throw new Error(
'Apollo bulk update requires contact_attributes (the fields to update). Use contact_attributes alone (array of per-contact updates with id) or together with contact_ids (single object applied to all listed contacts).'
)
}
if (!Array.isArray(body.contact_attributes) && !body.contact_ids) {
throw new Error(
'Apollo bulk update with object-form contact_attributes requires contact_ids to identify which contacts to update.'
)
}
if (body.contact_ids && Array.isArray(body.contact_attributes)) {
throw new Error(
'Apollo contact bulk update cannot combine contact_ids with array-form contact_attributes. Use contact_ids with object-form contact_attributes for uniform updates, or use array-form contact_attributes alone (each entry carries its own id).'
)
}
if (params.async !== undefined) body.async = params.async
return body
},
},
transformResponse: async (response: Response) => {
@@ -54,35 +99,22 @@ export const apolloContactBulkUpdateTool: ToolConfig<
return {
success: true,
output: {
updated_contacts: data.contacts || data.updated_contacts || [],
failed_contacts: data.failed_contacts || [],
total_submitted: data.contacts?.length || 0,
updated: data.updated_contacts?.length || data.contacts?.length || 0,
failed: data.failed_contacts?.length || 0,
message: data.message ?? null,
job_id: data.job_id ?? null,
},
}
},
outputs: {
updated_contacts: {
type: 'json',
description: 'Array of successfully updated contacts',
message: {
type: 'string',
description: 'Confirmation message from Apollo',
optional: true,
},
failed_contacts: {
type: 'json',
description: 'Array of contacts that failed to update',
},
total_submitted: {
type: 'number',
description: 'Total number of contacts submitted',
},
updated: {
type: 'number',
description: 'Number of contacts successfully updated',
},
failed: {
type: 'number',
description: 'Number of contacts that failed to update',
job_id: {
type: 'string',
description: 'Async job ID (returned for >100 contacts)',
optional: true,
},
},
}
+92 -4
View File
@@ -51,7 +51,80 @@ export const apolloContactCreateTool: ToolConfig<
type: 'string',
required: false,
visibility: 'user-only',
description: 'User ID of the contact owner',
description:
'User ID of the contact owner (accepted by Apollo but not officially documented for POST /contacts)',
},
organization_name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Name of the contact\'s employer (e.g., "Apollo")',
},
website_url: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Corporate website URL (e.g., "https://www.apollo.io/")',
},
label_names: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Lists/labels to add the contact to (e.g., ["Prospects"])',
},
contact_stage_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Apollo ID for the contact stage',
},
present_raw_address: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Personal location for the contact (e.g., "Atlanta, United States")',
},
direct_phone: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Primary phone number',
},
corporate_phone: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Work/office phone number',
},
mobile_phone: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Mobile phone number',
},
home_phone: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Home phone number',
},
other_phone: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Alternative phone number',
},
typed_custom_fields: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Custom field values keyed by custom field ID',
},
run_dedupe: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'When true, Apollo deduplicates against existing contacts',
},
},
@@ -64,7 +137,7 @@ export const apolloContactCreateTool: ToolConfig<
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloContactCreateParams) => {
const body: any = {
const body: Record<string, unknown> = {
first_name: params.first_name,
last_name: params.last_name,
}
@@ -72,6 +145,20 @@ export const apolloContactCreateTool: ToolConfig<
if (params.title) body.title = params.title
if (params.account_id) body.account_id = params.account_id
if (params.owner_id) body.owner_id = params.owner_id
if (params.organization_name) body.organization_name = params.organization_name
if (params.website_url) body.website_url = params.website_url
if (params.label_names && params.label_names.length > 0) {
body.label_names = params.label_names
}
if (params.contact_stage_id) body.contact_stage_id = params.contact_stage_id
if (params.present_raw_address) body.present_raw_address = params.present_raw_address
if (params.direct_phone) body.direct_phone = params.direct_phone
if (params.corporate_phone) body.corporate_phone = params.corporate_phone
if (params.mobile_phone) body.mobile_phone = params.mobile_phone
if (params.home_phone) body.home_phone = params.home_phone
if (params.other_phone) body.other_phone = params.other_phone
if (params.typed_custom_fields) body.typed_custom_fields = params.typed_custom_fields
if (params.run_dedupe !== undefined) body.run_dedupe = params.run_dedupe
return body
},
},
@@ -83,12 +170,13 @@ export const apolloContactCreateTool: ToolConfig<
}
const data = await response.json()
const contact = data?.contact ?? (data?.id ? data : null)
return {
success: true,
output: {
contact: data.contact ?? null,
created: !!data.contact,
contact,
created: !!contact,
},
}
},
+26 -3
View File
@@ -29,6 +29,25 @@ export const apolloContactSearchTool: ToolConfig<
visibility: 'user-only',
description: 'Filter by contact stage IDs',
},
contact_label_ids: {
type: 'array',
required: false,
visibility: 'user-only',
description: 'Filter by Apollo label IDs (lists)',
},
sort_by_field: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Sort field: contact_last_activity_date, contact_email_last_opened_at, contact_email_last_clicked_at, contact_created_at, or contact_updated_at',
},
sort_ascending: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'When true, sort ascending. Must be used together with sort_by_field',
},
page: {
type: 'number',
required: false,
@@ -52,7 +71,7 @@ export const apolloContactSearchTool: ToolConfig<
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloContactSearchParams) => {
const body: any = {
const body: Record<string, unknown> = {
page: params.page || 1,
per_page: Math.min(params.per_page || 25, 100),
}
@@ -60,6 +79,11 @@ export const apolloContactSearchTool: ToolConfig<
if (params.contact_stage_ids?.length) {
body.contact_stage_ids = params.contact_stage_ids
}
if (params.contact_label_ids?.length) {
body.contact_label_ids = params.contact_label_ids
}
if (params.sort_by_field) body.sort_by_field = params.sort_by_field
if (params.sort_ascending !== undefined) body.sort_ascending = params.sort_ascending
return body
},
},
@@ -75,7 +99,7 @@ export const apolloContactSearchTool: ToolConfig<
return {
success: true,
output: {
contacts: data.contacts ?? null,
contacts: data.contacts ?? [],
pagination: data.pagination ?? null,
},
}
@@ -85,7 +109,6 @@ export const apolloContactSearchTool: ToolConfig<
contacts: {
type: 'json',
description: 'Array of contacts matching the search criteria',
optional: true,
},
pagination: { type: 'json', description: 'Pagination information', optional: true },
},
+87 -5
View File
@@ -57,13 +57,81 @@ export const apolloContactUpdateTool: ToolConfig<
type: 'string',
required: false,
visibility: 'user-only',
description: 'User ID of the contact owner',
description:
'User ID of the contact owner (accepted by Apollo but not officially documented for PATCH /contacts/{id})',
},
organization_name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Name of the contact\'s employer (e.g., "Apollo")',
},
website_url: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Corporate website URL (e.g., "https://www.apollo.io/")',
},
label_names: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Lists/labels to add the contact to (e.g., ["Prospects"])',
},
contact_stage_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Apollo ID for the contact stage',
},
present_raw_address: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Personal location for the contact (e.g., "Atlanta, United States")',
},
direct_phone: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Primary phone number',
},
corporate_phone: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Work/office phone number',
},
mobile_phone: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Mobile phone number',
},
home_phone: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Home phone number',
},
other_phone: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Alternative phone number',
},
typed_custom_fields: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description:
'Custom field values keyed by custom field ID (accepted by Apollo but not officially documented for PATCH /contacts/{id})',
},
},
request: {
url: (params: ApolloContactUpdateParams) =>
`https://api.apollo.io/api/v1/contacts/${params.contact_id}`,
`https://api.apollo.io/api/v1/contacts/${params.contact_id.trim()}`,
method: 'PATCH',
headers: (params: ApolloContactUpdateParams) => ({
'Content-Type': 'application/json',
@@ -71,13 +139,26 @@ export const apolloContactUpdateTool: ToolConfig<
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloContactUpdateParams) => {
const body: any = {}
const body: Record<string, unknown> = {}
if (params.first_name) body.first_name = params.first_name
if (params.last_name) body.last_name = params.last_name
if (params.email) body.email = params.email
if (params.title) body.title = params.title
if (params.account_id) body.account_id = params.account_id
if (params.owner_id) body.owner_id = params.owner_id
if (params.organization_name) body.organization_name = params.organization_name
if (params.website_url) body.website_url = params.website_url
if (params.label_names && params.label_names.length > 0) {
body.label_names = params.label_names
}
if (params.contact_stage_id) body.contact_stage_id = params.contact_stage_id
if (params.present_raw_address) body.present_raw_address = params.present_raw_address
if (params.direct_phone) body.direct_phone = params.direct_phone
if (params.corporate_phone) body.corporate_phone = params.corporate_phone
if (params.mobile_phone) body.mobile_phone = params.mobile_phone
if (params.home_phone) body.home_phone = params.home_phone
if (params.other_phone) body.other_phone = params.other_phone
if (params.typed_custom_fields) body.typed_custom_fields = params.typed_custom_fields
return body
},
},
@@ -89,12 +170,13 @@ export const apolloContactUpdateTool: ToolConfig<
}
const data = await response.json()
const contact = data?.contact ?? (data?.id ? data : null)
return {
success: true,
output: {
contact: data.contact ?? null,
updated: !!data.contact,
contact,
updated: !!contact,
},
}
},
+3 -2
View File
@@ -36,12 +36,13 @@ export const apolloEmailAccountsTool: ToolConfig<
}
const data = await response.json()
const accounts = Array.isArray(data) ? data : data.email_accounts || data.data || []
return {
success: true,
output: {
email_accounts: data.email_accounts || [],
total: data.email_accounts?.length || 0,
email_accounts: accounts,
total: accounts.length,
},
}
},
+21 -20
View File
@@ -28,21 +28,21 @@ export const apolloOpportunityCreateTool: ToolConfig<
},
account_id: {
type: 'string',
required: true,
required: false,
visibility: 'user-or-llm',
description: 'ID of the account this opportunity belongs to (e.g., "acc_abc123")',
},
amount: {
type: 'number',
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Monetary value of the opportunity',
description: 'Monetary value as a plain number string with no commas or currency symbols',
},
stage_id: {
opportunity_stage_id: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'ID of the deal stage',
description: 'ID of the opportunity stage',
},
owner_id: {
type: 'string',
@@ -50,17 +50,17 @@ export const apolloOpportunityCreateTool: ToolConfig<
visibility: 'user-only',
description: 'User ID of the opportunity owner',
},
close_date: {
closed_date: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Expected close date (ISO 8601 format)',
description: 'Expected close date in YYYY-MM-DD format',
},
description: {
type: 'string',
typed_custom_fields: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Description or notes about the opportunity',
visibility: 'user-only',
description: 'Custom field values as { custom_field_id: value } map',
},
},
@@ -73,15 +73,15 @@ export const apolloOpportunityCreateTool: ToolConfig<
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloOpportunityCreateParams) => {
const body: any = {
name: params.name,
account_id: params.account_id,
const body: Record<string, unknown> = { name: params.name }
if (params.account_id) body.account_id = params.account_id
if (params.amount !== undefined && params.amount !== null && params.amount !== '') {
body.amount = String(params.amount)
}
if (params.amount !== undefined) body.amount = params.amount
if (params.stage_id) body.stage_id = params.stage_id
if (params.opportunity_stage_id) body.opportunity_stage_id = params.opportunity_stage_id
if (params.owner_id) body.owner_id = params.owner_id
if (params.close_date) body.close_date = params.close_date
if (params.description) body.description = params.description
if (params.closed_date) body.closed_date = params.closed_date
if (params.typed_custom_fields) body.typed_custom_fields = params.typed_custom_fields
return body
},
},
@@ -93,12 +93,13 @@ export const apolloOpportunityCreateTool: ToolConfig<
}
const data = await response.json()
const opportunity = data.opportunity ?? (data.id ? data : null)
return {
success: true,
output: {
opportunity: data.opportunity ?? null,
created: !!data.opportunity,
opportunity,
created: !!opportunity,
},
}
},
+7 -3
View File
@@ -27,7 +27,7 @@ export const apolloOpportunityGetTool: ToolConfig<
request: {
url: (params: ApolloOpportunityGetParams) =>
`https://api.apollo.io/api/v1/opportunities/${params.opportunity_id}`,
`https://api.apollo.io/api/v1/opportunities/${params.opportunity_id.trim()}`,
method: 'GET',
headers: (params: ApolloOpportunityGetParams) => ({
'Content-Type': 'application/json',
@@ -47,14 +47,18 @@ export const apolloOpportunityGetTool: ToolConfig<
return {
success: true,
output: {
opportunity: data.opportunity || {},
opportunity: data.opportunity ?? null,
found: !!data.opportunity,
},
}
},
outputs: {
opportunity: { type: 'json', description: 'Complete opportunity data from Apollo' },
opportunity: {
type: 'json',
description: 'Complete opportunity data from Apollo',
optional: true,
},
found: { type: 'boolean', description: 'Whether the opportunity was found' },
},
}
+14 -38
View File
@@ -20,29 +20,11 @@ export const apolloOpportunitySearchTool: ToolConfig<
visibility: 'hidden',
description: 'Apollo API key',
},
q_keywords: {
sort_by_field: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Keywords to search for in opportunity names',
},
account_ids: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Filter by specific account IDs (e.g., ["acc_123", "acc_456"])',
},
stage_ids: {
type: 'array',
required: false,
visibility: 'user-only',
description: 'Filter by deal stage IDs',
},
owner_ids: {
type: 'array',
required: false,
visibility: 'user-only',
description: 'Filter by opportunity owner IDs',
description: 'Sort field: "amount", "is_closed", or "is_won"',
},
page: {
type: 'number',
@@ -59,24 +41,18 @@ export const apolloOpportunitySearchTool: ToolConfig<
},
request: {
url: 'https://api.apollo.io/api/v1/opportunities/search',
method: 'POST',
url: (params: ApolloOpportunitySearchParams) => {
const query = new URLSearchParams()
query.set('page', String(params.page || 1))
query.set('per_page', String(Math.min(params.per_page || 25, 100)))
if (params.sort_by_field) query.set('sort_by_field', params.sort_by_field)
return `https://api.apollo.io/api/v1/opportunities/search?${query.toString()}`
},
method: 'GET',
headers: (params: ApolloOpportunitySearchParams) => ({
'Content-Type': 'application/json',
'Cache-Control': 'no-cache',
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloOpportunitySearchParams) => {
const body: any = {
page: params.page || 1,
per_page: Math.min(params.per_page || 25, 100),
}
if (params.q_keywords) body.q_keywords = params.q_keywords
if (params.account_ids?.length) body.account_ids = params.account_ids
if (params.stage_ids?.length) body.stage_ids = params.stage_ids
if (params.owner_ids?.length) body.owner_ids = params.owner_ids
return body
},
},
transformResponse: async (response: Response) => {
@@ -90,10 +66,10 @@ export const apolloOpportunitySearchTool: ToolConfig<
return {
success: true,
output: {
opportunities: data.opportunities || [],
page: data.pagination?.page || 1,
per_page: data.pagination?.per_page || 25,
total_entries: data.pagination?.total_entries || 0,
opportunities: data.opportunities ?? [],
page: data.pagination?.page ?? 1,
per_page: data.pagination?.per_page ?? 25,
total_entries: data.pagination?.total_entries ?? 0,
},
}
},
+21 -18
View File
@@ -33,16 +33,16 @@ export const apolloOpportunityUpdateTool: ToolConfig<
description: 'Name of the opportunity/deal (e.g., "Enterprise License - Q1")',
},
amount: {
type: 'number',
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Monetary value of the opportunity',
description: 'Monetary value as a plain number string with no commas or currency symbols',
},
stage_id: {
opportunity_stage_id: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'ID of the deal stage',
description: 'ID of the opportunity stage',
},
owner_id: {
type: 'string',
@@ -50,23 +50,23 @@ export const apolloOpportunityUpdateTool: ToolConfig<
visibility: 'user-only',
description: 'User ID of the opportunity owner',
},
close_date: {
closed_date: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Expected close date (ISO 8601 format)',
description: 'Expected close date in YYYY-MM-DD format',
},
description: {
type: 'string',
typed_custom_fields: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Description or notes about the opportunity',
visibility: 'user-only',
description: 'Custom field values as { custom_field_id: value } map',
},
},
request: {
url: (params: ApolloOpportunityUpdateParams) =>
`https://api.apollo.io/api/v1/opportunities/${params.opportunity_id}`,
`https://api.apollo.io/api/v1/opportunities/${params.opportunity_id.trim()}`,
method: 'PATCH',
headers: (params: ApolloOpportunityUpdateParams) => ({
'Content-Type': 'application/json',
@@ -74,13 +74,15 @@ export const apolloOpportunityUpdateTool: ToolConfig<
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloOpportunityUpdateParams) => {
const body: any = {}
const body: Record<string, unknown> = {}
if (params.name) body.name = params.name
if (params.amount !== undefined) body.amount = params.amount
if (params.stage_id) body.stage_id = params.stage_id
if (params.amount !== undefined && params.amount !== null && params.amount !== '') {
body.amount = String(params.amount)
}
if (params.opportunity_stage_id) body.opportunity_stage_id = params.opportunity_stage_id
if (params.owner_id) body.owner_id = params.owner_id
if (params.close_date) body.close_date = params.close_date
if (params.description) body.description = params.description
if (params.closed_date) body.closed_date = params.closed_date
if (params.typed_custom_fields) body.typed_custom_fields = params.typed_custom_fields
return body
},
},
@@ -92,12 +94,13 @@ export const apolloOpportunityUpdateTool: ToolConfig<
}
const data = await response.json()
const opportunity = data.opportunity ?? (data.id ? data : null)
return {
success: true,
output: {
opportunity: data.opportunity ?? null,
updated: !!data.opportunity,
opportunity,
updated: !!opportunity,
},
}
},
@@ -24,7 +24,8 @@ export const apolloOrganizationBulkEnrichTool: ToolConfig<
type: 'array',
required: true,
visibility: 'user-or-llm',
description: 'Array of organizations to enrich (max 10)',
description:
'Array of organizations to enrich (max 10). Each item requires `name` and may include `domain` (e.g., [{"name": "Example Corp", "domain": "example.com"}])',
},
},
@@ -37,7 +38,7 @@ export const apolloOrganizationBulkEnrichTool: ToolConfig<
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloOrganizationBulkEnrichParams) => ({
details: params.organizations.slice(0, 10),
organizations: params.organizations.slice(0, 10),
}),
},
@@ -48,20 +49,28 @@ export const apolloOrganizationBulkEnrichTool: ToolConfig<
}
const data = await response.json()
const organizations = data.organizations ?? []
return {
success: true,
output: {
organizations: data.matches || [],
total: data.matches?.length || 0,
enriched: data.matches?.filter((o: any) => o).length || 0,
organizations,
total: data.total_requested_domains ?? organizations.length,
enriched: data.unique_enriched_records ?? organizations.length,
missing_records: data.missing_records ?? 0,
unique_domains: data.unique_domains ?? organizations.length,
},
}
},
outputs: {
organizations: { type: 'json', description: 'Array of enriched organization data' },
total: { type: 'number', description: 'Total number of organizations processed' },
enriched: { type: 'number', description: 'Number of organizations successfully enriched' },
total: { type: 'number', description: 'Total number of domains requested' },
enriched: { type: 'number', description: 'Number of unique enriched records' },
missing_records: {
type: 'number',
description: 'Number of domains that could not be enriched',
},
unique_domains: { type: 'number', description: 'Number of unique domains processed' },
},
}
+12 -22
View File
@@ -20,19 +20,11 @@ export const apolloOrganizationEnrichTool: ToolConfig<
visibility: 'hidden',
description: 'Apollo API key',
},
organization_name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Name of the organization (e.g., "Acme Corporation") - at least one of organization_name or domain is required',
},
domain: {
type: 'string',
required: false,
required: true,
visibility: 'user-or-llm',
description:
'Company domain (e.g., "apollo.io", "acme.com") - at least one of domain or organization_name is required',
description: 'Company domain (e.g., "apollo.io", "acme.com")',
},
},
@@ -45,17 +37,11 @@ export const apolloOrganizationEnrichTool: ToolConfig<
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloOrganizationEnrichParams) => {
// At least one identifier is required
if (!params.organization_name && !params.domain) {
throw new Error(
'At least one of organization_name or domain is required for organization enrichment'
)
const domain = params.domain?.trim()
if (!domain) {
throw new Error('domain is required for organization enrichment')
}
const body: any = {}
if (params.organization_name) body.name = params.organization_name
if (params.domain) body.domain = params.domain
return body
return { domain }
},
},
@@ -70,14 +56,18 @@ export const apolloOrganizationEnrichTool: ToolConfig<
return {
success: true,
output: {
organization: data.organization || {},
organization: data.organization ?? null,
enriched: !!data.organization,
},
}
},
outputs: {
organization: { type: 'json', description: 'Enriched organization data from Apollo' },
organization: {
type: 'json',
description: 'Enriched organization data from Apollo',
optional: true,
},
enriched: {
type: 'boolean',
description: 'Whether the organization was successfully enriched',
+31 -3
View File
@@ -24,13 +24,20 @@ export const apolloOrganizationSearchTool: ToolConfig<
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Company locations to search',
description: 'Company HQ locations (cities, US states, or countries)',
},
organization_not_locations: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Exclude companies whose HQ is in these locations',
},
organization_num_employees_ranges: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Employee count ranges (e.g., ["1-10", "11-50"])',
description:
'Employee count ranges as "min,max" strings (e.g., ["1,10", "250,500", "10000,20000"])',
},
q_organization_keyword_tags: {
type: 'array',
@@ -44,6 +51,18 @@ export const apolloOrganizationSearchTool: ToolConfig<
visibility: 'user-or-llm',
description: 'Organization name to search for (e.g., "Acme", "TechCorp")',
},
organization_ids: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Apollo organization IDs to include (e.g., ["5e66b6381e05b4008c8331b8"])',
},
q_organization_domains_list: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Domain names to filter by (no www. or @, up to 1,000)',
},
page: {
type: 'number',
required: false,
@@ -67,7 +86,7 @@ export const apolloOrganizationSearchTool: ToolConfig<
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloOrganizationSearchParams) => {
const body: any = {
const body: Record<string, unknown> = {
page: params.page || 1,
per_page: Math.min(params.per_page || 25, 100),
}
@@ -75,6 +94,9 @@ export const apolloOrganizationSearchTool: ToolConfig<
if (params.organization_locations?.length) {
body.organization_locations = params.organization_locations
}
if (params.organization_not_locations?.length) {
body.organization_not_locations = params.organization_not_locations
}
if (params.organization_num_employees_ranges?.length) {
body.organization_num_employees_ranges = params.organization_num_employees_ranges
}
@@ -84,6 +106,12 @@ export const apolloOrganizationSearchTool: ToolConfig<
if (params.q_organization_name) {
body.q_organization_name = params.q_organization_name
}
if (params.organization_ids?.length) {
body.organization_ids = params.organization_ids
}
if (params.q_organization_domains_list?.length) {
body.q_organization_domains_list = params.q_organization_domains_list
}
return body
},
+54 -10
View File
@@ -36,12 +36,32 @@ export const apolloPeopleBulkEnrichTool: ToolConfig<
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Reveal phone numbers (uses credits)',
description: 'Reveal phone numbers (uses credits, requires webhook_url)',
},
webhook_url: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Webhook URL for async phone number delivery (required when reveal_phone_number is true)',
},
},
request: {
url: 'https://api.apollo.io/api/v1/people/bulk_match',
url: (params: ApolloPeopleBulkEnrichParams) => {
const qs = new URLSearchParams()
if (params.reveal_personal_emails !== undefined) {
qs.set('reveal_personal_emails', String(params.reveal_personal_emails))
}
if (params.reveal_phone_number !== undefined) {
qs.set('reveal_phone_number', String(params.reveal_phone_number))
}
if (params.webhook_url) {
qs.set('webhook_url', params.webhook_url)
}
const query = qs.toString()
return `https://api.apollo.io/api/v1/people/bulk_match${query ? `?${query}` : ''}`
},
method: 'POST',
headers: (params: ApolloPeopleBulkEnrichParams) => ({
'Content-Type': 'application/json',
@@ -50,8 +70,6 @@ export const apolloPeopleBulkEnrichTool: ToolConfig<
}),
body: (params: ApolloPeopleBulkEnrichParams) => ({
details: params.people.slice(0, 10),
reveal_personal_emails: params.reveal_personal_emails,
reveal_phone_number: params.reveal_phone_number,
}),
},
@@ -62,20 +80,46 @@ export const apolloPeopleBulkEnrichTool: ToolConfig<
}
const data = await response.json()
const matches = Array.isArray(data.matches)
? data.matches
: Array.isArray(data.people)
? data.people
: []
return {
success: true,
output: {
people: data.matches || [],
total: data.matches?.length || 0,
enriched: data.matches?.filter((p: any) => p).length || 0,
matches,
total_requested_enrichments: data.total_requested_enrichments ?? matches.length,
unique_enriched_records: data.unique_enriched_records ?? matches.filter(Boolean).length,
missing_records: data.missing_records ?? null,
credits_consumed: data.credits_consumed ?? null,
},
}
},
outputs: {
people: { type: 'json', description: 'Array of enriched people data' },
total: { type: 'number', description: 'Total number of people processed' },
enriched: { type: 'number', description: 'Number of people successfully enriched' },
matches: {
type: 'json',
description: 'Array of enriched people (null entries indicate no match)',
},
total_requested_enrichments: {
type: 'number',
description: 'Total number of records submitted for enrichment',
},
unique_enriched_records: {
type: 'number',
description: 'Number of records successfully enriched',
},
missing_records: {
type: 'number',
description: 'Number of records that could not be enriched',
optional: true,
},
credits_consumed: {
type: 'number',
description: 'Number of Apollo credits consumed by this request',
optional: true,
},
},
}
+50 -11
View File
@@ -29,6 +29,24 @@ export const apolloPeopleEnrichTool: ToolConfig<
visibility: 'user-or-llm',
description: 'Last name of the person',
},
name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Full name of the person (alternative to first_name/last_name)',
},
id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Apollo ID for the person',
},
hashed_email: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'MD5 or SHA-256 hashed email',
},
email: {
type: 'string',
required: false,
@@ -63,12 +81,32 @@ export const apolloPeopleEnrichTool: ToolConfig<
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Reveal phone numbers (uses credits)',
description: 'Reveal phone numbers (uses credits, requires webhook_url)',
},
webhook_url: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Webhook URL for async phone number delivery (required when reveal_phone_number is true)',
},
},
request: {
url: 'https://api.apollo.io/api/v1/people/match',
url: (params: ApolloPeopleEnrichParams) => {
const qs = new URLSearchParams()
if (params.reveal_personal_emails !== undefined) {
qs.set('reveal_personal_emails', String(params.reveal_personal_emails))
}
if (params.reveal_phone_number !== undefined) {
qs.set('reveal_phone_number', String(params.reveal_phone_number))
}
if (params.webhook_url) {
qs.set('webhook_url', params.webhook_url)
}
const query = qs.toString()
return `https://api.apollo.io/api/v1/people/match${query ? `?${query}` : ''}`
},
method: 'POST',
headers: (params: ApolloPeopleEnrichParams) => ({
'Content-Type': 'application/json',
@@ -76,20 +114,17 @@ export const apolloPeopleEnrichTool: ToolConfig<
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloPeopleEnrichParams) => {
const body: any = {}
const body: Record<string, unknown> = {}
if (params.first_name) body.first_name = params.first_name
if (params.last_name) body.last_name = params.last_name
if (params.name) body.name = params.name
if (params.email) body.email = params.email
if (params.hashed_email) body.hashed_email = params.hashed_email
if (params.id) body.id = params.id
if (params.organization_name) body.organization_name = params.organization_name
if (params.domain) body.domain = params.domain
if (params.linkedin_url) body.linkedin_url = params.linkedin_url
if (params.reveal_personal_emails !== undefined) {
body.reveal_personal_emails = params.reveal_personal_emails
}
if (params.reveal_phone_number !== undefined) {
body.reveal_phone_number = params.reveal_phone_number
}
return body
},
@@ -106,14 +141,18 @@ export const apolloPeopleEnrichTool: ToolConfig<
return {
success: true,
output: {
person: data.person || {},
person: data.person ?? null,
enriched: !!data.person,
},
}
},
outputs: {
person: { type: 'json', description: 'Enriched person data from Apollo' },
person: {
type: 'json',
description: 'Enriched person data from Apollo',
optional: true,
},
enriched: { type: 'boolean', description: 'Whether the person was successfully enriched' },
},
}
+65 -3
View File
@@ -23,6 +23,12 @@ export const apolloPeopleSearchTool: ToolConfig<
visibility: 'user-or-llm',
description: 'Job titles to search for (e.g., ["CEO", "VP of Sales"])',
},
include_similar_titles: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to return people with job titles similar to person_titles',
},
person_locations: {
type: 'array',
required: false,
@@ -33,13 +39,48 @@ export const apolloPeopleSearchTool: ToolConfig<
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Seniority levels (e.g., ["senior", "executive", "manager"])',
description:
'Seniority levels (one of: owner, founder, c_suite, partner, vp, head, director, manager, senior, entry, intern)',
},
organization_ids: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Apollo organization IDs to filter by (e.g., ["5e66b6381e05b4008c8331b8"])',
},
organization_names: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Company names to search within',
description: 'Company names to search within (legacy filter)',
},
organization_locations: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description:
"Headquarters locations of the people's current employer (e.g., ['texas', 'tokyo', 'spain'])",
},
q_organization_domains_list: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description:
'Employer domain names (e.g., ["apollo.io", "microsoft.com"]) — up to 1,000, no www. or @',
},
organization_num_employees_ranges: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description:
'Employee count ranges for the person\'s current employer. Each entry is "min,max" (e.g., ["1,10", "250,500", "10000,20000"])',
},
contact_email_status: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description:
'Email statuses to filter by: "verified", "unverified", "likely to engage", "unavailable"',
},
q_keywords: {
type: 'string',
@@ -70,7 +111,7 @@ export const apolloPeopleSearchTool: ToolConfig<
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloPeopleSearchParams) => {
const body: any = {
const body: Record<string, unknown> = {
page: params.page || 1,
per_page: Math.min(params.per_page || 25, 100),
}
@@ -78,15 +119,36 @@ export const apolloPeopleSearchTool: ToolConfig<
if (params.person_titles && params.person_titles.length > 0) {
body.person_titles = params.person_titles
}
if (params.include_similar_titles !== undefined) {
body.include_similar_titles = params.include_similar_titles
}
if (params.person_locations && params.person_locations.length > 0) {
body.person_locations = params.person_locations
}
if (params.person_seniorities && params.person_seniorities.length > 0) {
body.person_seniorities = params.person_seniorities
}
if (params.organization_ids && params.organization_ids.length > 0) {
body.organization_ids = params.organization_ids
}
if (params.organization_names && params.organization_names.length > 0) {
body.organization_names = params.organization_names
}
if (params.organization_locations && params.organization_locations.length > 0) {
body.organization_locations = params.organization_locations
}
if (params.q_organization_domains_list && params.q_organization_domains_list.length > 0) {
body.q_organization_domains_list = params.q_organization_domains_list
}
if (
params.organization_num_employees_ranges &&
params.organization_num_employees_ranges.length > 0
) {
body.organization_num_employees_ranges = params.organization_num_employees_ranges
}
if (params.contact_email_status && params.contact_email_status.length > 0) {
body.contact_email_status = params.contact_email_status
}
if (params.q_keywords) {
body.q_keywords = params.q_keywords
}
+168 -16
View File
@@ -28,28 +28,108 @@ export const apolloSequenceAddContactsTool: ToolConfig<
},
contact_ids: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description:
'Array of contact IDs to add to the sequence (e.g., ["con_abc123", "con_def456"]). Either contact_ids or label_names must be provided.',
},
label_names: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description:
'Array of label names to identify contacts to add to the sequence. Either contact_ids or label_names must be provided.',
},
send_email_from_email_account_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'Array of contact IDs to add to the sequence (e.g., ["con_abc123", "con_def456"])',
'ID of the email account to send from. Use the Get Email Accounts operation to look this up.',
},
emailer_campaign_id: {
send_email_from_email_address: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Optional emailer campaign ID',
description: 'Specific email address to send from within the email account.',
},
send_email_from_user_id: {
sequence_no_email: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Add contacts even if they have no email address',
},
sequence_unverified_email: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Add contacts with unverified email addresses',
},
sequence_job_change: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Add contacts who recently changed jobs',
},
sequence_active_in_other_campaigns: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Add contacts active in other campaigns',
},
sequence_finished_in_other_campaigns: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Add contacts who finished other campaigns',
},
sequence_same_company_in_same_campaign: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Add contacts even if others from the same company are in the sequence',
},
contacts_without_ownership_permission: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Add contacts without ownership permission',
},
add_if_in_queue: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Add contacts even if they are in the queue',
},
contact_verification_skipped: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Skip contact verification when adding',
},
user_id: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'User ID to send emails from',
description: 'ID of the user performing the action',
},
status: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Initial status for added contacts: "active" or "paused"',
},
auto_unpause_at: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'ISO 8601 datetime to automatically unpause contacts',
},
},
request: {
url: (params: ApolloSequenceAddContactsParams) =>
`https://api.apollo.io/api/v1/emailer_campaigns/${params.sequence_id}/add_contact_ids`,
`https://api.apollo.io/api/v1/emailer_campaigns/${params.sequence_id.trim()}/add_contact_ids`,
method: 'POST',
headers: (params: ApolloSequenceAddContactsParams) => ({
'Content-Type': 'application/json',
@@ -57,15 +137,48 @@ export const apolloSequenceAddContactsTool: ToolConfig<
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloSequenceAddContactsParams) => {
const body: any = {
contact_ids: params.contact_ids,
const hasContactIds = !!params.contact_ids?.length
const hasLabelNames = !!params.label_names?.length
if (!hasContactIds && !hasLabelNames) {
throw new Error(
'Apollo sequence add requires either contact_ids or label_names to be provided'
)
}
if (params.emailer_campaign_id) {
body.emailer_campaign_id = params.emailer_campaign_id
const body: Record<string, unknown> = {
emailer_campaign_id: params.sequence_id,
send_email_from_email_account_id: params.send_email_from_email_account_id,
}
if (params.send_email_from_user_id) {
body.send_email_from_user_id = params.send_email_from_user_id
if (hasContactIds) body.contact_ids = params.contact_ids
if (hasLabelNames) body.label_names = params.label_names
if (params.send_email_from_email_address) {
body.send_email_from_email_address = params.send_email_from_email_address
}
if (params.sequence_no_email !== undefined) body.sequence_no_email = params.sequence_no_email
if (params.sequence_unverified_email !== undefined) {
body.sequence_unverified_email = params.sequence_unverified_email
}
if (params.sequence_job_change !== undefined) {
body.sequence_job_change = params.sequence_job_change
}
if (params.sequence_active_in_other_campaigns !== undefined) {
body.sequence_active_in_other_campaigns = params.sequence_active_in_other_campaigns
}
if (params.sequence_finished_in_other_campaigns !== undefined) {
body.sequence_finished_in_other_campaigns = params.sequence_finished_in_other_campaigns
}
if (params.sequence_same_company_in_same_campaign !== undefined) {
body.sequence_same_company_in_same_campaign = params.sequence_same_company_in_same_campaign
}
if (params.contacts_without_ownership_permission !== undefined) {
body.contacts_without_ownership_permission = params.contacts_without_ownership_permission
}
if (params.add_if_in_queue !== undefined) body.add_if_in_queue = params.add_if_in_queue
if (params.contact_verification_skipped !== undefined) {
body.contact_verification_skipped = params.contact_verification_skipped
}
if (params.user_id) body.user_id = params.user_id
if (params.status) body.status = params.status
if (params.auto_unpause_at) body.auto_unpause_at = params.auto_unpause_at
return body
},
},
@@ -78,19 +191,58 @@ export const apolloSequenceAddContactsTool: ToolConfig<
const data = await response.json()
// Apollo's response shape for this endpoint varies: some payloads return a flat
// `contacts: [...]` array of successfully added contacts, others wrap under
// `contacts: { added, skipped }`. Handle both defensively.
const contactsField = data?.contacts
const added = Array.isArray(contactsField)
? contactsField
: Array.isArray(contactsField?.added)
? contactsField.added
: []
const skipped = Array.isArray(contactsField?.skipped) ? contactsField.skipped : []
const rawSkippedIds = data?.skipped_contact_ids
const skippedIds =
Array.isArray(rawSkippedIds) || (rawSkippedIds && typeof rawSkippedIds === 'object')
? rawSkippedIds
: null
return {
success: true,
output: {
contacts_added: data.contacts || params?.contact_ids || [],
sequence_id: params?.sequence_id || '',
total_added: data.contacts?.length || params?.contact_ids?.length || 0,
added,
skipped,
skipped_contact_ids: skippedIds,
emailer_campaign: data?.emailer_campaign ?? null,
sequence_id: params?.sequence_id || data?.emailer_campaign?.id || '',
total_added: added.length,
total_skipped: skipped.length,
},
}
},
outputs: {
contacts_added: { type: 'json', description: 'Array of contact IDs added to the sequence' },
added: {
type: 'json',
description: 'Array of contact objects successfully added to the sequence',
},
skipped: {
type: 'json',
description: 'Array of contact objects that were skipped, with reasons',
},
skipped_contact_ids: {
type: 'json',
description:
'Skipped contact IDs — either an array of IDs or a hash mapping ID → reason code',
optional: true,
},
emailer_campaign: {
type: 'json',
description: 'Details of the emailer campaign (id, name)',
optional: true,
},
sequence_id: { type: 'string', description: 'ID of the sequence contacts were added to' },
total_added: { type: 'number', description: 'Total number of contacts added' },
total_skipped: { type: 'number', description: 'Total number of contacts skipped' },
},
}
+1 -8
View File
@@ -23,12 +23,6 @@ export const apolloSequenceSearchTool: ToolConfig<
visibility: 'user-or-llm',
description: 'Search sequences by name (e.g., "Outbound Q1", "Follow-up")',
},
active: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Filter by active status (true for active sequences, false for inactive)',
},
page: {
type: 'number',
required: false,
@@ -52,12 +46,11 @@ export const apolloSequenceSearchTool: ToolConfig<
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloSequenceSearchParams) => {
const body: any = {
const body: Record<string, unknown> = {
page: params.page || 1,
per_page: Math.min(params.per_page || 25, 100),
}
if (params.q_name) body.q_name = params.q_name
if (params.active !== undefined) body.active = params.active
return body
},
},
+45 -36
View File
@@ -4,7 +4,7 @@ import type { ToolConfig } from '@/tools/types'
export const apolloTaskCreateTool: ToolConfig<ApolloTaskCreateParams, ApolloTaskCreateResponse> = {
id: 'apollo_task_create',
name: 'Apollo Create Task',
description: 'Create a new task in Apollo',
description: 'Create one or more tasks in Apollo (one task per contact_id, master key required)',
version: '1.0.0',
params: {
@@ -14,41 +14,48 @@ export const apolloTaskCreateTool: ToolConfig<ApolloTaskCreateParams, ApolloTask
visibility: 'hidden',
description: 'Apollo API key (master key required)',
},
note: {
user_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Task note/description',
description: 'ID of the Apollo user the task is assigned to',
},
contact_id: {
type: 'string',
required: false,
contact_ids: {
type: 'array',
required: true,
visibility: 'user-or-llm',
description: 'Contact ID to associate with (e.g., "con_abc123")',
},
account_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Account ID to associate with (e.g., "acc_abc123")',
},
due_at: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Due date in ISO format',
description: 'Array of contact IDs. One task is created per contact.',
},
priority: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Task priority',
visibility: 'user-or-llm',
description: 'Task priority: "high", "medium", or "low" (defaults to "medium")',
},
due_at: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Due date/time in ISO 8601 format (e.g., "2024-12-31T23:59:59Z")',
},
type: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'Task type: "call", "outreach_manual_email", "linkedin_step_connect", "linkedin_step_message", "linkedin_step_view_profile", "linkedin_step_interact_post", or "action_item"',
},
status: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Task status: "scheduled", "completed", or "skipped"',
},
note: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Task type',
visibility: 'user-or-llm',
description: 'Free-form note providing context for the task',
},
},
@@ -61,12 +68,15 @@ export const apolloTaskCreateTool: ToolConfig<ApolloTaskCreateParams, ApolloTask
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloTaskCreateParams) => {
const body: any = { note: params.note }
if (params.contact_id) body.contact_id = params.contact_id
if (params.account_id) body.account_id = params.account_id
if (params.due_at) body.due_at = params.due_at
if (params.priority) body.priority = params.priority
if (params.type) body.type = params.type
const body: Record<string, unknown> = {
user_id: params.user_id,
contact_ids: params.contact_ids,
priority: params.priority || 'medium',
due_at: params.due_at,
type: params.type,
status: params.status,
}
if (params.note) body.note = params.note
return body
},
},
@@ -77,21 +87,20 @@ export const apolloTaskCreateTool: ToolConfig<ApolloTaskCreateParams, ApolloTask
throw new Error(`Apollo API error: ${response.status} - ${errorText}`)
}
const data = await response.json()
const data = await response.json().catch(() => null)
const tasks = Array.isArray(data?.tasks) ? data.tasks : []
// Apollo's task creation endpoint currently only returns true, not the task object
// Return the request params as the task data since the API doesn't return it
return {
success: true,
output: {
task: data.task ?? null,
created: data === true || !!data.task,
tasks,
created: true,
},
}
},
outputs: {
task: { type: 'json', description: 'Created task data from Apollo', optional: true },
created: { type: 'boolean', description: 'Whether the task was successfully created' },
tasks: { type: 'json', description: 'Array of created tasks (when returned by Apollo)' },
created: { type: 'boolean', description: 'Whether the request succeeded' },
},
}
+10 -17
View File
@@ -14,23 +14,18 @@ export const apolloTaskSearchTool: ToolConfig<ApolloTaskSearchParams, ApolloTask
visibility: 'hidden',
description: 'Apollo API key (master key required)',
},
contact_id: {
sort_by_field: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by contact ID (e.g., "con_abc123")',
description: 'Sort field: "task_due_at" or "task_priority"',
},
account_id: {
type: 'string',
open_factor_names: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Filter by account ID (e.g., "acc_abc123")',
},
completed: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Filter by completion status',
description:
'Filter by status. Common values: ["task_types"] for open tasks, ["task_completed_at"] for completed tasks.',
},
page: {
type: 'number',
@@ -55,13 +50,12 @@ export const apolloTaskSearchTool: ToolConfig<ApolloTaskSearchParams, ApolloTask
'X-Api-Key': params.apiKey,
}),
body: (params: ApolloTaskSearchParams) => {
const body: any = {
const body: Record<string, unknown> = {
page: params.page || 1,
per_page: Math.min(params.per_page || 25, 100),
}
if (params.contact_id) body.contact_id = params.contact_id
if (params.account_id) body.account_id = params.account_id
if (params.completed !== undefined) body.completed = params.completed
if (params.sort_by_field) body.sort_by_field = params.sort_by_field
if (params.open_factor_names?.length) body.open_factor_names = params.open_factor_names
return body
},
},
@@ -77,7 +71,7 @@ export const apolloTaskSearchTool: ToolConfig<ApolloTaskSearchParams, ApolloTask
return {
success: true,
output: {
tasks: data.tasks ?? null,
tasks: data.tasks ?? [],
pagination: data.pagination ?? null,
},
}
@@ -87,7 +81,6 @@ export const apolloTaskSearchTool: ToolConfig<ApolloTaskSearchParams, ApolloTask
tasks: {
type: 'json',
description: 'Array of tasks matching the search criteria',
optional: true,
},
pagination: { type: 'json', description: 'Pagination information', optional: true },
},
+231 -95
View File
@@ -42,32 +42,74 @@ export interface ApolloContact {
export interface ApolloAccount {
id: string
name: string
website_url?: string
phone?: string
owner_id?: string
domain?: string | null
website_url?: string | null
phone?: string | null
sanitized_phone?: string | null
raw_address?: string | null
owner_id?: string | null
account_stage_id?: string | null
existence_level?: string
show_intent?: boolean
has_intent_signal_account?: boolean
typed_custom_fields?: Record<string, unknown>
created_at: string
}
export interface ApolloTask {
id: string
note: string
user_id?: string
contact_id?: string
account_id?: string
type?: string
priority?: string
status?: string
due_at?: string
completed: boolean
created_at: string
note?: string
created_at?: string
updated_at?: string
}
export interface ApolloOpportunity {
id: string
team_id?: string
name: string
account_id: string
amount?: number
stage_id?: string
owner_id?: string
close_date?: string
description?: string
account_id?: string | null
owner_id?: string | null
salesforce_owner_id?: string | null
amount?: number | string | null
amount_in_team_currency?: number | null
forecasted_revenue?: number | null
exchange_rate_code?: string
exchange_rate_value?: number
closed_date?: string | null
actual_close_date?: string | null
description?: string | null
is_closed?: boolean
is_won?: boolean
stage_name?: string | null
opportunity_stage_id?: string | null
opportunity_pipeline_id?: string | null
source?: string
salesforce_id?: string | null
forecast_category?: string
deal_probability?: number
probability?: number | null
created_by_id?: string
stage_updated_at?: string
next_step?: string | null
next_step_date?: string | null
closed_lost_reason?: string | null
closed_won_reason?: string | null
last_activity_date?: string
existence_level?: string
typed_custom_fields?: Record<string, unknown>
opportunity_rule_config_statuses?: unknown[]
opportunity_contact_roles?: unknown[]
currency?: { name?: string; iso_code?: string; symbol?: string }
account?: { id?: string; name?: string; website_url?: string | null }
created_at: string
updated_at?: string
}
interface ApolloBaseParams {
@@ -77,10 +119,15 @@ interface ApolloBaseParams {
// People Search Types
export interface ApolloPeopleSearchParams extends ApolloBaseParams {
person_titles?: string[]
include_similar_titles?: boolean
person_locations?: string[]
person_seniorities?: string[]
organization_ids?: string[]
organization_names?: string[]
organization_locations?: string[]
q_organization_domains_list?: string[]
organization_num_employees_ranges?: string[]
contact_email_status?: string[]
q_keywords?: string
page?: number
per_page?: number
@@ -99,17 +146,21 @@ export interface ApolloPeopleSearchResponse extends ToolResponse {
export interface ApolloPeopleEnrichParams extends ApolloBaseParams {
first_name?: string
last_name?: string
name?: string
id?: string
hashed_email?: string
organization_name?: string
email?: string
domain?: string
linkedin_url?: string
reveal_personal_emails?: boolean
reveal_phone_number?: boolean
webhook_url?: string
}
export interface ApolloPeopleEnrichResponse extends ToolResponse {
output: {
person: ApolloPerson
person: ApolloPerson | null
enriched: boolean
}
}
@@ -119,28 +170,38 @@ export interface ApolloPeopleBulkEnrichParams extends ApolloBaseParams {
people: Array<{
first_name?: string
last_name?: string
organization_name?: string
name?: string
email?: string
hashed_email?: string
organization_name?: string
domain?: string
id?: string
linkedin_url?: string
}>
reveal_personal_emails?: boolean
reveal_phone_number?: boolean
webhook_url?: string
}
export interface ApolloPeopleBulkEnrichResponse extends ToolResponse {
output: {
people: ApolloPerson[]
total: number
enriched: number
matches: Array<ApolloPerson | null>
total_requested_enrichments: number
unique_enriched_records: number
missing_records: number | null
credits_consumed: number | null
}
}
// Organization Search Types
export interface ApolloOrganizationSearchParams extends ApolloBaseParams {
organization_locations?: string[]
organization_not_locations?: string[]
organization_num_employees_ranges?: string[]
q_organization_keyword_tags?: string[]
q_organization_name?: string
organization_ids?: string[]
q_organization_domains_list?: string[]
page?: number
per_page?: number
}
@@ -156,23 +217,19 @@ export interface ApolloOrganizationSearchResponse extends ToolResponse {
// Organization Enrichment Types
export interface ApolloOrganizationEnrichParams extends ApolloBaseParams {
organization_name?: string
domain?: string
domain: string
}
export interface ApolloOrganizationEnrichResponse extends ToolResponse {
output: {
organization: ApolloOrganization
organization: ApolloOrganization | null
enriched: boolean
}
}
// Bulk Organization Enrichment Types
export interface ApolloOrganizationBulkEnrichParams extends ApolloBaseParams {
organizations: Array<{
organization_name?: string
domain?: string
}>
organizations: Array<{ name: string; domain?: string }>
}
export interface ApolloOrganizationBulkEnrichResponse extends ToolResponse {
@@ -180,6 +237,8 @@ export interface ApolloOrganizationBulkEnrichResponse extends ToolResponse {
organizations: ApolloOrganization[]
total: number
enriched: number
missing_records: number
unique_domains: number
}
}
@@ -191,6 +250,18 @@ export interface ApolloContactCreateParams extends ApolloBaseParams {
title?: string
account_id?: string
owner_id?: string
organization_name?: string
website_url?: string
label_names?: string[]
contact_stage_id?: string
present_raw_address?: string
direct_phone?: string
corporate_phone?: string
mobile_phone?: string
home_phone?: string
other_phone?: string
typed_custom_fields?: Record<string, unknown>
run_dedupe?: boolean
}
export interface ApolloContactCreateResponse extends ToolResponse {
@@ -209,6 +280,17 @@ export interface ApolloContactUpdateParams extends ApolloBaseParams {
title?: string
account_id?: string
owner_id?: string
organization_name?: string
website_url?: string
label_names?: string[]
contact_stage_id?: string
present_raw_address?: string
direct_phone?: string
corporate_phone?: string
mobile_phone?: string
home_phone?: string
other_phone?: string
typed_custom_fields?: Record<string, unknown>
}
export interface ApolloContactUpdateResponse extends ToolResponse {
@@ -221,14 +303,26 @@ export interface ApolloContactUpdateResponse extends ToolResponse {
// Contact Bulk Create Types
export interface ApolloContactBulkCreateParams extends ApolloBaseParams {
contacts: Array<{
first_name: string
last_name: string
first_name?: string
last_name?: string
email?: string
title?: string
organization_name?: string
account_id?: string
owner_id?: string
contact_stage_id?: string
linkedin_url?: string
phone?: string
phone_numbers?: Array<{ raw_number: string; position?: number }>
contact_emails?: Array<{ email: string; position?: number }>
salesforce_contact_id?: string
hubspot_id?: string
team_id?: string
typed_custom_fields?: Record<string, unknown>
[key: string]: unknown
}>
run_dedupe?: boolean
append_label_names?: string[]
}
export interface ApolloContactBulkCreateResponse extends ToolResponse {
@@ -243,24 +337,15 @@ export interface ApolloContactBulkCreateResponse extends ToolResponse {
// Contact Bulk Update Types
export interface ApolloContactBulkUpdateParams extends ApolloBaseParams {
contacts: Array<{
id: string
first_name?: string
last_name?: string
email?: string
title?: string
account_id?: string
owner_id?: string
}>
contact_ids?: string[]
contact_attributes?: Array<{ id: string; [key: string]: unknown }> | Record<string, unknown>
async?: boolean
}
export interface ApolloContactBulkUpdateResponse extends ToolResponse {
output: {
updated_contacts: ApolloContact[]
failed_contacts: Array<{ id: string; error: string }>
total_submitted: number
updated: number
failed: number
message: string | null
job_id: string | null
}
}
@@ -268,6 +353,9 @@ export interface ApolloContactBulkUpdateResponse extends ToolResponse {
export interface ApolloContactSearchParams extends ApolloBaseParams {
q_keywords?: string
contact_stage_ids?: string[]
contact_label_ids?: string[]
sort_by_field?: string
sort_ascending?: boolean
page?: number
per_page?: number
}
@@ -281,7 +369,7 @@ export interface ApolloPagination {
export interface ApolloContactSearchResponse extends ToolResponse {
output: {
contacts: ApolloContact[] | null
contacts: ApolloContact[]
pagination: ApolloPagination | null
}
}
@@ -289,9 +377,12 @@ export interface ApolloContactSearchResponse extends ToolResponse {
// Account Create Types
export interface ApolloAccountCreateParams extends ApolloBaseParams {
name: string
website_url?: string
domain?: string
phone?: string
owner_id?: string
account_stage_id?: string
raw_address?: string
typed_custom_fields?: Record<string, unknown>
}
export interface ApolloAccountCreateResponse extends ToolResponse {
@@ -305,9 +396,12 @@ export interface ApolloAccountCreateResponse extends ToolResponse {
export interface ApolloAccountUpdateParams extends ApolloBaseParams {
account_id: string
name?: string
website_url?: string
domain?: string
phone?: string
owner_id?: string
account_stage_id?: string
raw_address?: string
typed_custom_fields?: Record<string, unknown>
}
export interface ApolloAccountUpdateResponse extends ToolResponse {
@@ -319,16 +413,18 @@ export interface ApolloAccountUpdateResponse extends ToolResponse {
// Account Search Types
export interface ApolloAccountSearchParams extends ApolloBaseParams {
q_keywords?: string
owner_id?: string
q_organization_name?: string
account_stage_ids?: string[]
account_label_ids?: string[]
sort_by_field?: string
sort_ascending?: boolean
page?: number
per_page?: number
}
export interface ApolloAccountSearchResponse extends ToolResponse {
output: {
accounts: ApolloAccount[] | null
accounts: ApolloAccount[]
pagination: ApolloPagination | null
}
}
@@ -336,89 +432,128 @@ export interface ApolloAccountSearchResponse extends ToolResponse {
// Account Bulk Create Types
export interface ApolloAccountBulkCreateParams extends ApolloBaseParams {
accounts: Array<{
name: string
website_url?: string
name?: string
domain?: string
phone?: string
phone_status_cd?: string
raw_address?: string
owner_id?: string
linkedin_url?: string
facebook_url?: string
twitter_url?: string
salesforce_id?: string
hubspot_id?: string
[key: string]: unknown
}>
append_label_names?: string[]
run_dedupe?: boolean
}
export interface ApolloAccountBulkCreateResponse extends ToolResponse {
output: {
created_accounts: ApolloAccount[]
failed_accounts: Array<{ name: string; error: string }>
existing_accounts: ApolloAccount[]
failed_accounts: Array<Record<string, unknown>>
total_submitted: number
created: number
existing: number
failed: number
}
}
// Account Bulk Update Types
export interface ApolloAccountBulkUpdateParams extends ApolloBaseParams {
accounts: Array<{
id: string
name?: string
website_url?: string
phone?: string
owner_id?: string
}>
account_ids?: string[]
name?: string
owner_id?: string
account_attributes?: Array<{ id: string; [key: string]: unknown }> | Record<string, unknown>
async?: boolean
}
export interface ApolloAccountBulkUpdateResponse extends ToolResponse {
output: {
updated_accounts: ApolloAccount[]
failed_accounts: Array<{ id: string; error: string }>
total_submitted: number
updated: number
failed: number
message: string | null
account_ids: string[]
}
}
// Sequence Add Contacts Types
export interface ApolloSequenceAddContactsParams extends ApolloBaseParams {
sequence_id: string
contact_ids: string[]
emailer_campaign_id?: string
send_email_from_user_id?: string
contact_ids?: string[]
label_names?: string[]
send_email_from_email_account_id: string
send_email_from_email_address?: string
sequence_no_email?: boolean
sequence_unverified_email?: boolean
sequence_job_change?: boolean
sequence_active_in_other_campaigns?: boolean
sequence_finished_in_other_campaigns?: boolean
sequence_same_company_in_same_campaign?: boolean
contacts_without_ownership_permission?: boolean
add_if_in_queue?: boolean
contact_verification_skipped?: boolean
user_id?: string
status?: string
auto_unpause_at?: string
}
export interface ApolloSequenceAddedContact {
id: string
first_name?: string
last_name?: string
email?: string
status?: string
opened_rate?: number | null
replied_rate?: number | null
}
export interface ApolloSequenceSkippedContact {
id: string
reason: string
}
export interface ApolloSequenceAddContactsResponse extends ToolResponse {
output: {
contacts_added: string[]
added: ApolloSequenceAddedContact[]
skipped: ApolloSequenceSkippedContact[]
skipped_contact_ids: string[] | Record<string, string> | null
emailer_campaign: { id: string; name: string } | null
sequence_id: string
total_added: number
total_skipped: number
}
}
// Task Create Types
export interface ApolloTaskCreateParams extends ApolloBaseParams {
note: string
contact_id?: string
account_id?: string
due_at?: string
user_id: string
contact_ids: string[]
priority?: string
type?: string
due_at: string
type: string
status: string
note?: string
}
export interface ApolloTaskCreateResponse extends ToolResponse {
output: {
task: ApolloTask | null
tasks: ApolloTask[]
created: boolean
}
}
// Task Search Types
export interface ApolloTaskSearchParams extends ApolloBaseParams {
contact_id?: string
account_id?: string
completed?: boolean
sort_by_field?: string
open_factor_names?: string[]
page?: number
per_page?: number
}
export interface ApolloTaskSearchResponse extends ToolResponse {
output: {
tasks: ApolloTask[] | null
tasks: ApolloTask[]
pagination: ApolloPagination | null
}
}
@@ -426,13 +561,18 @@ export interface ApolloTaskSearchResponse extends ToolResponse {
// Email Accounts List Types
export interface ApolloEmailAccountsParams extends ApolloBaseParams {}
export interface ApolloEmailAccount {
id: string | number
email: string
type?: string
active?: boolean
default?: boolean
linked_at?: string | null
}
export interface ApolloEmailAccountsResponse extends ToolResponse {
output: {
email_accounts: Array<{
id: string
email: string
active: boolean
}>
email_accounts: ApolloEmailAccount[]
total: number
}
}
@@ -440,12 +580,12 @@ export interface ApolloEmailAccountsResponse extends ToolResponse {
// Opportunity Create Types
export interface ApolloOpportunityCreateParams extends ApolloBaseParams {
name: string
account_id: string
amount?: number
stage_id?: string
account_id?: string
amount?: string
opportunity_stage_id?: string
owner_id?: string
close_date?: string
description?: string
closed_date?: string
typed_custom_fields?: Record<string, unknown>
}
export interface ApolloOpportunityCreateResponse extends ToolResponse {
@@ -457,10 +597,7 @@ export interface ApolloOpportunityCreateResponse extends ToolResponse {
// Opportunity Search Types
export interface ApolloOpportunitySearchParams extends ApolloBaseParams {
q_keywords?: string
account_ids?: string[]
stage_ids?: string[]
owner_ids?: string[]
sort_by_field?: string
page?: number
per_page?: number
}
@@ -481,7 +618,7 @@ export interface ApolloOpportunityGetParams extends ApolloBaseParams {
export interface ApolloOpportunityGetResponse extends ToolResponse {
output: {
opportunity: ApolloOpportunity
opportunity: ApolloOpportunity | null
found: boolean
}
}
@@ -490,11 +627,11 @@ export interface ApolloOpportunityGetResponse extends ToolResponse {
export interface ApolloOpportunityUpdateParams extends ApolloBaseParams {
opportunity_id: string
name?: string
amount?: number
stage_id?: string
amount?: string
opportunity_stage_id?: string
owner_id?: string
close_date?: string
description?: string
closed_date?: string
typed_custom_fields?: Record<string, unknown>
}
export interface ApolloOpportunityUpdateResponse extends ToolResponse {
@@ -520,7 +657,6 @@ export interface ApolloSequence {
// Sequence Search Types
export interface ApolloSequenceSearchParams extends ApolloBaseParams {
q_name?: string
active?: boolean
page?: number
per_page?: number
}