Skip to content

Commit d414134

Browse files
committed
feat(enrichment): add ZeroBounce, NeverBounce, and MillionVerifier email verification
1 parent 12db7da commit d414134

28 files changed

Lines changed: 1258 additions & 0 deletions

File tree

apps/sim/app/(landing)/integrations/data/icon-mapping.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,13 @@ import {
124124
MicrosoftPlannerIcon,
125125
MicrosoftSharepointIcon,
126126
MicrosoftTeamsIcon,
127+
MillionVerifierIcon,
127128
MistralIcon,
128129
MondayIcon,
129130
MongoDBIcon,
130131
MySQLIcon,
131132
Neo4jIcon,
133+
NeverBounceIcon,
132134
NewRelicIcon,
133135
NotionIcon,
134136
ObsidianIcon,
@@ -209,6 +211,7 @@ import {
209211
YouTubeIcon,
210212
ZendeskIcon,
211213
ZepIcon,
214+
ZeroBounceIcon,
212215
ZoomIcon,
213216
ZoomInfoIcon,
214217
} from '@/components/icons'
@@ -336,10 +339,12 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
336339
microsoft_excel_v2: MicrosoftExcelIcon,
337340
microsoft_planner: MicrosoftPlannerIcon,
338341
microsoft_teams: MicrosoftTeamsIcon,
342+
millionverifier: MillionVerifierIcon,
339343
mistral_parse_v3: MistralIcon,
340344
monday: MondayIcon,
341345
mongodb: MongoDBIcon,
342346
mysql: MySQLIcon,
347+
neverbounce: NeverBounceIcon,
343348
neo4j: Neo4jIcon,
344349
new_relic: NewRelicIcon,
345350
notion_v2: NotionIcon,
@@ -421,6 +426,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
421426
workday: WorkdayIcon,
422427
x: xIcon,
423428
youtube: YouTubeIcon,
429+
zerobounce: ZeroBounceIcon,
424430
zendesk: ZendeskIcon,
425431
zep: ZepIcon,
426432
zoom: ZoomIcon,

apps/sim/app/workspace/[workspaceId]/settings/components/byok/byok.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ import {
2929
ImageIcon,
3030
JinaAIIcon,
3131
LinkupIcon,
32+
MillionVerifierIcon,
3233
MistralIcon,
34+
NeverBounceIcon,
3335
OllamaIcon,
3436
OpenAIIcon,
3537
ParallelIcon,
@@ -39,6 +41,7 @@ import {
3941
SerperIcon,
4042
TogetherIcon,
4143
WizaIcon,
44+
ZeroBounceIcon,
4245
} from '@/components/icons'
4346
import { Input } from '@/components/ui'
4447
import { BYOKKeySkeleton } from '@/app/workspace/[workspaceId]/settings/components/byok/byok-skeleton'
@@ -220,6 +223,27 @@ const PROVIDERS: {
220223
description: 'Prospect search, individual reveal, and company enrichment',
221224
placeholder: 'Enter your Wiza API key',
222225
},
226+
{
227+
id: 'zerobounce',
228+
name: 'ZeroBounce',
229+
icon: ZeroBounceIcon,
230+
description: 'Real-time email validation and deliverability checks',
231+
placeholder: 'Enter your ZeroBounce API key',
232+
},
233+
{
234+
id: 'neverbounce',
235+
name: 'NeverBounce',
236+
icon: NeverBounceIcon,
237+
description: 'Real-time email verification and list cleaning',
238+
placeholder: 'Enter your NeverBounce API key',
239+
},
240+
{
241+
id: 'millionverifier',
242+
name: 'MillionVerifier',
243+
icon: MillionVerifierIcon,
244+
description: 'Real-time email verification and deliverability checks',
245+
placeholder: 'Enter your MillionVerifier API key',
246+
},
223247
]
224248

225249
export function BYOK() {
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { MillionVerifierIcon } from '@/components/icons'
2+
import { AuthMode, type BlockConfig, IntegrationType } from '@/blocks/types'
3+
import type { MillionVerifierResponse } from '@/tools/millionverifier/types'
4+
5+
export const MillionVerifierBlock: BlockConfig<MillionVerifierResponse> = {
6+
type: 'millionverifier',
7+
name: 'MillionVerifier',
8+
description: 'Verify email deliverability and check account credits',
9+
authMode: AuthMode.ApiKey,
10+
longDescription:
11+
'Integrate MillionVerifier to verify email deliverability in real time — classify addresses as valid, invalid, catch-all, disposable, or unknown — and check your remaining verification credits.',
12+
docsLink: 'https://docs.sim.ai/tools/millionverifier',
13+
category: 'tools',
14+
integrationType: IntegrationType.Sales,
15+
tags: ['enrichment', 'sales-engagement'],
16+
bgColor: '#00B67A',
17+
icon: MillionVerifierIcon,
18+
subBlocks: [
19+
{
20+
id: 'operation',
21+
title: 'Operation',
22+
type: 'dropdown',
23+
options: [
24+
{ label: 'Verify Email', id: 'millionverifier_verify_email' },
25+
{ label: 'Get Remaining Credits', id: 'millionverifier_get_credits' },
26+
],
27+
value: () => 'millionverifier_verify_email',
28+
},
29+
{
30+
id: 've_email',
31+
title: 'Email Address',
32+
type: 'short-input',
33+
required: true,
34+
placeholder: 'john@example.com',
35+
condition: { field: 'operation', value: 'millionverifier_verify_email' },
36+
},
37+
// API Key — hidden on hosted Sim for operations with hosted-key support
38+
{
39+
id: 'apiKey',
40+
title: 'API Key',
41+
type: 'short-input',
42+
required: true,
43+
placeholder: 'Enter your MillionVerifier API key',
44+
password: true,
45+
hideWhenHosted: true,
46+
condition: { field: 'operation', value: 'millionverifier_get_credits', not: true },
47+
},
48+
// API Key — always required for the credit-balance lookup (no hosted key)
49+
{
50+
id: 'apiKey',
51+
title: 'API Key',
52+
type: 'short-input',
53+
required: true,
54+
placeholder: 'Enter your MillionVerifier API key',
55+
password: true,
56+
condition: { field: 'operation', value: 'millionverifier_get_credits' },
57+
},
58+
],
59+
tools: {
60+
access: ['millionverifier_verify_email', 'millionverifier_get_credits'],
61+
config: {
62+
tool: (params) => {
63+
switch (params.operation) {
64+
case 'millionverifier_verify_email':
65+
case 'millionverifier_get_credits':
66+
return params.operation
67+
default:
68+
return 'millionverifier_verify_email'
69+
}
70+
},
71+
params: (params) => {
72+
const { operation: _operation, ...rest } = params
73+
const idToParam: Record<string, string> = { ve_email: 'email' }
74+
const result: Record<string, unknown> = {}
75+
for (const [key, value] of Object.entries(rest)) {
76+
if (value === undefined || value === null || value === '') continue
77+
const mappedKey = idToParam[key] ?? key
78+
result[mappedKey] = value
79+
}
80+
return result
81+
},
82+
},
83+
},
84+
inputs: {
85+
operation: { type: 'string', description: 'Operation to perform' },
86+
apiKey: { type: 'string', description: 'MillionVerifier API key' },
87+
ve_email: { type: 'string', description: 'Email address to verify' },
88+
},
89+
outputs: {
90+
email: { type: 'string', description: 'The verified email address' },
91+
status: { type: 'string', description: 'Verification status' },
92+
deliverable: {
93+
type: 'boolean',
94+
description: 'Whether the email is valid and safe to send',
95+
},
96+
freeEmail: { type: 'boolean', description: 'Whether on a free email provider' },
97+
roleAccount: { type: 'boolean', description: 'Whether the address is a role account' },
98+
didYouMean: { type: 'string', description: 'Suggested correction' },
99+
subResult: { type: 'string', description: 'Additional classification detail' },
100+
credits: { type: 'number', description: 'Remaining verification credits' },
101+
},
102+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { NeverBounceIcon } from '@/components/icons'
2+
import { AuthMode, type BlockConfig, IntegrationType } from '@/blocks/types'
3+
import type { NeverBounceResponse } from '@/tools/neverbounce/types'
4+
5+
export const NeverBounceBlock: BlockConfig<NeverBounceResponse> = {
6+
type: 'neverbounce',
7+
name: 'NeverBounce',
8+
description: 'Verify email deliverability and check account credits',
9+
authMode: AuthMode.ApiKey,
10+
longDescription:
11+
'Integrate NeverBounce to verify email deliverability in real time — classify addresses as valid, invalid, catch-all, disposable, or unknown — and check your remaining verification credits.',
12+
docsLink: 'https://docs.sim.ai/tools/neverbounce',
13+
category: 'tools',
14+
integrationType: IntegrationType.Sales,
15+
tags: ['enrichment', 'sales-engagement'],
16+
bgColor: '#1A6DF0',
17+
icon: NeverBounceIcon,
18+
subBlocks: [
19+
{
20+
id: 'operation',
21+
title: 'Operation',
22+
type: 'dropdown',
23+
options: [
24+
{ label: 'Verify Email', id: 'neverbounce_verify_email' },
25+
{ label: 'Get Remaining Credits', id: 'neverbounce_get_credits' },
26+
],
27+
value: () => 'neverbounce_verify_email',
28+
},
29+
{
30+
id: 've_email',
31+
title: 'Email Address',
32+
type: 'short-input',
33+
required: true,
34+
placeholder: 'john@example.com',
35+
condition: { field: 'operation', value: 'neverbounce_verify_email' },
36+
},
37+
// API Key — hidden on hosted Sim for operations with hosted-key support
38+
{
39+
id: 'apiKey',
40+
title: 'API Key',
41+
type: 'short-input',
42+
required: true,
43+
placeholder: 'Enter your NeverBounce API key',
44+
password: true,
45+
hideWhenHosted: true,
46+
condition: { field: 'operation', value: 'neverbounce_get_credits', not: true },
47+
},
48+
// API Key — always required for the credit-balance lookup (no hosted key)
49+
{
50+
id: 'apiKey',
51+
title: 'API Key',
52+
type: 'short-input',
53+
required: true,
54+
placeholder: 'Enter your NeverBounce API key',
55+
password: true,
56+
condition: { field: 'operation', value: 'neverbounce_get_credits' },
57+
},
58+
],
59+
tools: {
60+
access: ['neverbounce_verify_email', 'neverbounce_get_credits'],
61+
config: {
62+
tool: (params) => {
63+
switch (params.operation) {
64+
case 'neverbounce_verify_email':
65+
case 'neverbounce_get_credits':
66+
return params.operation
67+
default:
68+
return 'neverbounce_verify_email'
69+
}
70+
},
71+
params: (params) => {
72+
const { operation: _operation, ...rest } = params
73+
const idToParam: Record<string, string> = { ve_email: 'email' }
74+
const result: Record<string, unknown> = {}
75+
for (const [key, value] of Object.entries(rest)) {
76+
if (value === undefined || value === null || value === '') continue
77+
const mappedKey = idToParam[key] ?? key
78+
result[mappedKey] = value
79+
}
80+
return result
81+
},
82+
},
83+
},
84+
inputs: {
85+
operation: { type: 'string', description: 'Operation to perform' },
86+
apiKey: { type: 'string', description: 'NeverBounce API key' },
87+
ve_email: { type: 'string', description: 'Email address to verify' },
88+
},
89+
outputs: {
90+
email: { type: 'string', description: 'The verified email address' },
91+
status: { type: 'string', description: 'Verification status' },
92+
deliverable: {
93+
type: 'boolean',
94+
description: 'Whether the email is valid and safe to send',
95+
},
96+
roleAccount: { type: 'boolean', description: 'Whether the address is a role account' },
97+
freeEmail: { type: 'boolean', description: 'Whether on a free email provider' },
98+
didYouMean: { type: 'string', description: 'Suggested correction' },
99+
flags: { type: 'array', description: 'Raw NeverBounce flags' },
100+
credits: { type: 'number', description: 'Remaining paid verification credits' },
101+
freeCredits: { type: 'number', description: 'Remaining free verification credits' },
102+
},
103+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { ZeroBounceIcon } from '@/components/icons'
2+
import { AuthMode, type BlockConfig, IntegrationType } from '@/blocks/types'
3+
import type { ZeroBounceResponse } from '@/tools/zerobounce/types'
4+
5+
export const ZeroBounceBlock: BlockConfig<ZeroBounceResponse> = {
6+
type: 'zerobounce',
7+
name: 'ZeroBounce',
8+
description: 'Validate email deliverability and check account credits',
9+
authMode: AuthMode.ApiKey,
10+
longDescription:
11+
'Integrate ZeroBounce to validate email deliverability in real time — detect invalid, catch-all, spamtrap, abuse, and do-not-mail addresses — and check your remaining validation credits.',
12+
docsLink: 'https://docs.sim.ai/tools/zerobounce',
13+
category: 'tools',
14+
integrationType: IntegrationType.Sales,
15+
tags: ['enrichment', 'sales-engagement'],
16+
bgColor: '#00B894',
17+
icon: ZeroBounceIcon,
18+
subBlocks: [
19+
{
20+
id: 'operation',
21+
title: 'Operation',
22+
type: 'dropdown',
23+
options: [
24+
{ label: 'Verify Email', id: 'zerobounce_verify_email' },
25+
{ label: 'Get Remaining Credits', id: 'zerobounce_get_credits' },
26+
],
27+
value: () => 'zerobounce_verify_email',
28+
},
29+
{
30+
id: 've_email',
31+
title: 'Email Address',
32+
type: 'short-input',
33+
required: true,
34+
placeholder: 'john@example.com',
35+
condition: { field: 'operation', value: 'zerobounce_verify_email' },
36+
},
37+
// API Key — hidden on hosted Sim for operations with hosted-key support
38+
{
39+
id: 'apiKey',
40+
title: 'API Key',
41+
type: 'short-input',
42+
required: true,
43+
placeholder: 'Enter your ZeroBounce API key',
44+
password: true,
45+
hideWhenHosted: true,
46+
condition: { field: 'operation', value: 'zerobounce_get_credits', not: true },
47+
},
48+
// API Key — always required for the credit-balance lookup (no hosted key)
49+
{
50+
id: 'apiKey',
51+
title: 'API Key',
52+
type: 'short-input',
53+
required: true,
54+
placeholder: 'Enter your ZeroBounce API key',
55+
password: true,
56+
condition: { field: 'operation', value: 'zerobounce_get_credits' },
57+
},
58+
],
59+
tools: {
60+
access: ['zerobounce_verify_email', 'zerobounce_get_credits'],
61+
config: {
62+
tool: (params) => {
63+
switch (params.operation) {
64+
case 'zerobounce_verify_email':
65+
case 'zerobounce_get_credits':
66+
return params.operation
67+
default:
68+
return 'zerobounce_verify_email'
69+
}
70+
},
71+
params: (params) => {
72+
const { operation: _operation, ...rest } = params
73+
const idToParam: Record<string, string> = { ve_email: 'email' }
74+
const result: Record<string, unknown> = {}
75+
for (const [key, value] of Object.entries(rest)) {
76+
if (value === undefined || value === null || value === '') continue
77+
const mappedKey = idToParam[key] ?? key
78+
result[mappedKey] = value
79+
}
80+
return result
81+
},
82+
},
83+
},
84+
inputs: {
85+
operation: { type: 'string', description: 'Operation to perform' },
86+
apiKey: { type: 'string', description: 'ZeroBounce API key' },
87+
ve_email: { type: 'string', description: 'Email address to validate' },
88+
},
89+
outputs: {
90+
email: { type: 'string', description: 'The validated email address' },
91+
status: { type: 'string', description: 'Validation status' },
92+
deliverable: {
93+
type: 'boolean',
94+
description: 'Whether the email is valid and safe to send',
95+
},
96+
subStatus: { type: 'string', description: 'Detailed sub-status' },
97+
freeEmail: { type: 'boolean', description: 'Whether on a free email provider' },
98+
didYouMean: { type: 'string', description: 'Suggested correction' },
99+
credits: { type: 'number', description: 'Remaining validation credits' },
100+
},
101+
}

0 commit comments

Comments
 (0)