Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import app from "../../apollo_io_oauth.app.mjs";

export default {
key: "apollo_io_oauth-add-contacts-to-sequence",
name: "Add Contacts to Sequence",
description:
"Adds one or more contacts to an email outreach sequence in"
+ " Apollo. Requires a sequence ID, contact IDs, and a"
+ " sending email account ID."
+ " Use **List Metadata** (type `sequences`) to find"
+ " sequence IDs, (type `email_accounts`) to find email"
+ " account IDs."
+ " Use **Search Contacts** to find contact IDs to enroll."
+ " Set `sequenceActiveInOtherCampaigns` to `true` to"
+ " enroll contacts already active in other sequences."
+ " [See the documentation](https://docs.apollo.io/reference"
+ "/add-contacts-to-sequence)",
version: "0.0.1",
type: "action",
annotations: {
destructiveHint: false,
openWorldHint: true,
readOnlyHint: false,
},
props: {
app,
sequenceId: {
type: "string",
label: "Sequence ID",
description:
"The ID of the sequence to add contacts to."
+ " Use **List Metadata** (type `sequences`) to find"
+ " available sequences.",
},
contactIds: {
type: "string[]",
label: "Contact IDs",
description:
"One or more contact IDs to enroll in the sequence."
+ " Use **Search Contacts** to find contact IDs.",
},
Comment thread
GTFalcao marked this conversation as resolved.
emailAccountId: {
type: "string",
label: "Email Account ID",
description:
"The ID of the email account to send from."
+ " Use **List Metadata** (type `email_accounts`) to"
+ " find available sending accounts.",
},
sequenceNoEmail: {
type: "boolean",
label: "Sequence Without Email",
description:
"Set to `true` to sequence contacts even if they don't"
+ " have an email address.",
optional: true,
},
sequenceActiveInOtherCampaigns: {
type: "boolean",
label: "Sequence Active in Other Campaigns",
description:
"Set to `true` to enroll contacts who are already"
+ " active or paused in another sequence.",
optional: true,
},
sequenceFinishedInOtherCampaigns: {
type: "boolean",
label: "Sequence Finished in Other Campaigns",
description:
"Set to `true` to enroll contacts who have already"
+ " finished another sequence.",
optional: true,
},
},
async run({ $ }) {
const response = await this.app.addContactsToSequence({
$,
sequenceId: this.sequenceId,
data: {
contact_ids: this.contactIds,
emailer_campaign_id: this.sequenceId,
send_email_from_email_account_id: this.emailAccountId,
sequence_no_email: this.sequenceNoEmail,
sequence_active_in_other_campaigns:
this.sequenceActiveInOtherCampaigns,
sequence_finished_in_other_campaigns:
this.sequenceFinishedInOtherCampaigns,
},
});
Comment on lines +75 to +89
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider validating non-empty contactIds before API call.

The description states "One or more contact IDs" but there's no guard against an empty array. While the API may reject it, a local check provides clearer error messaging and faster feedback.

🛡️ Suggested validation
   async run({ $ }) {
+    if (!this.contactIds?.length) {
+      throw new Error("At least one contact ID is required.");
+    }
+
     const response = await this.app.addContactsToSequence({
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@components/apollo_io_oauth/actions/add-contacts-to-sequence/add-contacts-to-sequence.mjs`
around lines 75 - 89, Before calling this.app.addContactsToSequence inside the
async run method, add a guard that validates this.contactIds is a non-empty
array (or contains at least one ID); if it's missing or has length === 0, abort
early and surface a clear error (e.g., throw an Error or return a failed action)
that mentions "contactIds must contain one or more contact IDs". Modify the run
function's pre-call logic to perform the check and avoid invoking
addContactsToSequence when contactIds is empty.

const contacts = response?.contacts ?? [];

$.export(
"$summary",
`Added ${contacts?.length} contact${contacts?.length === 1
? ""
: "s"} to sequence`,
);

return contacts;
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import app from "../../apollo_io_oauth.app.mjs";

export default {
key: "apollo_io_oauth-create-or-update-account",
name: "Create or Update Account",
description:
"Creates a new account (company) or updates an existing one"
+ " in your Apollo CRM. To create, omit `accountId` and"
+ " provide at least a `name`. To update, provide the"
+ " `accountId` and any fields to change."
+ " Use **Search Accounts** to find existing accounts before"
+ " updating."
+ " Use **List Metadata** (type `account_stages`) to"
+ " discover valid stage IDs."
+ " [See the documentation](https://docs.apollo.io/reference"
+ "/create-an-account)",
version: "0.0.1",
type: "action",
annotations: {
destructiveHint: false,
openWorldHint: true,
readOnlyHint: false,
},
props: {
app,
accountId: {
type: "string",
label: "Account ID",
description:
"The ID of an existing account to update. Omit this to"
+ " create a new account.",
optional: true,
},
name: {
type: "string",
label: "Name",
description:
"The name of the account/company. Required when"
+ " creating.",
optional: true,
},
domain: {
type: "string",
label: "Domain",
description:
"The company's domain. Example: `\"acme.com\"`. Apollo"
+ " uses this for enrichment.",
optional: true,
},
phone: {
type: "string",
label: "Phone",
description: "The company's phone number.",
optional: true,
},
},
async run({ $ }) {
const data = {
name: this.name,
domain: this.domain,
phone: this.phone,
};

let account;

if (this.accountId) {
({ account } = await this.app.updateAccount({
$,
accountId: this.accountId,
data,
}));
$.export(
"$summary",
`Updated account ${account.id}: ${account.name}`,
);
} else {
({ account } = await this.app.createAccount({
$,
data,
}));
$.export(
"$summary",
`Created account ${account.id}: ${account.name}`,
);
}

return account;
Comment thread
GTFalcao marked this conversation as resolved.
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import app from "../../apollo_io_oauth.app.mjs";

export default {
key: "apollo_io_oauth-create-or-update-contact",
name: "Create or Update Contact",
description:
"Creates a new contact or updates an existing one in your"
+ " Apollo CRM. To create, omit `contactId` and provide at"
+ " least an `email`. To update, provide the `contactId`"
+ " and any fields to change."
+ " Use **Search Contacts** to find existing contacts by"
+ " name or email before updating."
+ " Use **List Metadata** (type `contact_stages`) to"
+ " discover valid stage IDs."
+ " The `accountId` links this contact to a company — use"
+ " **Search Accounts** to find the account ID."
+ " [See the documentation](https://docs.apollo.io/reference"
+ "/create-a-contact)",
version: "0.0.1",
type: "action",
annotations: {
destructiveHint: false,
openWorldHint: true,
readOnlyHint: false,
},
props: {
app,
contactId: {
type: "string",
label: "Contact ID",
description:
"The ID of an existing contact to update. Omit this to"
+ " create a new contact.",
optional: true,
},
email: {
type: "string",
label: "Email",
description:
"Email address of the contact. Required when creating.",
optional: true,
},
firstName: {
type: "string",
label: "First Name",
description: "First name of the contact.",
optional: true,
},
lastName: {
type: "string",
label: "Last Name",
description: "Last name of the contact.",
optional: true,
},
title: {
type: "string",
label: "Title",
description:
"Job title of the contact. Example: `\"VP of Sales\"`.",
optional: true,
},
accountId: {
type: "string",
label: "Account ID",
description:
"ID of the account (company) to link this contact to."
+ " Use **Search Accounts** to find account IDs.",
optional: true,
},
websiteUrl: {
type: "string",
label: "Website URL",
description:
"The organization website for Apollo to enrich data."
+ " Do NOT pass personal social media URLs. This is"
+ " ignored if a valid email is provided.",
optional: true,
},
labelNames: {
type: "string[]",
label: "Label Names",
description:
"Tag names to apply to this contact. Example:"
+ " `[\"VIP\", \"Decision Maker\"]`."
+ " Use **List Metadata** (type `labels`) to see"
+ " existing labels.",
optional: true,
},
contactStageId: {
type: "string",
label: "Contact Stage ID",
description:
"The pipeline stage for this contact."
+ " Use **List Metadata** (type `contact_stages`) to"
+ " discover valid stage IDs.",
optional: true,
},
address: {
type: "string",
label: "Address",
description:
"Full address string. Apollo will infer city, state,"
+ " country, and timezone.",
optional: true,
},
phone: {
type: "string",
label: "Phone",
description: "Direct dial phone number for this contact.",
optional: true,
},
},
async run({ $ }) {
const data = {
email: this.email,
first_name: this.firstName,
last_name: this.lastName,
title: this.title,
account_id: this.accountId,
website_url: this.websiteUrl,
label_names: this.labelNames,
contact_stage_id: this.contactStageId,
present_raw_address: this.address,
direct_phone: this.phone,
};

let contact;

if (this.contactId) {
({ contact } = await this.app.updateContact({
$,
contactId: this.contactId,
data,
}));
$.export(
"$summary",
`Updated contact ${contact.id}: ${contact.name || contact.email}`,
);
} else {
({ contact } = await this.app.createContact({
$,
data,
}));
$.export(
"$summary",
`Created contact ${contact.id}: ${contact.name || contact.email}`,
);
}

return contact;
},
};
Loading
Loading