diff --git a/apps/docs/content/docs/en/tools/apollo.mdx b/apps/docs/content/docs/en/tools/apollo.mdx index d063d017123..355f62d9642 100644 --- a/apps/docs/content/docs/en/tools/apollo.mdx +++ b/apps/docs/content/docs/en/tools/apollo.mdx @@ -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 >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 >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\) | diff --git a/apps/sim/app/(landing)/integrations/data/integrations.json b/apps/sim/app/(landing)/integrations/data/integrations.json index 9a1513dc6f8..78006dcd8bb 100644 --- a/apps/sim/app/(landing)/integrations/data/integrations.json +++ b/apps/sim/app/(landing)/integrations/data/integrations.json @@ -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", @@ -14173,7 +14173,7 @@ "description": "Hire a pre-hire into an employee position. Converts an applicant into an active employee record with position, start date, and manager assignment." }, { - "name": "Update Worker", + "name": "Update Personal Information", "description": "Update fields on an existing worker record in Workday." }, { diff --git a/apps/sim/blocks/blocks/apollo.ts b/apps/sim/blocks/blocks/apollo.ts index 64bd9448d61..3ee33e90356 100644 --- a/apps/sim/blocks/blocks/apollo.ts +++ b/apps/sim/blocks/blocks/apollo.ts @@ -92,6 +92,21 @@ export const ApolloBlock: BlockConfig = { condition: { field: 'operation', value: 'people_search' }, mode: 'advanced', }, + { + id: 'include_similar_titles', + title: 'Include Similar Titles', + type: 'switch', + condition: { field: 'operation', value: 'people_search' }, + mode: 'advanced', + }, + { + id: 'contact_email_status', + title: 'Contact Email Status', + type: 'code', + placeholder: '["verified", "unverified", "likely to engage"]', + condition: { field: 'operation', value: 'people_search' }, + mode: 'advanced', + }, { id: 'contact_stage_ids', title: 'Contact Stage IDs', @@ -100,6 +115,14 @@ export const ApolloBlock: BlockConfig = { condition: { field: 'operation', value: 'contact_search' }, mode: 'advanced', }, + { + id: 'contact_label_ids', + title: 'Contact Label IDs', + type: 'code', + placeholder: '["label_id_1", "label_id_2"]', + condition: { field: 'operation', value: 'contact_search' }, + mode: 'advanced', + }, // People Enrich Fields { @@ -111,10 +134,7 @@ export const ApolloBlock: BlockConfig = { field: 'operation', value: ['people_enrich', 'contact_create', 'contact_update'], }, - required: { - field: 'operation', - value: 'contact_create', - }, + required: { field: 'operation', value: 'contact_create' }, }, { id: 'last_name', @@ -125,10 +145,7 @@ export const ApolloBlock: BlockConfig = { field: 'operation', value: ['people_enrich', 'contact_create', 'contact_update'], }, - required: { - field: 'operation', - value: 'contact_create', - }, + required: { field: 'operation', value: 'contact_create' }, }, { id: 'email', @@ -147,7 +164,7 @@ export const ApolloBlock: BlockConfig = { placeholder: 'Company name', condition: { field: 'operation', - value: ['people_enrich', 'organization_enrich'], + value: ['people_enrich', 'contact_create', 'contact_update'], }, }, { @@ -157,7 +174,11 @@ export const ApolloBlock: BlockConfig = { placeholder: 'example.com', condition: { field: 'operation', - value: ['people_enrich', 'organization_enrich'], + value: ['people_enrich', 'organization_enrich', 'account_create', 'account_update'], + }, + required: { + field: 'operation', + value: 'organization_enrich', }, }, { @@ -180,6 +201,17 @@ export const ApolloBlock: BlockConfig = { }, mode: 'advanced', }, + { + id: 'webhook_url', + title: 'Phone Reveal Webhook URL', + type: 'short-input', + placeholder: 'https://your-app.com/apollo-phone-webhook', + condition: { + field: 'operation', + value: ['people_enrich', 'people_bulk_enrich'], + }, + mode: 'advanced', + }, // Bulk Enrich Fields { @@ -194,7 +226,8 @@ export const ApolloBlock: BlockConfig = { id: 'organizations', title: 'Organizations (JSON Array)', type: 'code', - placeholder: '[{"organization_name": "Company A", "domain": "companya.com"}]', + placeholder: + '[{"name": "Company A", "domain": "companya.com"}, {"name": "Company B", "domain": "companyb.com"}]', condition: { field: 'operation', value: 'organization_bulk_enrich' }, required: true, }, @@ -205,15 +238,39 @@ export const ApolloBlock: BlockConfig = { title: 'Organization Locations', type: 'code', placeholder: '["San Francisco, CA"]', + condition: { field: 'operation', value: ['organization_search', 'people_search'] }, + mode: 'advanced', + }, + { + id: 'organization_not_locations', + title: 'Excluded Organization Locations', + type: 'code', + placeholder: '["Ireland", "Minnesota"]', condition: { field: 'operation', value: 'organization_search' }, mode: 'advanced', }, + { + id: 'organization_ids', + title: 'Organization IDs', + type: 'code', + placeholder: '["org_id_1", "org_id_2"]', + condition: { field: 'operation', value: ['organization_search', 'people_search'] }, + mode: 'advanced', + }, + { + id: 'q_organization_domains_list', + title: 'Organization Domains', + type: 'code', + placeholder: '["apollo.io", "stripe.com"]', + condition: { field: 'operation', value: ['organization_search', 'people_search'] }, + mode: 'advanced', + }, { id: 'organization_num_employees_ranges', title: 'Employee Count Ranges', type: 'code', - placeholder: '["1-10", "11-50", "51-200"]', - condition: { field: 'operation', value: 'organization_search' }, + placeholder: '["1,10", "11,50", "51,200"]', + condition: { field: 'operation', value: ['organization_search', 'people_search'] }, mode: 'advanced', }, { @@ -229,7 +286,7 @@ export const ApolloBlock: BlockConfig = { title: 'Organization Name', type: 'short-input', placeholder: 'Company name to search', - condition: { field: 'operation', value: 'organization_search' }, + condition: { field: 'operation', value: ['organization_search', 'account_search'] }, }, // Contact Fields @@ -246,10 +303,7 @@ export const ApolloBlock: BlockConfig = { title: 'Job Title', type: 'short-input', placeholder: 'Job title', - condition: { - field: 'operation', - value: ['contact_create', 'contact_update'], - }, + condition: { field: 'operation', value: ['contact_create', 'contact_update'] }, mode: 'advanced', }, { @@ -259,17 +313,11 @@ export const ApolloBlock: BlockConfig = { placeholder: 'Apollo account ID', condition: { field: 'operation', - value: [ - 'contact_create', - 'contact_update', - 'account_update', - 'task_create', - 'opportunity_create', - ], + value: ['contact_create', 'contact_update', 'account_update', 'opportunity_create'], }, required: { field: 'operation', - value: ['account_update', 'opportunity_create'], + value: 'account_update', }, }, { @@ -284,7 +332,6 @@ export const ApolloBlock: BlockConfig = { 'contact_update', 'account_create', 'account_update', - 'account_search', 'opportunity_create', 'opportunity_update', ], @@ -292,6 +339,104 @@ export const ApolloBlock: BlockConfig = { mode: 'advanced', }, + { + id: 'website_url', + title: 'Corporate Website URL', + type: 'short-input', + placeholder: 'https://www.apollo.io/', + condition: { field: 'operation', value: ['contact_create', 'contact_update'] }, + mode: 'advanced', + }, + { + id: 'label_names', + title: 'Label Names (JSON Array)', + type: 'code', + placeholder: '["Prospects", "VIP"]', + condition: { field: 'operation', value: ['contact_create', 'contact_update'] }, + mode: 'advanced', + }, + { + id: 'contact_stage_id', + title: 'Contact Stage ID', + type: 'short-input', + placeholder: 'Apollo contact stage ID', + condition: { field: 'operation', value: ['contact_create', 'contact_update'] }, + mode: 'advanced', + }, + { + id: 'present_raw_address', + title: 'Personal Location', + type: 'short-input', + placeholder: 'Atlanta, United States', + condition: { field: 'operation', value: ['contact_create', 'contact_update'] }, + mode: 'advanced', + }, + { + id: 'direct_phone', + title: 'Direct Phone', + type: 'short-input', + placeholder: '+1 555 123 4567', + condition: { field: 'operation', value: ['contact_create', 'contact_update'] }, + mode: 'advanced', + }, + { + id: 'corporate_phone', + title: 'Corporate Phone', + type: 'short-input', + placeholder: '+1 555 123 4567', + condition: { field: 'operation', value: ['contact_create', 'contact_update'] }, + mode: 'advanced', + }, + { + id: 'mobile_phone', + title: 'Mobile Phone', + type: 'short-input', + placeholder: '+1 555 123 4567', + condition: { field: 'operation', value: ['contact_create', 'contact_update'] }, + mode: 'advanced', + }, + { + id: 'home_phone', + title: 'Home Phone', + type: 'short-input', + placeholder: '+1 555 123 4567', + condition: { field: 'operation', value: ['contact_create', 'contact_update'] }, + mode: 'advanced', + }, + { + id: 'other_phone', + title: 'Other Phone', + type: 'short-input', + placeholder: '+1 555 123 4567', + condition: { field: 'operation', value: ['contact_create', 'contact_update'] }, + mode: 'advanced', + }, + { + id: 'typed_custom_fields', + title: 'Custom Fields (JSON Object)', + type: 'code', + placeholder: '{"custom_field_id": "value"}', + condition: { + field: 'operation', + value: [ + 'contact_create', + 'contact_update', + 'account_create', + 'account_update', + 'opportunity_create', + 'opportunity_update', + ], + }, + mode: 'advanced', + }, + { + id: 'contact_run_dedupe', + title: 'Run Deduplication', + type: 'switch', + condition: { field: 'operation', value: 'contact_create' }, + mode: 'advanced', + }, + // Contact Bulk Operations { id: 'contacts', @@ -304,55 +449,82 @@ export const ApolloBlock: BlockConfig = { }, { id: 'contacts', - title: 'Contacts (JSON Array)', + title: 'Contact IDs (JSON Array)', type: 'code', - placeholder: '[{"id": "contact_id_1", "first_name": "John", "last_name": "Doe"}]', + placeholder: '["contact_id_1", "contact_id_2"]', condition: { field: 'operation', value: 'contact_bulk_update' }, - required: true, }, { - id: 'run_dedupe', - title: 'Run Deduplication', - type: 'switch', - condition: { field: 'operation', value: 'contact_bulk_create' }, - mode: 'advanced', + id: 'contact_attributes', + title: 'Contact Attributes (JSON Array of Objects)', + type: 'code', + placeholder: + '[{"id": "contact_id_1", "first_name": "John", "title": "VP Sales", "owner_id": "user_id"}]', + condition: { field: 'operation', value: 'contact_bulk_update' }, }, - - // Account Fields { - id: 'account_name', - title: 'Account Name', - type: 'short-input', - placeholder: 'Company name', + id: 'async', + title: 'Force Asynchronous Processing', + type: 'switch', condition: { field: 'operation', - value: ['account_create', 'account_update'], + value: ['contact_bulk_update', 'account_bulk_update'], }, - required: { + mode: 'advanced', + }, + { + id: 'run_dedupe', + title: 'Run Deduplication', + type: 'switch', + condition: { field: 'operation', - value: 'account_create', + value: ['contact_bulk_create', 'account_bulk_create'], }, + mode: 'advanced', }, { - id: 'website_url', - title: 'Website URL', - type: 'short-input', - placeholder: 'https://example.com', + id: 'append_label_names', + title: 'Append Label Names (JSON Array)', + type: 'code', + placeholder: '["Hot Lead", "Q4 Outreach"]', condition: { field: 'operation', - value: ['account_create', 'account_update'], + value: ['contact_bulk_create', 'account_bulk_create'], }, mode: 'advanced', }, + + // Account Fields + { + id: 'account_name', + title: 'Account Name', + type: 'short-input', + placeholder: 'Company name', + condition: { field: 'operation', value: ['account_create', 'account_update'] }, + required: { field: 'operation', value: 'account_create' }, + }, { id: 'phone', title: 'Phone Number', type: 'short-input', placeholder: 'Company phone', - condition: { - field: 'operation', - value: ['account_create', 'account_update'], - }, + condition: { field: 'operation', value: ['account_create', 'account_update'] }, + mode: 'advanced', + }, + { + id: 'account_stage_id', + title: 'Account Stage ID', + type: 'short-input', + placeholder: 'Apollo account stage ID', + condition: { field: 'operation', value: ['account_create', 'account_update'] }, + mode: 'advanced', + }, + { + id: 'raw_address', + title: 'Account Address', + type: 'short-input', + placeholder: '123 Main St, San Francisco, CA', + condition: { field: 'operation', value: ['account_create', 'account_update'] }, mode: 'advanced', }, @@ -364,7 +536,7 @@ export const ApolloBlock: BlockConfig = { placeholder: 'Search keywords', condition: { field: 'operation', - value: ['people_search', 'contact_search', 'account_search', 'opportunity_search'], + value: ['people_search', 'contact_search'], }, }, { @@ -375,24 +547,54 @@ export const ApolloBlock: BlockConfig = { condition: { field: 'operation', value: 'account_search' }, mode: 'advanced', }, + { + id: 'account_label_ids', + title: 'Account Label IDs', + type: 'code', + placeholder: '["label_id_1", "label_id_2"]', + condition: { field: 'operation', value: 'account_search' }, + mode: 'advanced', + }, // Account Bulk Operations { id: 'accounts', title: 'Accounts (JSON Array)', type: 'code', - placeholder: - '[{"name": "Company A", "website_url": "https://companya.com", "phone": "+1234567890"}]', + placeholder: '[{"name": "Company A", "domain": "companya.com", "phone": "+1234567890"}]', condition: { field: 'operation', value: 'account_bulk_create' }, required: true, }, { id: 'accounts', - title: 'Accounts (JSON Array)', + title: 'Account IDs (JSON Array)', type: 'code', - placeholder: '[{"id": "account_id_1", "name": "Updated Company Name"}]', + placeholder: '["account_id_1", "account_id_2"]', + condition: { field: 'operation', value: 'account_bulk_update' }, + }, + { + id: 'account_bulk_update_name', + title: 'Uniform Name (used with Account IDs)', + type: 'short-input', + placeholder: 'Updated Account Name', + condition: { field: 'operation', value: 'account_bulk_update' }, + mode: 'advanced', + }, + { + id: 'account_bulk_update_owner_id', + title: 'Uniform Owner ID (used with Account IDs)', + type: 'short-input', + placeholder: 'Apollo user ID', + condition: { field: 'operation', value: 'account_bulk_update' }, + mode: 'advanced', + }, + { + id: 'account_attributes', + title: 'Account Attributes (JSON Array of Objects)', + type: 'code', + placeholder: + '[{"id": "account_id_1", "name": "Acme", "owner_id": "user_id", "account_stage_id": "stage_id"}]', condition: { field: 'operation', value: 'account_bulk_update' }, - required: true, }, // Opportunity Fields @@ -401,46 +603,31 @@ export const ApolloBlock: BlockConfig = { title: 'Opportunity Name', type: 'short-input', placeholder: 'Opportunity name', - condition: { - field: 'operation', - value: ['opportunity_create', 'opportunity_update'], - }, - required: { - field: 'operation', - value: 'opportunity_create', - }, + condition: { field: 'operation', value: ['opportunity_create', 'opportunity_update'] }, + required: { field: 'operation', value: 'opportunity_create' }, }, { id: 'amount', title: 'Amount', type: 'short-input', - placeholder: 'Deal amount (e.g., 50000)', - condition: { - field: 'operation', - value: ['opportunity_create', 'opportunity_update'], - }, + placeholder: 'Plain number, no commas (e.g., 50000)', + condition: { field: 'operation', value: ['opportunity_create', 'opportunity_update'] }, mode: 'advanced', }, { - id: 'stage_id', - title: 'Stage ID', + id: 'opportunity_stage_id', + title: 'Opportunity Stage ID', type: 'short-input', - placeholder: 'Opportunity stage ID', - condition: { - field: 'operation', - value: ['opportunity_create', 'opportunity_update'], - }, + placeholder: 'Apollo opportunity_stage_id', + condition: { field: 'operation', value: ['opportunity_create', 'opportunity_update'] }, mode: 'advanced', }, { - id: 'close_date', + id: 'closed_date', title: 'Close Date', type: 'short-input', - placeholder: 'ISO date (e.g., 2024-12-31)', - condition: { - field: 'operation', - value: ['opportunity_create', 'opportunity_update'], - }, + placeholder: 'YYYY-MM-DD (e.g., 2024-12-31)', + condition: { field: 'operation', value: ['opportunity_create', 'opportunity_update'] }, mode: 'advanced', wandConfig: { enabled: true, @@ -456,17 +643,6 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n generationType: 'timestamp', }, }, - { - id: 'description', - title: 'Description', - type: 'long-input', - placeholder: 'Opportunity description', - condition: { - field: 'operation', - value: ['opportunity_create', 'opportunity_update'], - }, - mode: 'advanced', - }, // Opportunity Get { @@ -474,36 +650,27 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n title: 'Opportunity ID', type: 'short-input', placeholder: 'Apollo opportunity ID', - condition: { - field: 'operation', - value: ['opportunity_get', 'opportunity_update'], - }, + condition: { field: 'operation', value: ['opportunity_get', 'opportunity_update'] }, required: true, }, - // Opportunity Search Fields + // Opportunity / Account / Task Search shared sort { - id: 'account_ids', - title: 'Account IDs', - type: 'code', - placeholder: '["account_id_1", "account_id_2"]', - condition: { field: 'operation', value: 'opportunity_search' }, - mode: 'advanced', - }, - { - id: 'stage_ids', - title: 'Stage IDs', - type: 'code', - placeholder: '["stage_id_1", "stage_id_2"]', - condition: { field: 'operation', value: 'opportunity_search' }, + id: 'sort_by_field', + title: 'Sort By Field', + type: 'short-input', + placeholder: 'Sort field name', + condition: { + field: 'operation', + value: ['opportunity_search', 'account_search', 'task_search', 'contact_search'], + }, mode: 'advanced', }, { - id: 'owner_ids', - title: 'Owner IDs', - type: 'code', - placeholder: '["user_id_1", "user_id_2"]', - condition: { field: 'operation', value: 'opportunity_search' }, + id: 'sort_ascending', + title: 'Sort Ascending', + type: 'switch', + condition: { field: 'operation', value: ['account_search', 'contact_search'] }, mode: 'advanced', }, @@ -515,15 +682,8 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n placeholder: 'Search by sequence name', condition: { field: 'operation', value: 'sequence_search' }, }, - { - id: 'active', - title: 'Active Only', - type: 'switch', - condition: { field: 'operation', value: 'sequence_search' }, - mode: 'advanced', - }, - // Sequence Fields + // Sequence Add Fields { id: 'sequence_id', title: 'Sequence ID', @@ -537,16 +697,137 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n title: 'Contact IDs (JSON Array)', type: 'code', placeholder: '["contact_id_1", "contact_id_2"]', + condition: { field: 'operation', value: ['sequence_add', 'task_create'] }, + required: { field: 'operation', value: 'task_create' }, + }, + { + id: 'sequence_add_label_names', + title: 'Label Names (JSON Array)', + type: 'code', + placeholder: '["Hot Lead", "Q4 Outreach"]', + condition: { field: 'operation', value: 'sequence_add' }, + mode: 'advanced', + }, + { + id: 'send_email_from_email_account_id', + title: 'Send Email From (Email Account ID)', + type: 'short-input', + placeholder: 'Apollo email account ID', condition: { field: 'operation', value: 'sequence_add' }, required: true, }, + { + id: 'send_email_from_email_address', + title: 'Send Email From (Email Address)', + type: 'short-input', + placeholder: 'sender@example.com', + condition: { field: 'operation', value: 'sequence_add' }, + mode: 'advanced', + }, + { + id: 'sequence_same_company_in_same_campaign', + title: 'Allow Same Company in Same Campaign', + type: 'switch', + condition: { field: 'operation', value: 'sequence_add' }, + mode: 'advanced', + }, + { + id: 'contacts_without_ownership_permission', + title: 'Add Contacts Without Ownership Permission', + type: 'switch', + condition: { field: 'operation', value: 'sequence_add' }, + mode: 'advanced', + }, + { + id: 'add_if_in_queue', + title: 'Add If In Queue', + type: 'switch', + condition: { field: 'operation', value: 'sequence_add' }, + mode: 'advanced', + }, + { + id: 'contact_verification_skipped', + title: 'Skip Contact Verification', + type: 'switch', + condition: { field: 'operation', value: 'sequence_add' }, + mode: 'advanced', + }, + { + id: 'sequence_user_id', + title: 'Acting User ID', + type: 'short-input', + placeholder: 'Apollo user ID', + condition: { field: 'operation', value: 'sequence_add' }, + mode: 'advanced', + }, + { + id: 'sequence_status', + title: 'Initial Status', + type: 'dropdown', + options: [ + { label: 'Active', id: 'active' }, + { label: 'Paused', id: 'paused' }, + ], + condition: { field: 'operation', value: 'sequence_add' }, + mode: 'advanced', + }, + { + id: 'auto_unpause_at', + title: 'Auto Unpause At', + type: 'short-input', + placeholder: 'ISO 8601 (e.g., 2024-12-31T23:59:59Z)', + condition: { field: 'operation', value: 'sequence_add' }, + mode: 'advanced', + }, - // Task Fields + // Task Create Fields { - id: 'note', - title: 'Task Note', - type: 'long-input', - placeholder: 'Task description', + id: 'user_id', + title: 'Assigned User ID', + type: 'short-input', + placeholder: 'Apollo user ID', + condition: { field: 'operation', value: 'task_create' }, + required: true, + }, + { + id: 'priority', + title: 'Priority', + type: 'dropdown', + options: [ + { label: 'High', id: 'high' }, + { label: 'Medium', id: 'medium' }, + { label: 'Low', id: 'low' }, + ], + value: () => 'medium', + condition: { field: 'operation', value: 'task_create' }, + }, + { + id: 'type', + title: 'Task Type', + type: 'dropdown', + options: [ + { label: 'Call', id: 'call' }, + { label: 'Outreach Manual Email', id: 'outreach_manual_email' }, + { label: 'LinkedIn — Connect', id: 'linkedin_step_connect' }, + { label: 'LinkedIn — Message', id: 'linkedin_step_message' }, + { label: 'LinkedIn — View Profile', id: 'linkedin_step_view_profile' }, + { label: 'LinkedIn — Interact with Post', id: 'linkedin_step_interact_post' }, + { label: 'Action Item', id: 'action_item' }, + ], + value: () => 'action_item', + condition: { field: 'operation', value: 'task_create' }, + required: true, + }, + { + id: 'status', + title: 'Status', + type: 'dropdown', + options: [ + { label: 'Scheduled', id: 'scheduled' }, + { label: 'Completed', id: 'completed' }, + { label: 'Skipped', id: 'skipped' }, + ], + value: () => 'scheduled', condition: { field: 'operation', value: 'task_create' }, required: true, }, @@ -554,9 +835,9 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n id: 'due_at', title: 'Due Date', type: 'short-input', - placeholder: 'ISO date (e.g., 2024-12-31T23:59:59Z)', + placeholder: 'ISO 8601 (e.g., 2024-12-31T23:59:59Z)', condition: { field: 'operation', value: 'task_create' }, - mode: 'advanced', + required: true, wandConfig: { enabled: true, prompt: `Generate an ISO 8601 timestamp based on the user's description. @@ -573,9 +854,20 @@ Return ONLY the timestamp string in ISO 8601 format - no explanations, no quotes }, }, { - id: 'completed', - title: 'Completed', - type: 'switch', + id: 'task_notes', + title: 'Task Notes', + type: 'long-input', + placeholder: 'Notes for the task', + condition: { field: 'operation', value: 'task_create' }, + mode: 'advanced', + }, + + // Task Search Fields + { + id: 'open_factor_names', + title: 'Open Factor Names', + type: 'code', + placeholder: '["task_types"]', condition: { field: 'operation', value: 'task_search' }, mode: 'advanced', }, @@ -707,75 +999,120 @@ Return ONLY the timestamp string in ISO 8601 format - no explanations, no quotes }, params: (params) => { const { apiKey, ...rest } = params + const parsedParams: Record = { apiKey, ...rest } - // Parse JSON inputs safely - const parsedParams: any = { apiKey, ...rest } + const parseJsonField = (field: string) => { + const value = (rest as Record)[field] + if (typeof value === 'string' && value.trim() !== '') { + parsedParams[field] = JSON.parse(value) + } + } try { - if (rest.person_titles && typeof rest.person_titles === 'string') { - parsedParams.person_titles = JSON.parse(rest.person_titles) - } - if (rest.person_locations && typeof rest.person_locations === 'string') { - parsedParams.person_locations = JSON.parse(rest.person_locations) - } - if (rest.person_seniorities && typeof rest.person_seniorities === 'string') { - parsedParams.person_seniorities = JSON.parse(rest.person_seniorities) - } - if (rest.organization_names && typeof rest.organization_names === 'string') { - parsedParams.organization_names = JSON.parse(rest.organization_names) - } - if (rest.organization_locations && typeof rest.organization_locations === 'string') { - parsedParams.organization_locations = JSON.parse(rest.organization_locations) - } - if ( - rest.organization_num_employees_ranges && - typeof rest.organization_num_employees_ranges === 'string' - ) { - parsedParams.organization_num_employees_ranges = JSON.parse( - rest.organization_num_employees_ranges - ) + for (const field of [ + 'person_titles', + 'person_locations', + 'person_seniorities', + 'organization_names', + 'organization_locations', + 'organization_not_locations', + 'organization_ids', + 'q_organization_domains_list', + 'contact_email_status', + 'organization_num_employees_ranges', + 'q_organization_keyword_tags', + 'contact_stage_ids', + 'contact_label_ids', + 'account_stage_ids', + 'account_label_ids', + 'people', + 'organizations', + 'contacts', + 'accounts', + 'contact_ids', + 'contact_attributes', + 'account_attributes', + 'label_names', + 'sequence_add_label_names', + 'append_label_names', + 'typed_custom_fields', + 'open_factor_names', + ]) { + parseJsonField(field) } - if ( - rest.q_organization_keyword_tags && - typeof rest.q_organization_keyword_tags === 'string' - ) { - parsedParams.q_organization_keyword_tags = JSON.parse(rest.q_organization_keyword_tags) - } - if (rest.contact_stage_ids && typeof rest.contact_stage_ids === 'string') { - parsedParams.contact_stage_ids = JSON.parse(rest.contact_stage_ids) - } - if (rest.account_stage_ids && typeof rest.account_stage_ids === 'string') { - parsedParams.account_stage_ids = JSON.parse(rest.account_stage_ids) - } - if (rest.people && typeof rest.people === 'string') { - parsedParams.people = JSON.parse(rest.people) - } - if (rest.organizations && typeof rest.organizations === 'string') { - parsedParams.organizations = JSON.parse(rest.organizations) + } catch (error) { + const message = error instanceof Error ? error.message : String(error) + throw new Error(`Invalid JSON input: ${message}`) + } + + const splitBulkUpdateInput = ( + raw: unknown + ): { ids?: string[]; attributes?: Array> } => { + if (!Array.isArray(raw)) return {} + const ids: string[] = [] + const attributes: Array> = [] + for (const item of raw) { + if (typeof item === 'string') { + ids.push(item) + continue + } + if (item && typeof item === 'object' && 'id' in item) { + const obj = item as Record + const id = obj.id + if (typeof id !== 'string') continue + const otherKeys = Object.keys(obj).filter((k) => k !== 'id') + if (otherKeys.length === 0) { + ids.push(id) + } else { + attributes.push(obj) + } + } } - if (rest.contacts && typeof rest.contacts === 'string') { - parsedParams.contacts = JSON.parse(rest.contacts) + return { + ids: ids.length > 0 ? ids : undefined, + attributes: attributes.length > 0 ? attributes : undefined, } - if (rest.accounts && typeof rest.accounts === 'string') { - parsedParams.accounts = JSON.parse(rest.accounts) + } + + if (params.operation === 'contact_bulk_update') { + const { ids, attributes } = splitBulkUpdateInput(parsedParams.contacts) + if (attributes) { + if (parsedParams.contact_attributes === undefined) { + parsedParams.contact_attributes = attributes + } + } else if (ids && parsedParams.contact_ids === undefined) { + parsedParams.contact_ids = ids } - if (rest.contact_ids && typeof rest.contact_ids === 'string') { - parsedParams.contact_ids = JSON.parse(rest.contact_ids) + parsedParams.contacts = undefined + } + + if (params.operation === 'account_bulk_update') { + const { ids, attributes } = splitBulkUpdateInput(parsedParams.accounts) + if (attributes) { + if (parsedParams.account_attributes === undefined) { + parsedParams.account_attributes = attributes + } + } else if (ids && parsedParams.account_ids === undefined) { + parsedParams.account_ids = ids } - if (rest.account_ids && typeof rest.account_ids === 'string') { - parsedParams.account_ids = JSON.parse(rest.account_ids) + parsedParams.accounts = undefined + if (rest.account_bulk_update_name) { + parsedParams.name = rest.account_bulk_update_name } - if (rest.stage_ids && typeof rest.stage_ids === 'string') { - parsedParams.stage_ids = JSON.parse(rest.stage_ids) + if (rest.account_bulk_update_owner_id) { + parsedParams.owner_id = rest.account_bulk_update_owner_id } - if (rest.owner_ids && typeof rest.owner_ids === 'string') { - parsedParams.owner_ids = JSON.parse(rest.owner_ids) + parsedParams.account_bulk_update_name = undefined + parsedParams.account_bulk_update_owner_id = undefined + } + + if (params.operation === 'contact_create') { + if (rest.contact_run_dedupe !== undefined) { + parsedParams.run_dedupe = rest.contact_run_dedupe } - } catch (error: any) { - throw new Error(`Invalid JSON input: ${error.message}`) + parsedParams.contact_run_dedupe = undefined } - // Map UI field names to API parameter names if (params.operation === 'account_create' || params.operation === 'account_update') { if (rest.account_name) parsedParams.name = rest.account_name parsedParams.account_name = undefined @@ -785,6 +1122,28 @@ Return ONLY the timestamp string in ISO 8601 format - no explanations, no quotes parsedParams.account_id = rest.account_id } + if (params.operation === 'sequence_add') { + if (parsedParams.sequence_add_label_names !== undefined) { + parsedParams.label_names = parsedParams.sequence_add_label_names + } + parsedParams.sequence_add_label_names = undefined + if (rest.sequence_user_id !== undefined && rest.sequence_user_id !== '') { + parsedParams.user_id = rest.sequence_user_id + } + parsedParams.sequence_user_id = undefined + if (rest.sequence_status !== undefined && rest.sequence_status !== '') { + parsedParams.status = rest.sequence_status + } + parsedParams.sequence_status = undefined + } + + if (params.operation === 'task_create') { + if (rest.task_notes !== undefined) { + parsedParams.note = rest.task_notes + } + parsedParams.task_notes = undefined + } + if ( params.operation === 'opportunity_create' || params.operation === 'opportunity_update' @@ -793,12 +1152,12 @@ Return ONLY the timestamp string in ISO 8601 format - no explanations, no quotes parsedParams.opportunity_name = undefined } - // Convert page/per_page to numbers if provided if (parsedParams.page) parsedParams.page = Number(parsedParams.page) if (parsedParams.per_page) parsedParams.per_page = Number(parsedParams.per_page) - // Convert amount to number if provided - if (parsedParams.amount) parsedParams.amount = Number(parsedParams.amount) + if (parsedParams.amount !== undefined && parsedParams.amount !== '') { + parsedParams.amount = String(parsedParams.amount) + } return parsedParams }, diff --git a/apps/sim/lib/workflows/migrations/subblock-migrations.ts b/apps/sim/lib/workflows/migrations/subblock-migrations.ts index 0d49aeefb36..f62e2b59839 100644 --- a/apps/sim/lib/workflows/migrations/subblock-migrations.ts +++ b/apps/sim/lib/workflows/migrations/subblock-migrations.ts @@ -37,6 +37,16 @@ export const SUBBLOCK_ID_MIGRATIONS: Record> = { 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', diff --git a/apps/sim/tools/apollo/account_bulk_create.ts b/apps/sim/tools/apollo/account_bulk_create.ts index 6b8cc45f4d4..68a9b3617fa 100644 --- a/apps/sim/tools/apollo/account_bulk_create.ts +++ b/apps/sim/tools/apollo/account_bulk_create.ts @@ -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 = { + 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', }, }, } diff --git a/apps/sim/tools/apollo/account_bulk_update.ts b/apps/sim/tools/apollo/account_bulk_update.ts index a78d4f8c45c..8a2df0b8dcd 100644 --- a/apps/sim/tools/apollo/account_bulk_update.ts +++ b/apps/sim/tools/apollo/account_bulk_update.ts @@ -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 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 accounts to update (max 1000). Each account must include id field, and optionally name, website_url, phone, owner_id', + '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 = {} + 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: { - type: 'json', - description: 'Array of successfully updated accounts', + message: { + type: 'string', + description: 'Confirmation message from Apollo', + optional: true, }, - failed_accounts: { + account_ids: { 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', }, }, } diff --git a/apps/sim/tools/apollo/account_create.ts b/apps/sim/tools/apollo/account_create.ts index eb341844098..220dfccc606 100644 --- a/apps/sim/tools/apollo/account_create.ts +++ b/apps/sim/tools/apollo/account_create.ts @@ -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 = { 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, }, } }, diff --git a/apps/sim/tools/apollo/account_search.ts b/apps/sim/tools/apollo/account_search.ts index 78bfa0ce447..e60686d9347 100644 --- a/apps/sim/tools/apollo/account_search.ts +++ b/apps/sim/tools/apollo/account_search.ts @@ -18,23 +18,36 @@ 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', + description: 'Filter accounts by organization name (partial-match search)', }, - owner_id: { - type: 'string', + account_stage_ids: { + type: 'array', required: false, visibility: 'user-only', - description: 'Filter by account owner user ID', + description: 'Filter by account stage IDs', }, - account_stage_ids: { + account_label_ids: { type: 'array', required: false, visibility: 'user-only', - description: 'Filter by account stage IDs', + 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', @@ -59,15 +72,19 @@ export const apolloAccountSearchTool: ToolConfig< 'X-Api-Key': params.apiKey, }), body: (params: ApolloAccountSearchParams) => { - const body: any = { + const body: Record = { 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 }, }, diff --git a/apps/sim/tools/apollo/account_update.ts b/apps/sim/tools/apollo/account_update.ts index 996e80cc646..acaef42cc60 100644 --- a/apps/sim/tools/apollo/account_update.ts +++ b/apps/sim/tools/apollo/account_update.ts @@ -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 = {} 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, }, } }, diff --git a/apps/sim/tools/apollo/contact_bulk_create.ts b/apps/sim/tools/apollo/contact_bulk_create.ts index b16a4e94c13..5a012485741 100644 --- a/apps/sim/tools/apollo/contact_bulk_create.ts +++ b/apps/sim/tools/apollo/contact_bulk_create.ts @@ -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 = { 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, }, } }, diff --git a/apps/sim/tools/apollo/contact_bulk_update.ts b/apps/sim/tools/apollo/contact_bulk_update.ts index 4f254cfc1f1..298873022b5 100644 --- a/apps/sim/tools/apollo/contact_bulk_update.ts +++ b/apps/sim/tools/apollo/contact_bulk_update.ts @@ -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 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: - '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', + '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 = {} + 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', - }, - 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', + message: { + type: 'string', + description: 'Confirmation message from Apollo', + optional: true, }, - 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, }, }, } diff --git a/apps/sim/tools/apollo/contact_create.ts b/apps/sim/tools/apollo/contact_create.ts index ecc61a0b219..c1ebb6e2b62 100644 --- a/apps/sim/tools/apollo/contact_create.ts +++ b/apps/sim/tools/apollo/contact_create.ts @@ -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 = { 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, }, } }, diff --git a/apps/sim/tools/apollo/contact_search.ts b/apps/sim/tools/apollo/contact_search.ts index e2c80604634..7295b745c50 100644 --- a/apps/sim/tools/apollo/contact_search.ts +++ b/apps/sim/tools/apollo/contact_search.ts @@ -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 = { 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 }, }, diff --git a/apps/sim/tools/apollo/contact_update.ts b/apps/sim/tools/apollo/contact_update.ts index 31ebd0c877f..3c99984ca6f 100644 --- a/apps/sim/tools/apollo/contact_update.ts +++ b/apps/sim/tools/apollo/contact_update.ts @@ -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 = {} 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, }, } }, diff --git a/apps/sim/tools/apollo/email_accounts.ts b/apps/sim/tools/apollo/email_accounts.ts index ceb854a602f..96b9b58ee94 100644 --- a/apps/sim/tools/apollo/email_accounts.ts +++ b/apps/sim/tools/apollo/email_accounts.ts @@ -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, }, } }, diff --git a/apps/sim/tools/apollo/opportunity_create.ts b/apps/sim/tools/apollo/opportunity_create.ts index 4880609e88f..5c949fc692c 100644 --- a/apps/sim/tools/apollo/opportunity_create.ts +++ b/apps/sim/tools/apollo/opportunity_create.ts @@ -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 = { 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, }, } }, diff --git a/apps/sim/tools/apollo/opportunity_get.ts b/apps/sim/tools/apollo/opportunity_get.ts index 4ef79fea54d..db8613b91eb 100644 --- a/apps/sim/tools/apollo/opportunity_get.ts +++ b/apps/sim/tools/apollo/opportunity_get.ts @@ -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' }, }, } diff --git a/apps/sim/tools/apollo/opportunity_search.ts b/apps/sim/tools/apollo/opportunity_search.ts index 8f1a6db88e9..c7fa7245292 100644 --- a/apps/sim/tools/apollo/opportunity_search.ts +++ b/apps/sim/tools/apollo/opportunity_search.ts @@ -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, }, } }, diff --git a/apps/sim/tools/apollo/opportunity_update.ts b/apps/sim/tools/apollo/opportunity_update.ts index 8bb31c901dd..aea000414f8 100644 --- a/apps/sim/tools/apollo/opportunity_update.ts +++ b/apps/sim/tools/apollo/opportunity_update.ts @@ -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 = {} 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, }, } }, diff --git a/apps/sim/tools/apollo/organization_bulk_enrich.ts b/apps/sim/tools/apollo/organization_bulk_enrich.ts index c72d31a0e89..d461318431c 100644 --- a/apps/sim/tools/apollo/organization_bulk_enrich.ts +++ b/apps/sim/tools/apollo/organization_bulk_enrich.ts @@ -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' }, }, } diff --git a/apps/sim/tools/apollo/organization_enrich.ts b/apps/sim/tools/apollo/organization_enrich.ts index d6c1de4f0d7..6c3702c56ab 100644 --- a/apps/sim/tools/apollo/organization_enrich.ts +++ b/apps/sim/tools/apollo/organization_enrich.ts @@ -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', diff --git a/apps/sim/tools/apollo/organization_search.ts b/apps/sim/tools/apollo/organization_search.ts index 18f31d376f0..1b6213039fb 100644 --- a/apps/sim/tools/apollo/organization_search.ts +++ b/apps/sim/tools/apollo/organization_search.ts @@ -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 = { 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 }, diff --git a/apps/sim/tools/apollo/people_bulk_enrich.ts b/apps/sim/tools/apollo/people_bulk_enrich.ts index cb6c35aa8ea..9c9721ff9a8 100644 --- a/apps/sim/tools/apollo/people_bulk_enrich.ts +++ b/apps/sim/tools/apollo/people_bulk_enrich.ts @@ -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, + }, }, } diff --git a/apps/sim/tools/apollo/people_enrich.ts b/apps/sim/tools/apollo/people_enrich.ts index 80e5f0322ee..c231bcf3c07 100644 --- a/apps/sim/tools/apollo/people_enrich.ts +++ b/apps/sim/tools/apollo/people_enrich.ts @@ -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 = {} 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' }, }, } diff --git a/apps/sim/tools/apollo/people_search.ts b/apps/sim/tools/apollo/people_search.ts index c4841024e8e..7e4bf528a32 100644 --- a/apps/sim/tools/apollo/people_search.ts +++ b/apps/sim/tools/apollo/people_search.ts @@ -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 = { 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 } diff --git a/apps/sim/tools/apollo/sequence_add_contacts.ts b/apps/sim/tools/apollo/sequence_add_contacts.ts index 652ddca2dc1..7008a5d894f 100644 --- a/apps/sim/tools/apollo/sequence_add_contacts.ts +++ b/apps/sim/tools/apollo/sequence_add_contacts.ts @@ -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.', + }, + send_email_from_email_address: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Specific email address to send from within the email account.', + }, + 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', }, - emailer_campaign_id: { + 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: 'Optional emailer campaign ID', + description: 'ID of the user performing the action', }, - send_email_from_user_id: { + status: { type: 'string', required: false, visibility: 'user-only', - description: 'User ID to send emails from', + 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' + ) + } + const body: Record = { + emailer_campaign_id: params.sequence_id, + send_email_from_email_account_id: params.send_email_from_email_account_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.emailer_campaign_id) { - body.emailer_campaign_id = params.emailer_campaign_id + 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.send_email_from_user_id) { - body.send_email_from_user_id = params.send_email_from_user_id + 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' }, }, } diff --git a/apps/sim/tools/apollo/sequence_search.ts b/apps/sim/tools/apollo/sequence_search.ts index 70c5a474d98..c7ed1a8b8ad 100644 --- a/apps/sim/tools/apollo/sequence_search.ts +++ b/apps/sim/tools/apollo/sequence_search.ts @@ -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 = { 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 }, }, diff --git a/apps/sim/tools/apollo/task_create.ts b/apps/sim/tools/apollo/task_create.ts index d835fe67574..f53b4599e76 100644 --- a/apps/sim/tools/apollo/task_create.ts +++ b/apps/sim/tools/apollo/task_create.ts @@ -4,7 +4,7 @@ import type { ToolConfig } from '@/tools/types' export const apolloTaskCreateTool: ToolConfig = { 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 { - 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 = { + 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 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' }, }, } diff --git a/apps/sim/tools/apollo/task_search.ts b/apps/sim/tools/apollo/task_search.ts index f85a16851de..6c05ad60db8 100644 --- a/apps/sim/tools/apollo/task_search.ts +++ b/apps/sim/tools/apollo/task_search.ts @@ -14,23 +14,18 @@ export const apolloTaskSearchTool: ToolConfig { - const body: any = { + const body: Record = { 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 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 + 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 + 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 + 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 } 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 + [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 + 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 } 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 } 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> 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 + 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 | 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 } 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 } 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 }