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": "04b Outreach: Approval Webhook",
"nodes": [
{
"parameters": {
"httpMethod": "GET",
"path": "outreach-decision",
"responseMode": "responseNode",
"options": {}
},
"id": "wh-decision",
"name": "Approve/Reject Link",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
240,
300
]
},
{
"parameters": {
"jsCode": "// Validate the decision and build the state-transition SQL.\nconst token = ($json.query && $json.query.token) || '';\nlet decision = ($json.query && $json.query.decision) || '';\nif (!['approved','rejected'].includes(decision)) decision = 'rejected';\nif (!/^[0-9a-fA-F-]{36}$/.test(token)) {\n return [{ json: { ok: false, message: 'Invalid token', query: 'SELECT 1 WHERE false;' } }];\n}\nconst outreachStatus = decision === 'approved' ? 'approved' : 'cancelled';\nconst query = `WITH a AS (UPDATE app.approvals SET status = '${decision}', decided_at = now() WHERE token = '${token}' AND status = 'pending' RETURNING entity_id) UPDATE app.outreach o SET status = '${outreachStatus}', approved_at = CASE WHEN '${decision}' = 'approved' THEN now() ELSE approved_at END FROM a WHERE o.id = a.entity_id RETURNING o.id;`;\nreturn [{ json: { ok: true, decision, query } }];"
},
"id": "code-decide",
"name": "Build Decision SQL",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
460,
300
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "={{ $json.query }}",
"options": {}
},
"id": "pg-apply",
"name": "Apply Decision",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
680,
300
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"respondWith": "text",
"responseBody": "={{ ($('Apply Decision').first().json.id ? 'Decision recorded: ' + $('Build Decision SQL').first().json.decision + '. You can close this tab.' : 'No matching pending approval \u2014 already decided or invalid token.') }}",
"options": {}
},
"id": "resp",
"name": "Respond",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
900,
300
]
}
],
"connections": {
"Approve/Reject Link": {
"main": [
[
{
"node": "Build Decision SQL",
"type": "main",
"index": 0
}
]
]
},
"Build Decision SQL": {
"main": [
[
{
"node": "Apply Decision",
"type": "main",
"index": 0
}
]
]
},
"Apply Decision": {
"main": [
[
{
"node": "Respond",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
}
}
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.
postgres
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
04b Outreach: Approval Webhook. Uses postgres. Webhook trigger; 4 nodes.
Source: https://github.com/AskariJafri/job-search-os/blob/main/n8n/workflows/04-outreach/approval-webhook.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.
CMM. Uses httpRequest, postgres, redis. Webhook trigger; 90 nodes.
Scraping. Uses httpRequest, postgres, @apify/n8n-nodes-apify, respondToWebhook. Webhook trigger; 61 nodes.
Workflow B — AI Listing Engine. Uses httpRequest, postgres, errorTrigger. Webhook trigger; 47 nodes.