AI Automation
Implementing a Low‑Latency Human Approval Gate with Cloudflare Workers AI and n8n
TL;DR: Use Cloudflare Workers AI to generate content, route the output to an n8n webhook that creates a short‑lived approval ticket in a lightweight UI (e.g., Google Forms or a custom page). The ticket is displayed to the approver via a push notification (e.g., Slack or email). Approvers click “Approve” or “Reject”, which instantly returns a Boolean to the Worker, letting the workflow continue without queueing delays. Secure the flow with short‑lived tokens, audit logs, and weekly health checks.
What is the core pattern for a fast human‑in‑the‑loop gate?
The pattern consists of four pieces:
- AI generation step – a Cloudflare Worker runs an LLM request (e.g., Claude or OpenAI) via Cloudflare Workers AI.
- Approval request creation – the Worker calls an n8n webhook that stores the generated text and creates a short‑lived approval URL.
- Human interaction – the URL renders a minimal UI (HTML form) where the reviewer can approve or reject. Notification is sent through Slack, email, or a mobile push.
- Result consumption – the approval decision is sent back to the original Worker via a signed callback, allowing the workflow to continue immediately.
This keeps the latency to the time it takes the human to click a button, eliminating polling loops or long‑running queues.
How do I set up the Cloudflare Worker for instant callbacks?
Below is a minimal example that demonstrates the flow. Save it as worker.js and bind a secret APPROVAL_SECRET in the Worker settings.
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
// 1️⃣ Generate AI output
const aiResponse = await fetch('https://api.cloudflare.com/client/v4/accounts/.../ai/run', {
method: 'POST',
headers: { 'Authorization': `Bearer ${AI_TOKEN}` },
body: JSON.stringify({ prompt: 'Summarize the attached report.' })
})
const { result } = await aiResponse.json()
// 2️⃣ Create approval request via n8n webhook
const approvalId = crypto.randomUUID()
const callbackUrl = `https://example.com/approval-callback?token=${signToken(approvalId)}`
await fetch('https://n8n.example.com/webhook/approval', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: approvalId, text: result, callback: callbackUrl })
})
// 3️⃣ Respond to the client (optional)
return new Response('Approval request sent', { status: 202 })
}
function signToken(id) {
const encoder = new TextEncoder()
const data = encoder.encode(id + Date.now())
const key = encoder.encode(APPROVAL_SECRET)
return crypto.subtle.importKey('raw', key, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'])
.then(cryptoKey => crypto.subtle.sign('HMAC', cryptoKey, data))
.then(sig => btoa(String.fromCharCode(...new Uint8Array(sig))))
}
The callback URL is a one‑time endpoint that the n8n workflow will hit after the reviewer clicks a button.
How does n8n handle the approval UI and callback?
In n8n, create a workflow triggered by the Webhook node (the URL used above). The workflow consists of:
- Set node – store the incoming payload (text, id, callback URL).
- HTTP Request node – send a message to Slack (or email) with a button that links to a small static page (e.g., hosted on Cloudflare Pages) where the reviewer sees the text and two buttons: Approve / Reject.
- Webhook Response node – receives the button click, validates the signed token, and posts the decision back to the original callback URL using a
POSTrequest.
Because the callback URL is signed and expires after a few minutes, a compromised reviewer cannot reuse it.
How can I keep the approval step secure and auditable?
Follow these best practices:
- Short‑lived signed tokens – generate a HMAC token that expires after 5 minutes (see the
signTokenfunction). - Role‑based notification channels – only send the approval request to users with the
approverrole in Slack or your internal directory. - Immutable audit log – in the n8n workflow, after the decision is recorded, append a JSON line to a Cloudflare R2 bucket. Include timestamp, approver ID, decision, and the original AI output.
- Rate limiting – apply a per‑user limit on approval actions (e.g., max 30 approvals per hour) using n8n’s
IFnode with a Redis counter. - Weekly review checklist – verify that no expired tokens remain, that audit logs are stored securely, and that notification channels are still correctly scoped (see the NIST AI RMF for guidance).
What should I monitor after the workflow is live?
Set up a lightweight dashboard (e.g., Grafana with Cloudflare Workers metrics) to track:
- Average approval latency (time from request to button click).
- Failure rate of signed‑token validation.
- Number of approvals per user (to detect unusual spikes).
- Worker error logs (CPU time, request latency).
Alert on thresholds such as latency > 30 seconds or validation failures > 5 %.
When is this pattern better than a full‑blown queue system?
If your approval volume is low (under 100 approvals per day) and you need decisions within seconds, the direct callback approach avoids the overhead of a message queue (e.g., RabbitMQ) and reduces operational cost. For high‑throughput scenarios, consider a queue, but keep the short‑lived token idea for security.
Implementing this pattern gives small teams the speed of a no‑code tool while retaining the control and auditability of a custom solution.
Need a quick review of your current AI workflow security? Reach out to AISecAll for a free assessment.
Want this kind of automation built for your workflow?
AISecAll designs, builds, deploys, and maintains focused AI automations for small companies and independent entrepreneurs.