This workflow follows the Airtable → HTTP Request recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "Shave Of Voice, Checked! \u2014 Free Visibility Check",
"active": false,
"settings": {
"executionOrder": "v1"
},
"nodes": [
{
"id": "webhook",
"name": "Free Check Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
240,
300
],
"parameters": {
"httpMethod": "POST",
"path": "free-check",
"responseMode": "responseNode",
"options": {
"allowedOrigins": "*"
}
}
},
{
"id": "validate",
"name": "Validate + Anti-Abuse",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
460,
300
],
"parameters": {
"jsCode": "const body = $input.item.json.body || $input.item.json;\nconst brand = (body.brand || '').trim();\nconst email = (body.email || '').trim().toLowerCase();\nconst ip = $input.item.json.headers?.['x-forwarded-for']?.split(',')[0]?.trim() || 'unknown';\n\n// Basic validation\nif (!brand || brand.length < 2) throw new Error('Brand name too short');\nif (!email || !email.includes('@') || !email.includes('.')) throw new Error('Invalid email');\n\n// Disposable email domain blocklist (extend as needed)\nconst blocked = ['mailinator.com','guerrillamail.com','tempmail.com','throwaway.email','yopmail.com','sharklasers.com','trashmail.com','maildrop.cc'];\nconst domain = email.split('@')[1];\nif (blocked.includes(domain)) throw new Error('Disposable email not allowed');\n\nconst crypto = require('crypto');\nconst ipHash = crypto.createHash('sha256').update(ip).digest('hex').slice(0, 16);\n\nreturn [{ json: { brand, email, ip_hash: ipHash, domain } }];"
}
},
{
"id": "check-abuse",
"name": "Check Abuse in Airtable",
"type": "n8n-nodes-base.airtable",
"typeVersion": 2,
"position": [
680,
300
],
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"parameters": {
"operation": "list",
"base": {
"__rl": true,
"value": "={{ $env.AIRTABLE_BASE_ID }}",
"mode": "id"
},
"table": {
"__rl": true,
"value": "Clients",
"mode": "name"
},
"filterByFormula": "OR(AND({email}='{{ $json.email }}',LOWER({name})=LOWER('{{ $json.brand }}')),{free_check_blocked}=1)",
"options": {
"fields": [
"id",
"free_check_count",
"free_check_blocked",
"free_check_hashes"
]
}
}
},
{
"id": "abuse-gate",
"name": "Abuse Gate",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
900,
300
],
"parameters": {
"jsCode": "const existing = $('Check Abuse in Airtable').all();\nconst { brand, email, ip_hash } = $('Validate + Anti-Abuse').item.json;\n\nfor (const rec of existing) {\n const f = rec.json.fields || rec.json;\n if (f.free_check_blocked) throw new Error('ABUSE_BLOCKED');\n // Same brand+email combo already used\n if (f.email === email) throw new Error('ALREADY_USED');\n // IP rate limit: check if ip_hash appears in recent hashes\n try {\n const hashes = JSON.parse(f.free_check_hashes || '[]');\n if (hashes.filter(h => h === ip_hash).length >= 3) throw new Error('RATE_LIMITED');\n } catch(e) { if (e.message === 'RATE_LIMITED') throw e; }\n}\n\nreturn [{ json: { brand, email, ip_hash, existing_id: existing[0]?.json?.id || null } }];"
}
},
{
"id": "run-prompts",
"name": "Run 5 Default Prompts",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1120,
300
],
"parameters": {
"jsCode": "const { brand, email, ip_hash } = $json;\n\n// 5 generic buyer-intent prompt templates\nconst templates = [\n `What are the best tools for companies like ${brand}?`,\n `Who are the top vendors in the same category as ${brand}?`,\n `What do people recommend instead of or alongside ${brand}?`,\n `Is ${brand} a good choice for B2B companies?`,\n `What are the main alternatives to ${brand}?`\n];\n\nconst results = [];\nfor (const prompt of templates) {\n const res = await helpers.httpRequest({\n method: 'POST',\n url: 'https://openrouter.ai/api/v1/chat/completions',\n headers: { 'Authorization': 'Bearer ' + $env.OPENROUTER_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: 'gpt-4o-mini',\n temperature: 0.3,\n messages: [\n { role: 'system', content: 'You are a helpful assistant. Answer the user question directly and thoroughly.' },\n { role: 'user', content: prompt }\n ]\n })\n });\n const text = res.choices[0].message.content;\n const mentioned = text.toLowerCase().includes(brand.toLowerCase());\n results.push({ prompt, response: text, mentioned });\n}\n\nconst score = results.filter(r => r.mentioned).length;\nreturn [{ json: { brand, email, ip_hash, score, total: 5, results } }];"
}
},
{
"id": "store-result",
"name": "Store Free Check in Airtable",
"type": "n8n-nodes-base.airtable",
"typeVersion": 2,
"position": [
1340,
300
],
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"parameters": {
"operation": "create",
"base": {
"__rl": true,
"value": "={{ $env.AIRTABLE_BASE_ID }}",
"mode": "id"
},
"table": {
"__rl": true,
"value": "Clients",
"mode": "name"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"name": "={{ $json.brand }}",
"email": "={{ $json.email }}",
"plan": "free_check",
"status": "lead",
"free_check_count": 1,
"free_check_hashes": "={{ JSON.stringify([$json.ip_hash]) }}",
"created_at": "={{ new Date().toISOString() }}"
}
},
"options": {}
}
},
{
"id": "send-result-email",
"name": "Send Result Email",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1560,
300
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"parameters": {
"method": "POST",
"url": "https://api.resend.com/emails",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({\n from: 'Shave Of Voice, Checked! <reports@sovcheck.online>',\n to: [$('Run 5 Default Prompts').item.json.email],\n subject: `${$('Run 5 Default Prompts').item.json.brand} appeared in ${$('Run 5 Default Prompts').item.json.score}/5 AI prompts`,\n html: `<!DOCTYPE html><html><body style=\"font-family:Helvetica Neue,Arial,sans-serif;background:#0B0F1A;color:#F0F4FF;margin:0;padding:0\"><table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"><tr><td align=\"center\" style=\"padding:40px 16px\"><table width=\"560\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:#141927;border-radius:16px;border:1px solid #1E2740;overflow:hidden\"><tr><td style=\"padding:28px 32px;border-bottom:1px solid #1E2740;background:#10172A\"><span style=\"font-size:12px;font-weight:700;color:#8B95B0;letter-spacing:0.08em;text-transform:uppercase\">Shave Of Voice, Checked! — Free Visibility Check</span></td></tr><tr><td style=\"padding:32px\"><p style=\"margin:0 0 8px;font-size:13px;font-weight:700;letter-spacing:0.12em;text-transform:uppercase;color:#4FFFB0\">Your AI Visibility Score</p><p style=\"margin:0 0 24px;font-size:42px;font-weight:800;color:#F0F4FF;font-family:monospace\">${$('Run 5 Default Prompts').item.json.score}<span style=\"font-size:20px;color:#8B95B0\">/5 prompts</span></p><p style=\"margin:0 0 20px;font-size:15px;color:#8B95B0;line-height:1.6\">We ran 5 buyer-intent prompts about <strong style=\"color:#F0F4FF\">${$('Run 5 Default Prompts').item.json.brand}</strong> through ChatGPT. Your brand appeared in <strong style=\"color:#4FFFB0\">${$('Run 5 Default Prompts').item.json.score} out of 5</strong>.</p><p style=\"margin:0 0 28px;font-size:15px;color:#8B95B0;line-height:1.6\">Want the full picture? The Pro plan runs <strong style=\"color:#F0F4FF\">50 prompts per week</strong> across ChatGPT, Perplexity, and Gemini — with competitor tracking and week-over-week trends.</p><a href=\"https://tally.so/r/q4Y6xY\" style=\"display:inline-block;background:#4FFFB0;color:#0B0F1A;font-size:14px;font-weight:700;padding:13px 28px;border-radius:10px;text-decoration:none\">Get the Full Report →</a></td></tr><tr><td style=\"padding:18px 32px;border-top:1px solid #1E2740;background:#10172A;font-size:12px;color:#5C6885\">Shave Of Voice, Checked! · AI Brand Visibility Monitoring</td></tr></table></td></tr></table></body></html>`\n}) }}",
"options": {}
}
},
{
"id": "respond-ok",
"name": "Respond with Score",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
1780,
300
],
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ ok: true, score: $('Run 5 Default Prompts').item.json.score, total: 5, brand: $('Run 5 Default Prompts').item.json.brand }) }}",
"options": {
"responseCode": 200,
"responseHeaders": {
"entries": [
{
"name": "Access-Control-Allow-Origin",
"value": "*"
}
]
}
}
}
},
{
"id": "respond-error",
"name": "Respond with Error",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
900,
500
],
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ ok: false, message: $json.message }) }}",
"options": {
"responseCode": 429
}
}
}
],
"connections": {
"Free Check Webhook": {
"main": [
[
{
"node": "Validate + Anti-Abuse",
"type": "main",
"index": 0
}
]
]
},
"Validate + Anti-Abuse": {
"main": [
[
{
"node": "Check Abuse in Airtable",
"type": "main",
"index": 0
}
]
]
},
"Check Abuse in Airtable": {
"main": [
[
{
"node": "Abuse Gate",
"type": "main",
"index": 0
}
]
]
},
"Abuse Gate": {
"main": [
[
{
"node": "Run 5 Default Prompts",
"type": "main",
"index": 0
}
]
]
},
"Run 5 Default Prompts": {
"main": [
[
{
"node": "Store Free Check in Airtable",
"type": "main",
"index": 0
}
]
]
},
"Store Free Check in Airtable": {
"main": [
[
{
"node": "Send Result Email",
"type": "main",
"index": 0
}
]
]
},
"Send Result Email": {
"main": [
[
{
"node": "Respond with Score",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
airtableTokenApihttpHeaderAuth
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Shave Of Voice, Checked! — Free Visibility Check. Uses airtable, httpRequest. Webhook trigger; 9 nodes.
Source: https://github.com/alvee1994/promptscore/blob/86adec3e7e7f92f0602e694d78bef062735f9d17/n8n/workflow_free_check.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This premium n8n workflow harnesses the power of DataForSEO's API combined with Airtable's relational database capabilities to transform your keyword research process, providing deeper insights for co
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
This workflow automates the entire lifecycle of a service-based client, combining four distinct business flows into a single view: Intake Leads: Receives a webhook from your form builder, validates th
It intelligently syncs confirmed sales orders from your Airtable base to QuickBooks, automatically creating new customers if they don't exist before generating a perfectly matched invoice. It then log
Who is this for? Business who manually prep/route DocuSign envelopes and want zero-touch contract signing from form submission.