Airtable bases collect addresses from forms, imports and interfaces, and a verdict column next to the email column is the simplest way to keep them honest. There are three ways to fill it: a Zap or Make scenario that reacts to new records, an Airtable automation that runs a short script, and a bulk run for the rows already there. All three write the same fields. A phone column works the same way with its own fields, as the phone section shows.
Prepare the table
- Add three fields: a single select or text field named Verdict, a text field named Reason, and a number field named Risk score. A fourth text field for the suggested correction is worth having.
- Create a view that shows records whose Verdict is empty; the flows below use it as their trigger and their to-do list.
- Create a secret API key on the API keys page of your Spaw dashboard.
Verify new records through Zapier or Make
- Start the flow with the Airtable trigger for a new record in the view from step 2.
- Add the verification step: a Webhooks by Zapier POST or a Make HTTP request to
https://spaw.co/api/v1/emailwith the headerAuthorization: Bearer sk_live_…and this body, mapping the record's email field:
{
"email": "[email protected]"
}
The Zapier page and the Make page show the step in full. When the Spaw apps for those platforms are listed, it becomes a Verify Email action with a saved connection.
- Add the Airtable action that updates a record, match on the record id from the trigger, and map
data.deliverable,data.reason,data.risk_scoreanddata.did_you_meaninto the four fields. Filling Verdict removes the record from the view, so it is not processed twice.
Verify inside an Airtable automation
Airtable automations can run a script with network access, which removes the need for a third tool. Create an automation triggered when a record enters the empty-Verdict view, add a script action, pass the record id and email as input variables, and use this script:
const { recordId, email } = input.config();
const table = base.getTable('Leads');
const response = await fetch('https://spaw.co/api/v1/email', {
method: 'POST',
headers: { 'Authorization': 'Bearer sk_live_…', 'Content-Type': 'application/json' },
body: JSON.stringify({ email }),
});
const result = await response.json();
if (result.success === false) {
throw new Error(`Spaw: ${result.error.code} ${result.error.message}`);
}
await table.updateRecordAsync(recordId, {
'Verdict': result.data.deliverable,
'Reason': result.data.reason ?? '',
'Risk score': result.data.risk_score,
'Suggestion': result.data.did_you_mean ?? '',
});
The key sits in the script, so anyone who can edit the automation can read it; give the base its own key and revoke it from the API keys page if the base is shared widely. Test with [email protected], a free test address that answers a fixed result and is never logged.
Clean a whole table
Export the table to CSV, run the email column through a bulk verification on the Spaw dashboard or with the bulk endpoint, and import the result back with the verdict columns mapped to the four fields, matching on the primary field. The result keeps your original columns. For a spreadsheet rather than a base, the Google Sheets page offers a custom function instead.
What to do with the verdict
Filter views on Verdict. deliverable records are ready for outreach. risky records need the Reason: catch_all means an unconfirmed mailbox on a domain that accepts everything, role means a shared inbox, disposable means a throwaway address. undeliverable records should be kept out of sends, and a filled Suggestion field is usually a person worth one correction.
Phone numbers
A phone column gets its own fields and its own view, and either path above can fill them.
- Add four fields: Phone E.164 and Line type as text, OK to send as a checkbox, and Blocked by as text.
- Create a view that shows records whose phone field is filled and whose Phone E.164 and Blocked by are both empty. Every answer fills one of the two, so a record leaves the view once it has been checked.
- Through Zapier or Make, start a second flow on a new record in that view, add a Webhooks by Zapier POST or a Make HTTP request to
https://spaw.co/api/v1/phonewith the same header and a body of{ "phone": "(202) 555-0100", "country": "US" }, mapping the phone field, and mapdata.e164,data.line_type,data.ok_to_sendanddata.blocked_byinto the four fields. An invalid number in a block nobody holds still comes back with its parsed E.164 form, so read Phone E.164 together with OK to send, which is false for every invalid number. Once the Spaw apps are listed, the step becomes the Validate Phone Number action in Zapier and Validate a Phone Number in Make.
Inside Airtable, create a second automation triggered when a record enters the view, pass the record id, the phone number and, if the table has one, a two-letter country field as input variables, and use this script:
const { recordId, phone, country } = input.config();
const table = base.getTable('Leads');
const response = await fetch('https://spaw.co/api/v1/phone', {
method: 'POST',
headers: { 'Authorization': 'Bearer sk_live_…', 'Content-Type': 'application/json' },
body: JSON.stringify({ phone, country: country || null }),
});
const result = await response.json();
if (result.success === false) {
throw new Error(`Spaw: ${result.error.code} ${result.error.message}`);
}
await table.updateRecordAsync(recordId, {
'Phone E.164': result.data.valid ? result.data.e164 : '',
'Line type': result.data.line_type ?? '',
'OK to send': result.data.ok_to_send,
'Blocked by': result.data.blocked_by ?? '',
});
country must be an ISO 3166-1 alpha-2 code such as US or GB, and it matters only for numbers written without their + and calling code; a national number sent without one answers valid: false with the reason missing_country, free. A country name, or UK, is refused with a 422, which the script turns into an error on the automation run. The script keeps Phone E.164 empty for an invalid number, because an invalid number in a block nobody holds still carries its parsed form. Test with +1 202 555 0100 and +1 202 555 0103, two documented test numbers that cost nothing: the first answers a valid mobile with ok_to_send true, the second ok_to_send false with blocked_by disposable, under the default SMS policy.
Filter the views that feed a text message on OK to send. It is your account's SMS policy applied to the answer, set once on the account so every automation, Zap, batch and bulk run applies the same rules, and Blocked by names the rule that decided a false: invalid, opted_out (by default) for a number your suppression list holds as opted out, country, line_type, a signal such as disposable for a number published by an SMS-receiving website, where anyone can read a code sent to it, or risk_score. It is a fraud and deliverability decision, not consent and not a do-not-call scrub. line_type comes from the numbering plan or, for a North American number, from the holder of its block in the regulator's table; it describes how the range was allocated, never the network that serves the number after a port, and an ordinary US number answers fixed_line_or_mobile unless its block holder settles the type (why). Every field is on the phone endpoint page.
Cost
Each record that answers deliverable or risky spends 1 credit; unverified answers and repeats within seven days are free, and undeliverable answers are free within a fair-use allowance. A phone lookup costs 1 credit when the number is valid; invalid numbers, unassigned blocks and repeats of the same number within seven days are free. Zapier and Make bill their own tasks; Airtable automation runs count against your Airtable plan. Bulk runs bill per processed row and verify duplicates once.