Files
simstudioai--sim/apps/sim/tools/apollo/sequence_add_contacts.ts
T
wehub-resource-sync d25d482dc2
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:20:55 +08:00

270 lines
9.3 KiB
TypeScript

import type {
ApolloSequenceAddContactsParams,
ApolloSequenceAddContactsResponse,
} from '@/tools/apollo/types'
import type { ToolConfig } from '@/tools/types'
export const apolloSequenceAddContactsTool: ToolConfig<
ApolloSequenceAddContactsParams,
ApolloSequenceAddContactsResponse
> = {
id: 'apollo_sequence_add_contacts',
name: 'Apollo Add Contacts to Sequence',
description: 'Add contacts to an Apollo sequence',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Apollo API key (master key required)',
},
sequence_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the sequence to add contacts to (e.g., "seq_abc123")',
},
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:
'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',
},
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: 'ID of the user performing the action',
},
status: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Initial status for added contacts: "active" or "paused"',
},
auto_unpause_at: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'ISO 8601 datetime to automatically unpause contacts',
},
},
request: {
/**
* Apollo documents every field for this endpoint as a query parameter (no request body),
* so `contact_ids[]`/`label_names[]` are appended to the query string alongside the scalar
* settings, matching the documented contract.
*/
url: (params: ApolloSequenceAddContactsParams) => {
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 qs = new URLSearchParams()
qs.set('emailer_campaign_id', params.sequence_id)
qs.set('send_email_from_email_account_id', params.send_email_from_email_account_id)
for (const id of params.contact_ids ?? []) {
if (typeof id === 'string' && id.length > 0) qs.append('contact_ids[]', id)
}
for (const name of params.label_names ?? []) {
if (typeof name === 'string' && name.length > 0) qs.append('label_names[]', name)
}
if (params.send_email_from_email_address) {
qs.set('send_email_from_email_address', params.send_email_from_email_address)
}
if (params.sequence_no_email !== undefined) {
qs.set('sequence_no_email', String(params.sequence_no_email))
}
if (params.sequence_unverified_email !== undefined) {
qs.set('sequence_unverified_email', String(params.sequence_unverified_email))
}
if (params.sequence_job_change !== undefined) {
qs.set('sequence_job_change', String(params.sequence_job_change))
}
if (params.sequence_active_in_other_campaigns !== undefined) {
qs.set(
'sequence_active_in_other_campaigns',
String(params.sequence_active_in_other_campaigns)
)
}
if (params.sequence_finished_in_other_campaigns !== undefined) {
qs.set(
'sequence_finished_in_other_campaigns',
String(params.sequence_finished_in_other_campaigns)
)
}
if (params.sequence_same_company_in_same_campaign !== undefined) {
qs.set(
'sequence_same_company_in_same_campaign',
String(params.sequence_same_company_in_same_campaign)
)
}
if (params.contacts_without_ownership_permission !== undefined) {
qs.set(
'contacts_without_ownership_permission',
String(params.contacts_without_ownership_permission)
)
}
if (params.add_if_in_queue !== undefined) {
qs.set('add_if_in_queue', String(params.add_if_in_queue))
}
if (params.contact_verification_skipped !== undefined) {
qs.set('contact_verification_skipped', String(params.contact_verification_skipped))
}
if (params.user_id) qs.set('user_id', params.user_id)
if (params.status) qs.set('status', params.status)
if (params.auto_unpause_at) qs.set('auto_unpause_at', params.auto_unpause_at)
return `https://api.apollo.io/api/v1/emailer_campaigns/${params.sequence_id.trim()}/add_contact_ids?${qs.toString()}`
},
method: 'POST',
headers: (params: ApolloSequenceAddContactsParams) => ({
'Cache-Control': 'no-cache',
'X-Api-Key': params.apiKey,
}),
},
transformResponse: async (response: Response, params?: ApolloSequenceAddContactsParams) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Apollo API error: ${response.status} - ${errorText}`)
}
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: {
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: {
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' },
},
}