Skip to content

Commit 101b120

Browse files
committed
feat: Add LeadMagic integration plugin (#1)
- Add LeadMagic plugin with full API integration - People Operations: Find Email, Validate Email, Search Profile, Find Mobile, Find Role, B2B Profile Email - Company Operations: Search Company, Get Technographics, Get Company Funding - All actions use native fetch (no SDK dependencies) - Fix node-config-panel to clear old config when changing action types - Add LeadMagic logo asset
1 parent fc4eac1 commit 101b120

17 files changed

+1241
-3
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ Visit [http://localhost:3000](http://localhost:3000) to get started.
8686
- **Firecrawl**: Scrape URL, Search Web
8787
- **GitHub**: Create Issue, List Issues, Get Issue, Update Issue
8888
- **Instantly**: Create Lead, Get Lead, List Leads, Update Lead, Delete Lead, Update Lead Interest Status, Add Lead to Campaign, Create Campaign, Get Campaign, List Campaigns, Update Campaign, Delete Campaign, Activate Campaign, Pause Campaign, List Accounts, Get Account, Pause Account, Resume Account, Enable Warmup, Disable Warmup
89+
- **LeadMagic**: Find Email, Validate Email, Search Profile, Find Mobile, Find Role, B2B Profile Email, Search Company, Get Technographics, Get Company Funding
8990
- **Linear**: Create Ticket, Find Issues
9091
- **Perplexity**: Search Web, Ask Question, Research Topic
9192
- **Resend**: Send Email

components/workflow/node-config-panel.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -408,9 +408,13 @@ export const PanelInner = () => {
408408
if (selectedNode) {
409409
let newConfig = { ...selectedNode.data.config, [key]: value };
410410

411-
// When action type changes, clear the integrationId since it may not be valid for the new action
412-
if (key === "actionType" && selectedNode.data.config?.integrationId) {
413-
newConfig = { ...newConfig, integrationId: undefined };
411+
// When action type changes, clear ALL old config fields to prevent data pollution
412+
// Only keep actionType and integrationId (which will be cleared below if needed)
413+
if (key === "actionType") {
414+
newConfig = {
415+
actionType: value,
416+
integrationId: undefined, // Will be auto-selected if needed
417+
};
414418
}
415419

416420
updateNodeData({ id: selectedNode.id, data: { config: newConfig } });

plugins/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import "./fal";
2121
import "./firecrawl";
2222
import "./github";
2323
import "./instantly";
24+
import "./leadmagic";
2425
import "./linear";
2526
import "./perplexity";
2627
import "./resend";

plugins/leadmagic/credentials.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export type LeadMagicCredentials = {
2+
LEADMAGIC_API_KEY?: string;
3+
};

plugins/leadmagic/icon.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* eslint-disable @next/next/no-img-element */
2+
export function LeadMagicIcon({ className }: { className?: string }) {
3+
return (
4+
<img
5+
alt="LeadMagic"
6+
className={className}
7+
src="/leadmagic-logo.png"
8+
/>
9+
);
10+
}

plugins/leadmagic/index.ts

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
import type { IntegrationPlugin } from "../registry";
2+
import { registerIntegration } from "../registry";
3+
import { LeadMagicIcon } from "./icon";
4+
5+
const leadmagicPlugin: IntegrationPlugin = {
6+
type: "leadmagic",
7+
label: "LeadMagic",
8+
description: "B2B data enrichment and lead intelligence platform",
9+
10+
icon: LeadMagicIcon,
11+
12+
formFields: [
13+
{
14+
id: "apiKey",
15+
label: "API Key",
16+
type: "password",
17+
placeholder: "Your LeadMagic API key",
18+
configKey: "apiKey",
19+
envVar: "LEADMAGIC_API_KEY",
20+
helpText: "Get your API key from ",
21+
helpLink: {
22+
text: "leadmagic.io/dashboard",
23+
url: "https://leadmagic.io/dashboard",
24+
},
25+
},
26+
],
27+
28+
testConfig: {
29+
getTestFunction: async () => {
30+
const { testLeadMagic } = await import("./test");
31+
return testLeadMagic;
32+
},
33+
},
34+
35+
actions: [
36+
// People Operations
37+
{
38+
slug: "email-finder",
39+
label: "Find Email",
40+
description: "Find a person's email address using their LinkedIn profile URL",
41+
category: "LeadMagic",
42+
stepFunction: "emailFinderStep",
43+
stepImportPath: "email-finder",
44+
outputFields: [
45+
{ field: "email", description: "Email address" },
46+
{ field: "email_status", description: "Email status" },
47+
{ field: "first_name", description: "First name" },
48+
{ field: "last_name", description: "Last name" },
49+
{ field: "company", description: "Company" },
50+
{ field: "job_title", description: "Job title" },
51+
],
52+
configFields: [
53+
{
54+
key: "profile_url",
55+
label: "LinkedIn Profile URL",
56+
type: "template-input",
57+
placeholder: "https://linkedin.com/in/username or {{NodeName.profileUrl}}",
58+
required: true,
59+
},
60+
],
61+
},
62+
{
63+
slug: "email-validation",
64+
label: "Validate Email",
65+
description: "Validate an email address and check deliverability",
66+
category: "LeadMagic",
67+
stepFunction: "emailValidationStep",
68+
stepImportPath: "email-validation",
69+
outputFields: [
70+
{ field: "email", description: "Email address" },
71+
{ field: "status", description: "Validation status" },
72+
{ field: "is_valid", description: "Is valid" },
73+
{ field: "is_deliverable", description: "Is deliverable" },
74+
{ field: "is_catch_all", description: "Is catch-all" },
75+
{ field: "is_disposable", description: "Is disposable" },
76+
],
77+
configFields: [
78+
{
79+
key: "email",
80+
label: "Email Address",
81+
type: "template-input",
82+
placeholder: "[email protected] or {{NodeName.email}}",
83+
required: true,
84+
},
85+
],
86+
},
87+
{
88+
slug: "profile-search",
89+
label: "Search Profile",
90+
description: "Get detailed profile information from a LinkedIn URL",
91+
category: "LeadMagic",
92+
stepFunction: "profileSearchStep",
93+
stepImportPath: "profile-search",
94+
outputFields: [
95+
{ field: "first_name", description: "First name" },
96+
{ field: "last_name", description: "Last name" },
97+
{ field: "headline", description: "Headline" },
98+
{ field: "location", description: "Location" },
99+
{ field: "company", description: "Company" },
100+
{ field: "job_title", description: "Job title" },
101+
{ field: "profile_picture", description: "Profile picture URL" },
102+
],
103+
configFields: [
104+
{
105+
key: "profile_url",
106+
label: "LinkedIn Profile URL",
107+
type: "template-input",
108+
placeholder: "https://linkedin.com/in/username or {{NodeName.profileUrl}}",
109+
required: true,
110+
},
111+
],
112+
},
113+
{
114+
slug: "mobile-finder",
115+
label: "Find Mobile",
116+
description: "Find a person's mobile phone number from their LinkedIn profile",
117+
category: "LeadMagic",
118+
stepFunction: "mobileFinderStep",
119+
stepImportPath: "mobile-finder",
120+
outputFields: [
121+
{ field: "mobile", description: "Mobile number" },
122+
{ field: "mobile_status", description: "Mobile status" },
123+
{ field: "first_name", description: "First name" },
124+
{ field: "last_name", description: "Last name" },
125+
],
126+
configFields: [
127+
{
128+
key: "profile_url",
129+
label: "LinkedIn Profile URL",
130+
type: "template-input",
131+
placeholder: "https://linkedin.com/in/username or {{NodeName.profileUrl}}",
132+
required: true,
133+
},
134+
],
135+
},
136+
{
137+
slug: "role-finder",
138+
label: "Find Role",
139+
description: "Find a person at a company by their role/title",
140+
category: "LeadMagic",
141+
stepFunction: "roleFinderStep",
142+
stepImportPath: "role-finder",
143+
outputFields: [
144+
{ field: "first_name", description: "First name" },
145+
{ field: "last_name", description: "Last name" },
146+
{ field: "email", description: "Email address" },
147+
{ field: "job_title", description: "Job title" },
148+
{ field: "linkedin_url", description: "LinkedIn URL" },
149+
],
150+
configFields: [
151+
{
152+
key: "company_name",
153+
label: "Company Name",
154+
type: "template-input",
155+
placeholder: "Acme Inc or {{NodeName.companyName}}",
156+
required: true,
157+
},
158+
{
159+
key: "role",
160+
label: "Role/Title",
161+
type: "template-input",
162+
placeholder: "CEO or {{NodeName.role}}",
163+
required: true,
164+
},
165+
],
166+
},
167+
{
168+
slug: "b2b-profile-email",
169+
label: "B2B Profile Email",
170+
description: "Get B2B profile data and email from a LinkedIn URL",
171+
category: "LeadMagic",
172+
stepFunction: "b2bProfileEmailStep",
173+
stepImportPath: "b2b-profile-email",
174+
outputFields: [
175+
{ field: "email", description: "Email address" },
176+
{ field: "first_name", description: "First name" },
177+
{ field: "last_name", description: "Last name" },
178+
{ field: "company", description: "Company" },
179+
{ field: "job_title", description: "Job title" },
180+
{ field: "linkedin_url", description: "LinkedIn URL" },
181+
],
182+
configFields: [
183+
{
184+
key: "profile_url",
185+
label: "LinkedIn Profile URL",
186+
type: "template-input",
187+
placeholder: "https://linkedin.com/in/username or {{NodeName.profileUrl}}",
188+
required: true,
189+
},
190+
],
191+
},
192+
// Company Operations
193+
{
194+
slug: "company-search",
195+
label: "Search Company",
196+
description: "Get company information from a domain or LinkedIn URL",
197+
category: "LeadMagic",
198+
stepFunction: "companySearchStep",
199+
stepImportPath: "company-search",
200+
outputFields: [
201+
{ field: "name", description: "Company name" },
202+
{ field: "domain", description: "Domain" },
203+
{ field: "industry", description: "Industry" },
204+
{ field: "employee_count", description: "Employee count" },
205+
{ field: "location", description: "Location" },
206+
{ field: "description", description: "Description" },
207+
{ field: "linkedin_url", description: "LinkedIn URL" },
208+
],
209+
configFields: [
210+
{
211+
key: "domain",
212+
label: "Company Domain",
213+
type: "template-input",
214+
placeholder: "example.com or {{NodeName.domain}}",
215+
},
216+
{
217+
key: "linkedin_url",
218+
label: "LinkedIn Company URL",
219+
type: "template-input",
220+
placeholder: "https://linkedin.com/company/example or {{NodeName.linkedinUrl}}",
221+
},
222+
],
223+
},
224+
{
225+
slug: "technographics",
226+
label: "Get Technographics",
227+
description: "Get technology stack information for a company",
228+
category: "LeadMagic",
229+
stepFunction: "technographicsStep",
230+
stepImportPath: "technographics",
231+
outputFields: [
232+
{ field: "domain", description: "Domain" },
233+
{ field: "technologies", description: "Technologies" },
234+
{ field: "categories", description: "Categories" },
235+
],
236+
configFields: [
237+
{
238+
key: "domain",
239+
label: "Company Domain",
240+
type: "template-input",
241+
placeholder: "example.com or {{NodeName.domain}}",
242+
required: true,
243+
},
244+
],
245+
},
246+
{
247+
slug: "company-funding",
248+
label: "Get Company Funding",
249+
description: "Get funding information for a company",
250+
category: "LeadMagic",
251+
stepFunction: "companyFundingStep",
252+
stepImportPath: "company-funding",
253+
outputFields: [
254+
{ field: "company_name", description: "Company name" },
255+
{ field: "total_funding", description: "Total funding" },
256+
{ field: "last_funding_date", description: "Last funding date" },
257+
{ field: "last_funding_amount", description: "Last funding amount" },
258+
{ field: "funding_rounds", description: "Funding rounds" },
259+
],
260+
configFields: [
261+
{
262+
key: "domain",
263+
label: "Company Domain",
264+
type: "template-input",
265+
placeholder: "example.com or {{NodeName.domain}}",
266+
required: true,
267+
},
268+
],
269+
},
270+
],
271+
};
272+
273+
registerIntegration(leadmagicPlugin);
274+
275+
export default leadmagicPlugin;

0 commit comments

Comments
 (0)