Add an Email Verification Gate to Your Signup Form
A hands-on guide to wiring real-time email verification into a signup form so junk and undeliverable addresses are caught before they reach your list.

Copy this line to your agent to add an email verification gate to your signup form.
set up https://monid.ai/SKILL.md and use strale to validate the email on my signup form before I create the account
Put a real-time email check between the submit button and the account, and a junk address never becomes a row. One call classifies the address on the way in: valid syntax, live MX, not a throwaway, actually deliverable. You accept the good ones, offer a correction on an obvious typo, and block the dead ones at the door instead of scrubbing them out of your list three months later. This walkthrough wires that gate into a signup handler, step by step, against an endpoint you can run today. Monid is a pay-per-call data API marketplace: one key and one wallet reach hundreds of external data endpoints, so verification is a single call you meter, not a vendor you onboard.
TL;DR
- The gate is one call in the handler. Send the submitted address to Strale
/x402/email-validateand read the verdict before you write the user row. - One response covers the whole decision: RFC 5322 syntax, MX presence, disposable-domain detection, role-based pattern flags, free-provider flags, a typo suggestion, and a deliverability assessment.
- Branch three ways: accept a deliverable address, re-prompt with the suggested fix on a typo, block or soft-warn on a dead or disposable one.
- Read the schema for free with
monid inspectbefore you spend, and decide fail-open vs fail-closed for the case where the check itself times out. - Priced per call at a few cents, drawn from the same wallet as the rest of your data stack. Live numbers are at monid.ai/tools.
What you are building
A branch in your registration handler: submitted email -> verdict -> accept, correct, or block. Client-side type="email" and the browser's built-in form validation catch the shape of an address, not whether it can receive mail. sam@mailinator.com is perfectly well-formed and completely useless to you. The gate runs server-side, after submit and before the insert, so the shape check stays in the browser and the deliverability check sits where a user cannot skip it.
For agents
Grab an API key at app.monid.ai, then paste this to your agent and hand it the key:
set up https://monid.ai/SKILL.md
It learns the whole discover, inspect, run workflow itself. More details in the agent quickstart.
For humans
npm install -g @monid-ai/cli
monid keys add --label main --key <your-api-key>
More details in the CLI quickstart.
Step 1. Read the schema before you spend
inspect is free and prints the exact input shape and the price, so you never burn a paid run on a field the endpoint does not accept.
monid inspect -p api.strale.io -e /x402/email-validate
# input: query param "email" (string, required)
# pricing: PER_CALL, cents-scale, shown before you run
# returns: RFC 5322 syntax, MX, disposable, role, free-provider,
# typo suggestion, deliverability assessment
One input, one query parameter named email. That is the whole contract, which is why this drops into a handler in a few lines.
Step 2. Make the one call that decides
This is the only step that costs money. Feed the submitted address into the email query param. The -w flag waits inline and hands the verdict straight back, which is exactly what a request handler needs.
monid run -p api.strale.io -e /x402/email-validate \
--query '{"email": "hello@gmial.com"}' -w
# -> deliverability verdict + syntax, MX, disposable, role, free-provider,
# and a typo suggestion (gmial.com -> gmail.com)
# billed per call, price shown before it ran
Note the --query flag, not -i. This endpoint takes its input as a query parameter, so the address rides in --query, not in a request body. The response is one JSON object with the full assessment, so a single call settles whether the row stays or goes.

Step 3. Branch on the verdict
Read three things out of the response and turn them into a decision. Deliverability gates the accept, the typo suggestion drives a re-prompt, and the disposable and MX flags drive the block. Here it is as a small shell branch you can lift straight into a handler.
result=$(monid run -p api.strale.io -e /x402/email-validate \
--query "{\"email\": \"$email\"}" -w -o -)
deliverable=$(jq -r '.deliverable // false' <<<"$result")
suggestion=$(jq -r '.typo_suggestion // empty' <<<"$result")
disposable=$(jq -r '.disposable // false' <<<"$result")
if [ -n "$suggestion" ]; then
echo "reprompt: did you mean $suggestion ?" # offer the fix, do not save yet
elif [ "$disposable" = "true" ]; then
echo "block: throwaway address" # hard stop
elif [ "$deliverable" = "true" ]; then
echo "accept: create the account" # clean row, proceed
else
echo "soft-warn: undeliverable, ask to re-check" # no MX, dead mailbox
fi
The typo branch is the one that earns its keep on a live form. A user who fumbled gmial.com is a real person you would otherwise lose to a silent bounce. Handing back the suggested correction turns a dead signup into a good one before the account exists. Match the exact field names to what your inspect output shows, since the branch is only as good as the fields it reads.
Step 4. Drop the gate into the handler
Now put it where submit lands. The pattern is the same in any stack: run the check, branch, and only reach the insert on the accept path. Here it is in a Node handler that shells out to the CLI, so the account is never created until the address clears.
const { execFile } = require("node:child_process");
app.post("/signup", (req, res) => {
const email = req.body.email;
execFile("monid", [
"run", "-p", "api.strale.io", "-e", "/x402/email-validate",
"--query", JSON.stringify({ email }), "-w", "-o", "-",
], (err, stdout) => {
if (err) return res.status(503).json({ error: "verification unavailable" });
const v = JSON.parse(stdout);
if (v.typo_suggestion) return res.status(422).json({ suggest: v.typo_suggestion });
if (v.disposable || !v.deliverable) return res.status(422).json({ error: "undeliverable" });
createAccount(email); // only clean addresses get here
res.status(201).json({ ok: true });
});
});
The 422 on a typo carries the suggestion back to the form so the browser can render "did you mean gmail.com?" and let the user accept it with one tap. The insert sits behind the gate, so a disposable or undeliverable address never becomes a user.

Step 5. Decide what happens when the check times out
A network call can hang, and your signup form cannot. Pick a policy on purpose. Fail-open lets the signup through when the check is unreachable, which protects conversion at the cost of an occasional bad row. Fail-closed blocks until the check answers, which protects the list at the cost of turning away a real user during an outage. Most consumer signups fail-open with a short timeout and re-verify async; a cold-outreach list that lives or dies on sender reputation fails-closed. The 503 path in Step 4 is where that choice lives: return it to block, or wave the signup through and queue a re-check. Either way, set a timeout so a slow check never becomes a stuck button.
What this costs to run
You see the exact price before every run, and it is always on monid.ai/tools. In magnitude terms:
Inspect the schema free
email-validate a few cents PER_CALL (once per real submit)
Branch + handler your own code, free
--------------------------------------------------------------
Typical signup: one call, a few cents
Ten thousand signups: still tens of dollars, not a subscription
The gate only fires on a real submit, so a burst of bot signups that never make it past your other defenses never bills you for verification you did not want. There is no pack to size in advance and nothing that expires, because the call draws from the same pay-as-you-go wallet as the rest of your data stack.
Hand it to an agent
The same gate runs without you in the loop. Because Monid ships as an MCP server, an agent that owns your signup flow can inspect the endpoint, run the check on each submitted address, apply the accept, correct, and block rules above, and only then create the account. Free discovery and a per-call price shown before each run are what make that safe to delegate: the agent can price the step before it spends.
FAQ
Do I need a Strale account? No. Integrate Monid once and fund one pay-as-you-go wallet. Strale is reached through the same key, billed at the price shown before each run. Live numbers are at monid.ai/tools.
Client-side or server-side?
Both, for different jobs. Keep the browser's type="email" and built-in validation for the shape of an address, and run the deliverability gate server-side after submit, where a user cannot bypass it and your key stays off the page.
Will this slow down my form? It adds one network call to the submit path, so set a timeout and pick a fail-open or fail-closed policy as in Step 5. Consumer signups usually fail-open with a short timeout; reputation-sensitive lists fail-closed.
What about the typo path specifically? When the response carries a typo suggestion, do not save the row. Return the suggestion to the form and let the user confirm the fix. That recovers a real signup that would otherwise bounce silently.
How is this priced?
Per call, a few cents, with the number shown before you run by a free monid inspect. No minimum, no pack, nothing that expires, drawn from the shared wallet. Current prices are at monid.ai/tools.
Ship it
Inspect /x402/email-validate for free to see its schema and price, run it against ten addresses whose fate you already know, and confirm the verdicts match. Then wire Step 3 and Step 4 into your signup handler, pick your timeout policy, and let the gate keep the junk off your list. Start at monid.ai.


