Verify Emails in a Pipedream Workflow: HTTP Trigger and a Node.js Code Step
To verify emails in Pipedream, start a workflow with an HTTP / Webhook trigger, add a Node.js code step that sends the address to an email verification API, and end the run early for anything that is not valid. Keep the key in a secret environment variable, answer the sender immediately, and let the verification happen after the response so a slow mail server never blocks a form.
This guide builds that flow with the SimpleVerifier API. Pipedream is a good fit when you want code and a hosted trigger with no server, for example to sit between a form tool and your CRM. If you prefer a visual builder, we have guides for n8n and Make, and the Zapier, Make or webhook guide compares them.
What you need
- A SimpleVerifier plan with API access: Pro, Business or Agency. Pro is $39 a month. It includes 10,000 API calls a month, then $1.50 per 1,000 on monthly billing. Pro email verification also runs the first 100,000 checks a month at full speed, then slower, never cut off. See pricing.
- An API key from the API keys page. It starts with
sv_live_and is shown once. - A Pipedream account, and something that can send a webhook with an
emailfield (a form tool, your app, or curl).
The endpoint is GET or POST https://simpleverifier.com/api/v1/verify with email and a Bearer key. The full reference is in the API docs.
Step 1: Store the key
In Pipedream, open Settings, then Environment Variables, and choose New Variable. Name it SIMPLEVERIFIER_KEY and paste the key. Secret is on by default, so the value is never shown in the UI again. You can also scope variables to a project from the project's Variables page.
Pipedream's docs give two cautions worth repeating. Names are case-sensitive, and process.env only works inside the component's run function. And anything you console.log ends up in the step's logs, so never log the key.
Step 2: The trigger
Create a workflow and pick HTTP / Webhook as the trigger. Pipedream gives you a URL. Whatever JSON is sent to it appears at steps.trigger.event.body. Test it with curl, replacing the URL with yours:
curl -X POST "https://YOUR-ENDPOINT.m.pipedream.net" \
-H "Content-Type: application/json" \
-d '{"email": "jane@company.com", "name": "Jane", "source": "demo-form"}'
Your form tool or app should send the same shape. Select the event in Pipedream so the later steps can see the fields.
Step 3: The verification code step
Add a Node.js code step. Every Pipedream code step uses defineComponent with an async run. Paste this.
import axios from "axios";
const ENDPOINT = "https://simpleverifier.com/api/v1/verify";
export default defineComponent({
async run({ steps, $ }) {
await $.respond({
immediate: true,
status: 200,
body: { received: true },
});
const lead = steps.trigger.event.body ?? {};
const email = String(lead.email ?? "").trim().toLowerCase();
if (!email.includes("@")) {
return $.flow.exit("No usable email in the payload");
}
const verdict = await verify(email, $);
$.export("verdict", verdict);
$.export("lead", { ...lead, email });
if (verdict.action !== "send") {
return $.flow.exit(`Not sending: ${verdict.status} (${verdict.reason})`);
}
return verdict;
},
});
async function verify(email, $) {
for (let attempt = 0; attempt < 3; attempt++) {
const response = await axios.get(ENDPOINT, {
params: { email },
headers: { Authorization: `Bearer ${process.env.SIMPLEVERIFIER_KEY}` },
timeout: 30000,
validateStatus: () => true,
});
if (response.status === 200) return response.data;
if (response.status === 401) throw new Error("401: API key missing, wrong or revoked");
if (response.status === 402) throw new Error("402: plan has no API access, or the yearly quota is used");
if (response.status === 429) {
const wait = Number(response.headers["retry-after"]) || 30;
await new Promise((resolve) => setTimeout(resolve, wait * 1000));
continue;
}
if (response.status === 503) {
await new Promise((resolve) => setTimeout(resolve, 2000 * (attempt + 1)));
continue;
}
throw new Error(`Verification API answered ${response.status}`);
}
throw new Error("Verification API still unavailable after 3 tries");
}
Walking through it:
- The response goes first.
$.respondwithimmediate: trueanswers the sender, then the rest of the workflow keeps running. Without it Pipedream answers when the workflow ends, and if it is never called at all the endpoint returns a400with "No $.respond called in workflow", according to Pipedream's trigger docs. Keep the call at the very top. - A 30 second timeout. A live mailbox check waits on the recipient's mail server and can take close to 30 seconds. A shorter timeout turns slow answers into errors.
- Retries are explicit. A
429waits the number of seconds inRetry-After(the same number isretry_after_secondsin the JSON body). A503backs off and tries again. Both are the documented retryable cases. - A
402throws. Retrying will not help. Pipedream marks the run as failed and, by default, emails you about an unhandled error, at most one email per workflow per 24 hours. Note that errors during testing do not send emails; only live runs do. 401and other 4xx throw too, because the same request would fail again.
Pipedream also offers an auto-retry setting for failed steps. You can leave it off here because the step already retries what is retryable, and you do not want a 402 repeated.
Step 4: What happens to each status
The step exits early unless action is send, and action is only ever send for valid. Here is the full picture so you can route the other cases instead of dropping them.
| status | action | Suggested route |
|---|---|---|
| valid | send | Continue to your CRM step |
| risky | send_with_care | Add to a review list or tag unconfirmed, hold from sequences |
| risky | retry_later | The server did not answer. Queue it for another try later |
| invalid | do_not_send | Tag and suppress, never send |
To route rather than exit, replace the $.flow.exit with a branch (Pipedream's If / Else operator or your own code) that writes each class to a different place: a Google Sheet tab, an Airtable table, a CRM property. The decision fields section of the docs lists every action and reason_code, and unknown codes should be treated like other, going by action.
Step 5: The CRM step
After the code step, add the step that creates or updates the contact. Reference the verified lead as steps.nodejs.lead (rename the step first if you called it something else) and the verdict as steps.nodejs.verdict. Because the code step exited early for anything unsafe, this step only runs for valid addresses.
Store the result on the contact too, for example as a custom email_status property, so later segments can filter on it.
Testing it
- Send a known good address and check that the verdict shows
validand the CRM step runs. - Send an address that cannot work, such as
jane@gmial.com. The run should exit early with the status and reason in the log, and the CRM step should not run. - Temporarily change the environment variable to a wrong key and send again. The run should fail with the 401 message.
- Send 130 requests in a minute, from a loop in a script, to see the 429 path. Pipedream's logs show the wait.
Test with your own addresses first. Every call is a billable API call on your plan.
Errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Sender gets 400 "No $.respond called" | The respond call was skipped or an error came first | Keep $.respond first in the step |
process.env.SIMPLEVERIFIER_KEY is undefined |
Variable name mismatch, or used outside run |
Check case, move code inside run |
| 402 on every run | Starter plan, inactive subscription, or yearly quota used | See errors |
| Regular 429s | Bulk import or another tool on the same key | Spread requests out, or use a CSV upload for the import |
| Slow runs | Slow recipient mail servers | Expected, the sender is not waiting |
What it costs
Each lead is one call. Pro includes 10,000 calls a month, then $1.50 per 1,000 on monthly billing. On yearly billing the included calls are a fixed monthly quota, and further calls return 402 until the 1st (UTC). Business and Agency include more. Pipedream has its own plan limits, which are on their site.
The short version
HTTP trigger, a secret SIMPLEVERIFIER_KEY, a Node.js step that responds first, then verifies with a 30 second timeout, and exits early unless the action is send. Wait out 429s, stop on 402, and keep risky addresses in a review list instead of deleting them. The quickstart has a curl test for the key.
Common questions
Can Pipedream verify email addresses?
Pipedream runs the workflow, it does not check mailboxes. Add a Node.js code step that calls an email verification API, then branch on the status the API returns.
How do I keep an API key safe in Pipedream?
Create an environment variable in Settings with the Secret option on, which is the default, and read it in code with process.env. Do not paste the key into the step, and avoid console.log on it, because logged values appear in the step's logs.
What happens if my Pipedream workflow never calls $.respond?
Pipedream's documentation says the endpoint answers 400 Bad Request with the message 'No $.respond called in workflow' if $.respond is never called, for example when an error happens before it. Call it first with immediate: true so the sender always gets an answer.
What should the workflow do with a risky result?
Do not treat it as valid and do not delete the lead. Risky means delivery was not confirmed, for example a catch-all domain or a server that did not answer. Send it to a review list, or hold it back from automated sequences.
How many calls can I make a minute?
One API key can make 120 calls in a sliding minute. A 429 reply includes a Retry-After header with the seconds to wait. A workflow that fires once per lead rarely reaches that, but a burst from a bulk import can.
Check your list before you send
Real mailbox checks over SMTP, and nothing is sent to the address. Sign in with Google and your first 100 email checks are free, no card. Plans for bigger lists are on the pricing page.