This workflow corresponds to n8n.io template #6253 — we link there as the canonical source.
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 →
{
"nodes": [
{
"id": "1529173e-b4ae-4352-a378-a327c39e7fed",
"name": "success",
"type": "n8n-nodes-base.if",
"position": [
448,
-48
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "faa449f2-815c-41fa-8378-e93093ad10d7",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.success }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "140b3700-3721-4e41-b87c-8df70e995bc4",
"name": "validation failed",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
672,
48
],
"parameters": {
"options": {
"responseCode": 401
},
"respondWith": "json",
"responseBody": "={\n \"success\": false,\n \"error\": \"{{ $('validator').first().json.reason }}\"\n}"
},
"typeVersion": 1.4
},
{
"id": "4715c17e-56d8-49f5-bcce-7f5b6f822f49",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
592,
-304
],
"parameters": {
"color": 6,
"width": 260,
"height": 308,
"content": "## Database Example\nClone this [Airtable Base](https://airtable.com/appbw5TEhn8xIxxXR/shrN8ve4dfJIXjcAm)"
},
"typeVersion": 1
},
{
"id": "722d92c2-445c-4c0a-9e06-a58dbc075907",
"name": "secret validation",
"type": "n8n-nodes-base.if",
"position": [
1120,
-240
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "2e4151fc-f513-4a1d-8e42-c77857de22bc",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.secret }}",
"rightValue": "={{ $('client receiver').item.json.body.client_secret }}"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "a02e3055-eb51-4fec-a429-d8349e388fe4",
"name": "invalid client",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1120,
-48
],
"parameters": {
"options": {
"responseCode": 400
},
"respondWith": "json",
"responseBody": "{\n\"success\": false,\n\"error\": \"Invalid client id\"\n}"
},
"typeVersion": 1.1
},
{
"id": "729d6799-6f74-457c-aac5-ca651e10f170",
"name": "invalid secret",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1344,
-144
],
"parameters": {
"options": {
"responseCode": 401
},
"respondWith": "json",
"responseBody": "{\n\"success\": false,\n\"error\": \"Invalid client secret\"\n}"
},
"typeVersion": 1.1
},
{
"id": "b94ab331-23c2-473d-afdc-e591b89b38e0",
"name": "generate token",
"type": "n8n-nodes-base.code",
"position": [
1344,
-336
],
"parameters": {
"jsCode": "function generateLongToken(length) {\n const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n let token = '';\n for (let i = 0; i < length; i++) {\n token += chars.charAt(Math.floor(Math.random() * chars.length));\n }\n return token;\n}\n\nconst token = generateLongToken(32);\n\nreturn [\n {\n json: {\n token\n }\n }\n];\n"
},
"typeVersion": 2
},
{
"id": "204e04af-34a7-4824-b107-4cc3e89199e3",
"name": "client exists",
"type": "n8n-nodes-base.if",
"position": [
896,
-144
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "6f16470a-aae4-4647-850e-332284b00a9f",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.id }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "e366461a-ece5-47d1-b5de-ea29aa1846ca",
"name": "validator",
"type": "n8n-nodes-base.code",
"position": [
224,
-48
],
"parameters": {
"jsCode": "const input = $json;\n\nconst body = input.body || {};\n\n// Allowed keys\nconst allowedKeys = ['client_id', 'client_secret'];\n\n// Check required keys\nfor (const key of allowedKeys) {\n if (!body.hasOwnProperty(key)) {\n return [{ json: { success: false, reason: `Missing '${key}' in body` } }];\n }\n}\n\n// Check for extra keys\nconst extraKeys = Object.keys(body).filter(k => !allowedKeys.includes(k));\nif (extraKeys.length > 0) {\n return [{ json: { success: false, reason: `Body must contain only 'client_id' and 'client_secret', found extra key(s): ${extraKeys.join(', ')}` } }];\n}\n\n// All good\nreturn [{ json: { success: true } }];"
},
"typeVersion": 2
},
{
"id": "b7ea9e5f-d097-4ff7-b165-ee1560bb054d",
"name": "client receiver",
"type": "n8n-nodes-base.webhook",
"position": [
0,
-48
],
"parameters": {
"path": "token-refresher",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "70e09e1b-336a-4756-af23-aa4a0636e854",
"name": "get client id",
"type": "n8n-nodes-base.airtable",
"position": [
672,
-144
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "appbw5TEhn8xIxxXR",
"cachedResultUrl": "https://airtable.com/appbw5TEhn8xIxxXR",
"cachedResultName": "Testing Bearer YOUR_TOKEN_HERE "
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblK2jv4hLOsaKM3m",
"cachedResultUrl": "https://airtable.com/appbw5TEhn8xIxxXR/tblK2jv4hLOsaKM3m",
"cachedResultName": "Client IDs"
},
"options": {},
"operation": "search",
"filterByFormula": "={client} = \"{{ $('client receiver').item.json.body.client_id }}\""
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1,
"alwaysOutputData": true
},
{
"id": "d5c9d779-5805-45a0-affd-a683a17391ac",
"name": "create token",
"type": "n8n-nodes-base.airtable",
"position": [
1568,
-336
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "appbw5TEhn8xIxxXR",
"cachedResultUrl": "https://airtable.com/appbw5TEhn8xIxxXR",
"cachedResultName": "Testing Bearer YOUR_TOKEN_HERE "
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblnqvjl4U2t9OMQD",
"cachedResultUrl": "https://airtable.com/appbw5TEhn8xIxxXR/tblnqvjl4U2t9OMQD",
"cachedResultName": "Tokens"
},
"columns": {
"value": {
"Token ID": "={{ $json.token }}",
"Client IDs": "={{ [$('client receiver').item.json.body.client_id] }}",
"Token Type": "Bearer",
"Creation Date": "={{ $now }}"
},
"schema": [
{
"id": "Token ID",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Token ID",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Token Type",
"type": "options",
"display": true,
"options": [
{
"name": "Bearer",
"value": "Bearer"
}
],
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Token Type",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Expiration Date",
"type": "dateTime",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Expiration Date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Issued To",
"type": "array",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Issued To",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Creation Date",
"type": "dateTime",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Creation Date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Is Active",
"type": "boolean",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Is Active",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Days Until Expiration",
"type": "string",
"display": true,
"removed": true,
"readOnly": true,
"required": false,
"displayName": "Days Until Expiration",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Status",
"type": "string",
"display": true,
"removed": true,
"readOnly": true,
"required": false,
"displayName": "Status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Client IDs",
"type": "array",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Client IDs",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {
"typecast": true
},
"operation": "create"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1,
"alwaysOutputData": true
},
{
"id": "09da5fce-3635-4125-903b-a55ca93a5b4b",
"name": "respond ",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1792,
-336
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={\n \"access_token\": \"{{ $('generate token').item.json.token }}\",\n \"expires_in\": 3600,\n \"token_type\": \"{{ $json.fields['Token Type'] }}\"\n}"
},
"typeVersion": 1.2
},
{
"id": "325443c4-31b4-472f-ab2c-e606776466ba",
"name": "Other methods",
"type": "n8n-nodes-base.webhook",
"position": [
0,
272
],
"parameters": {
"path": "test-jobs",
"options": {},
"httpMethod": [
"DELETE",
"HEAD",
"PATCH",
"PUT",
"GET"
],
"responseMode": "responseNode",
"multipleMethods": true
},
"typeVersion": 2
},
{
"id": "8b407e94-1e92-4882-872f-f6b2e6e60673",
"name": "405 Error",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
208,
320
],
"parameters": {
"options": {
"responseCode": 405
},
"respondWith": "json",
"responseBody": "{\n \"error\": \"Use POST request instead\"\n}"
},
"typeVersion": 1.1
},
{
"id": "7bc3accc-791d-4f8a-8f14-80f2d3d4ddf9",
"name": "When clicking \u2018Execute workflow\u2019",
"type": "n8n-nodes-base.manualTrigger",
"position": [
16,
-448
],
"parameters": {},
"typeVersion": 1
},
{
"id": "d94ffe0e-95f7-459c-a837-a08b9ac3d87f",
"name": "Make a request",
"type": "n8n-nodes-base.httpRequest",
"position": [
240,
-448
],
"parameters": {
"url": "https://localhost:8080/webhook/token-refresher",
"method": "POST",
"options": {},
"jsonBody": "{\n \"client_id\": \"client_a_1234567890abcdef\",\n \"client_secret\": \"secret_a_abcdef1234567890\"\n\n}",
"sendBody": true,
"specifyBody": "json"
},
"typeVersion": 4.2
},
{
"id": "b9ea6991-ba49-49f0-bffb-5cc807eedb94",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-48,
-512
],
"parameters": {
"color": 7,
"width": 460,
"height": 220,
"content": "## Test the request"
},
"typeVersion": 1
},
{
"id": "e8814551-20d1-4114-b691-0a45cbf61086",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-48,
208
],
"parameters": {
"color": 7,
"width": 460,
"height": 280,
"content": "## HTTP Method handler"
},
"typeVersion": 1
},
{
"id": "db9c1b3f-00ba-40fe-8e9c-f900be065940",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-688,
-512
],
"parameters": {
"color": 4,
"width": 580,
"height": 1656,
"content": "## OAuth Token Generator and Validator\n\nThis **n8n template** helps you generate, validate, and store tokens for your customers securely using:\n\n- **n8n** as your backend automation engine \n- **Airtable** as your lightweight client and token store\n\n---\n\n### \ud83d\ude80 What It Does\n\n- Accepts `client_id` and `client_secret` via **POST webhook**.\n- Validates client credentials against Airtable.\n- Generates a **long token** on success.\n- Stores the generated token in Airtable with metadata.\n- Responds with a JSON containing the token, expiry, and type.\n- Returns clear error messages if validation fails.\n\n---\n\n### How It Works\n\n1. **Webhook node** receives `client_id` and `client_secret`.\n2. **Validator (Code node)** checks:\n - Body contains only `client_id` and `client_secret`.\n - Rejects missing or extra fields.\n3. **Airtable search**:\n - Looks up the `client_id`.\n - Rejects if not found.\n4. **Secret validation (If node)**:\n - Compares provided `client_secret` with stored value.\n - Rejects if incorrect.\n5. **Token generation (Code node)**:\n - Generates a 128-character secure token.\n6. **Airtable create**:\n - Stores token, client ID, creation date, and type.\n7. **Webhook response**:\n - Returns JSON `{ access_token, expires_in, token_type }` on success.\n - Returns appropriate JSON error messages on failure.\n\n---\n\n### Related Workflow\n\nYou can also use it with the published **Bearer YOUR_TOKEN_HERE Validation** workflow:\n\n\ud83d\udc49 [Validate API Requests with Bearer YOUR_TOKEN_HERE Authentication and Airtable](https://n8n.io/workflows/6184-validate-api-requests-with-bearer-token-authentication-and-airtable)\n\nto securely validate tokens you generate with this workflow across your protected endpoints.\n\n---\n\n### Why Use This\n\n- Provides **OAuth-like flows** without a complex backend.\n- Uses **n8n + Airtable** for client management and token storage.\n- Clean, modular, and ready for your SaaS or internal API automations.\n- Extendable for token expiry, refresh, and rotation handling.\n\n---\n\nEnjoy building secure token-based APIs using **n8n + Airtable**! \ud83d\ude80\n\n### Built by:\n[Nazmy](https://n8n.io/creators/islamnazmi/)\n"
},
"typeVersion": 1
}
],
"connections": {
"success": {
"main": [
[
{
"node": "get client id",
"type": "main",
"index": 0
}
],
[
{
"node": "validation failed",
"type": "main",
"index": 0
}
]
]
},
"validator": {
"main": [
[
{
"node": "success",
"type": "main",
"index": 0
}
]
]
},
"create token": {
"main": [
[
{
"node": "respond ",
"type": "main",
"index": 0
}
]
]
},
"Other methods": {
"main": [
[
{
"node": "405 Error",
"type": "main",
"index": 0
}
],
[
{
"node": "405 Error",
"type": "main",
"index": 0
}
],
[
{
"node": "405 Error",
"type": "main",
"index": 0
}
],
[
{
"node": "405 Error",
"type": "main",
"index": 0
}
],
[
{
"node": "405 Error",
"type": "main",
"index": 0
}
]
]
},
"client exists": {
"main": [
[
{
"node": "secret validation",
"type": "main",
"index": 0
}
],
[
{
"node": "invalid client",
"type": "main",
"index": 0
}
]
]
},
"get client id": {
"main": [
[
{
"node": "client exists",
"type": "main",
"index": 0
}
]
]
},
"generate token": {
"main": [
[
{
"node": "create token",
"type": "main",
"index": 0
}
]
]
},
"client receiver": {
"main": [
[
{
"node": "validator",
"type": "main",
"index": 0
}
]
]
},
"secret validation": {
"main": [
[
{
"node": "generate token",
"type": "main",
"index": 0
}
],
[
{
"node": "invalid secret",
"type": "main",
"index": 0
}
]
]
},
"When clicking \u2018Execute workflow\u2019": {
"main": [
[
{
"node": "Make a request",
"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.
airtableTokenApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow contains community nodes that are only compatible with the self-hosted version of n8n.
Source: https://n8n.io/workflows/6253/ — 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 powerful n8n automation workflow is designed to execute advanced B2B lead enrichment and hyper-personalization for cold email outreach. By orchestrating a complex chain of data scraping, AI analy
This template is perfect for e-commerce entrepreneurs, marketers, agencies, and creative teams who want to turn simple product photos and short descriptions into professional flyers or product videos—
This workflow automates the process of generating stylized product photos for e-commerce by combining real product shots with creative templates. It enables the creation of a complete set of images fo
This n8n template demonstrates how to capture inbound leads from a form, qualify them with OpenAI, and route the hottest ones to a Bland AI voice agent that calls them back, books a meeting on Google
AI-Powered Fake Review Detection Workflow Using n8n & Airtable. Uses httpRequest, airtable, openAi, slack. Webhook trigger; 27 nodes.