n8n form approvals
n8n form approvals use FormNode to capture the human decision before an n8n workflow performs the work. The approver can decide from email or a portal surface, and FormNode sends the result back to n8n as structured JSON.
Approval callback shape
When an approval is decided, FormNode posts an approval.responded event to the configured callback URL.
{
"eventType": "approval.responded",
"approvalId": "8f2a4e1c-6b7d-4f3a-9e8d-1c2b3a4d5e6f",
"formId": "5a8b3c2d-1e4f-4a9b-8c7d-6e5f4a3b2c1d",
"status": "responded",
"decision": "approved",
"comments": "Approved for Sunday maintenance.",
"contextData": {
"customer": "Acme",
"ticketId": "CW-104923",
"requestedBy": "sarah.chen@example.com"
},
"responseData": {
"decision": "approved"
},
"respondedAt": "2026-05-14T18:42:11.337Z",
"respondentInput": {
"email": "client@example.com",
"name": null
}
}In n8n, branch on $json.decision. Preserve $json.approvalId and related ticket or request IDs for auditability.
Two implementation paths
| Path | Use when | n8n responsibility |
|---|---|---|
| Form submission creates an approval | A user submits a request and approval is the next step | Wait for the approval callback before fulfillment |
| n8n creates an approval through the API | n8n detects work first, such as firmware updates or access reviews | Create the approval, then branch when FormNode calls back |
Decision tables
Decision tables are for approvals where each row can have a different outcome. Firmware upgrades, device maintenance, license changes, and batch access reviews often need this shape.
{
"responseData": {
"firewall_decisions": [
{ "deviceId": "fw_001", "decision": "approved", "scheduledFor": "2026-05-18T07:00:00Z" },
{ "deviceId": "fw_002", "decision": "rejected", "reason": "Replace next quarter" }
]
}
}In n8n, iterate the row array. Fulfill approved rows, skip rejected rows, and write both outcomes back to the ticket or system of record.
n8n approval implementation rules
- Do not perform the protected side effect before approval arrives.
- Handle rejection as a first-class workflow path, not an error.
- Handle expiration and missing callbacks separately from rejection.
- Use the approval ID and any request ID you stored in
contextDatafor idempotency. - Keep enough context in
contextDatafor the approver to decide without opening n8n.
Common n8n approval workflows
- Microsoft 365 user onboarding that needs manager approval.
- License changes that need budget owner approval.
- Firmware or patch maintenance that needs customer approval.
- Access requests that need owner approval before provisioning.
- Ticket work that needs customer confirmation before scheduling.
Troubleshooting
n8n continues before the approval
Move the side effect behind the approval callback. Creating the approval should not also provision the user, schedule the upgrade, or update the target system.
Approval email was sent but n8n did not resume
The callback fires only after the approver decides. Check FormNode approval delivery logs and the n8n webhook execution for non-2xx responses.
Rejected rows were still processed
Iterate the decision-table results and branch per row. Do not treat the overall approval as a single approved boolean when the form uses per-row decisions.