Recipe 04, Approval-routed purchase order
Goal: a form submission for a purchase order is auto-approved if under $1 000, routes to a finance approver if higher, and (on approval) records the PO to a database.
Difficulty: Intermediate · Time: ~30 minutes · Connectors: Form trigger, Database (Postgres / similar), optional Slack
Prerequisites
- A database credential (Postgres / MySQL / etc.) with a
purchase_orderstable:CREATE TABLE purchase_orders ( id serial PRIMARY KEY, requester text, vendor text, amount numeric, description text, status text, approved_by text, created_at timestamp DEFAULT now() ); - One workspace user who will act as the finance approver. (Or use a public approval link for an external CFO.)
Finished workflow
┌──────────┐ ┌────────┐
│ Form │───▶│ If │ amount > 1000
│ Trigger │ └───┬────┘
└──────────┘ │
true ┤ false
▼ ▼
┌──────────┐ ┌────────────┐
│ Approval │ │ DB: insert │
│ finance │ │ (status │
└────┬─────┘ │ = approved)│
│ approved └────┬───────┘
▼ │
┌────────────┐ │
│ DB: insert │ │
│ (status │ │
│ = approved)│ │
└─────┬──────┘ │
│ │
└──────┬───────┘
▼
┌──────────┐
│ Email: │
│ receipt │
└──────────┘Step-by-step
1. Form trigger
Replace Start with Form Trigger:
Fields:
requester(text, required)vendor(text, required)amount(number, required, min 0)description(textarea)
Visibility: Authenticated (so the requester is identified). Or Public if you want anyone to submit.
2. If: amount > 1 000
Add an If node connected to the Form Trigger.
Condition:
value1:{{ $trigger.amount }}operator:greaterThanvalue2:1000
3. Approval branch (true)
From the true port, add an Approval node:
- Title:
Purchase order needs your approval - Message:
{{ $trigger.requester }} is requesting **${{ $trigger.amount }}** for {{ $trigger.vendor }}. Reason: {{ $trigger.description }} - Data to show: requester, vendor, amount, description
- Approvers: your finance approver user
- Approval policy:
Any one - Timeout:
72h, actionescalate to: another approver
4. DB insert (both branches)
From the Approval's approved port AND from the If's false port, both connect to a single Database node:
- Operation:
Insert - Table:
purchase_orders - Data:
requester: {{ $trigger.requester }} vendor: {{ $trigger.vendor }} amount: {{ $trigger.amount }} description: {{ $trigger.description }} status: approved approved_by: {{ $node["Approval"].json.approval.approvedBy[0] || "auto" }}
(The || shorthand here is conceptual, implement the fallback via a Set node or Code node as needed.)
5. Email the requester
After Database, add an Email node:
- To:
{{ $trigger.requester }} - Subject:
PO #${{ $node["Database"].json.id }} approved - Body: a short HTML confirmation.
6. Reject branch (optional)
From Approval's rejected port, add another Email node informing the requester that their PO was declined, including the approval comment.
7. Save and activate
Try it
- Share the form URL with yourself. Submit a request for $100.
- Workflow auto-approves; PO appears in the database; you get the confirmation email.
- Submit for $5 000.
- Workflow pauses at Approval.
- Approver gets an in-app notification; navigates to
/approvalsand approves. - PO inserts; email arrives.
Variations
Multi-party approval
For very large POs (> $50k), route through a Switch instead of an If to a second Approval node with multiple approvers and All approvers policy. See Multi-party approvals.
Public approval
Replace the named approver with a public approval link → email to your CFO who doesn't use Flero. See Public approval links.
Slack notification on submission
Add a Slack node between Form Trigger and If, posting to #purchasing with the request details.
Attach a contract PDF
Add a file field to the Form Trigger. Save the file to S3 (File Operations node) and include the link in the database row and email.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Form submits but workflow doesn't fire | Workflow not active | Set status to active |
| Approval never reaches the finance user | They have notifications off | They enable approval notifications in Settings → Notifications |
approved_by is null on auto-approved POs |
The expression fallback doesn't work | Use a Set node to compute it explicitly |
| Database write fails with "amount: invalid type" | Form amount comes through as string | Use Data Transform convert to cast to number before the Database node |
Next
Found something out of date? This page lives in the Flero docs content set.