Context
Approval workflows (Leave Request, Claim, Payment Request) are the most complex feature on the roadmap. This issue specifies the architecture — using n8n PRO as the routing engine and PulseBox as the data/UI layer. No new workflow designer is built — PulseBox provides the data model, status management, and inbox UI; n8n handles routing logic.
Core Principle
PulseBox collection = the form/entity. n8n = the routing brain. They communicate via webhooks + REST API.
Data Model Additions
Status field type enhancement
Collections with approval workflows use a `status` field with locked transitions — not all status changes are allowed from UI directly. Transitions are driven by n8n callbacks.
Add `workflow_enabled` and `workflow_config` to collection metadata:
```json
{
"workflow_enabled": true,
"workflow_config": {
"n8n_workflow_id": "abc123",
"status_field": "status",
"status_transitions": {
"draft": ["submit"],
"pending_l1": [],
"pending_l2": [],
"approved": [],
"rejected": []
},
"submittable_statuses": ["draft"],
"terminal_statuses": ["approved", "rejected"]
}
}
```
`workflow_instances` table
```sql
create table workflow_instances (
id uuid primary key default gen_random_uuid(),
collection_id uuid not null references collections(id),
item_id uuid not null,
tenant_id uuid not null references tenants(id),
current_status text not null,
current_approver_id uuid references auth.users(id),
n8n_execution_id text,
submitted_at timestamptz,
completed_at timestamptz,
created_at timestamptz default now(),
updated_at timestamptz default now()
);
```
`workflow_events` table (audit trail)
```sql
create table workflow_events (
id uuid primary key default gen_random_uuid(),
instance_id uuid not null references workflow_instances(id),
event_type text not null, -- submitted | approved_l1 | approved_l2 | rejected | delegated | escalated
actor_id uuid references auth.users(id),
comment text,
metadata jsonb,
created_at timestamptz default now()
);
```
Flow
```
-
Requester fills form in PulseBox (Leave Request collection item)
-
Hits "Submit" → status changes to "pending_l1"
→ Supabase DB trigger fires: POST n8n webhook { item_id, collection_slug, tenant_id, submitter_id }
-
n8n workflow:
→ reads item data via PulseBox API (GET /api/collections/leave-requests/items/{id})
→ determines approver (from collection data, org hierarchy, or config)
→ sends notification to approver (email / Lark message)
→ waits for approval webhook (n8n "Wait" node)
-
Approver opens PulseBox Approver Inbox → clicks Approve/Reject
→ PulseBox: POST /api/automata/workflow/[instance_id]/respond { decision, comment }
→ Server action: validate approver, advance status, resume n8n wait node
-
n8n resumes:
→ If multi-level: advance to L2, notify next approver, wait again
→ If final: mark approved/rejected, POST back to PulseBox
→ PulseBox updates item status to "approved" or "rejected"
→ Creates workflow_events record
```
UI Changes Required
1. Submit button on collection item
When `workflow_enabled: true` and item is in a submittable status:
- Show "Submit for Approval" button (distinct from Save)
- Confirmation dialog: "Submit this Leave Request for approval?"
- After submit: status locked from manual edit
2. Approver Inbox
Route: `/dashboard/inbox`
- Lists all items where current user is the pending approver
- Columns: Type, Requester, Submitted, Summary, Days pending, Actions
- Quick approve/reject from row (opens dialog for comment)
- Click row → opens item detail with full form (read-only) + approval action panel
3. Requester status tracking
- On item detail: show timeline of events (submitted, L1 approved, L2 pending, etc.)
- "Recall" button if status is pending and not yet actioned
n8n Workflow Strategy
Next Novas vibe-codes approval workflows per use case:
- `next-novas.leave-approval-2l` — 2-level leave approval
- `next-novas.claim-approval-1l` — 1-level claim approval
- `next-novas.payment-approval-3l` — 3-level payment approval
These are published as platform apps (via #19 App Store), installed by tenants, and linked to their specific collections.
Tenants configure via Automata:
- Which collection this workflow applies to
- Who the L1/L2 approvers are (role or specific user)
- Escalation timeout (e.g. 48h → auto-escalate)
- Notification template (email subject, body)
Acceptance Criteria
Note
This is a Phase 5+ feature — do not start until #18, #19, #20 are complete and stable.
Context
Approval workflows (Leave Request, Claim, Payment Request) are the most complex feature on the roadmap. This issue specifies the architecture — using n8n PRO as the routing engine and PulseBox as the data/UI layer. No new workflow designer is built — PulseBox provides the data model, status management, and inbox UI; n8n handles routing logic.
Core Principle
Data Model Additions
Status field type enhancement
Collections with approval workflows use a `status` field with locked transitions — not all status changes are allowed from UI directly. Transitions are driven by n8n callbacks.
Add `workflow_enabled` and `workflow_config` to collection metadata:
```json
{
"workflow_enabled": true,
"workflow_config": {
"n8n_workflow_id": "abc123",
"status_field": "status",
"status_transitions": {
"draft": ["submit"],
"pending_l1": [],
"pending_l2": [],
"approved": [],
"rejected": []
},
"submittable_statuses": ["draft"],
"terminal_statuses": ["approved", "rejected"]
}
}
```
`workflow_instances` table
```sql
create table workflow_instances (
id uuid primary key default gen_random_uuid(),
collection_id uuid not null references collections(id),
item_id uuid not null,
tenant_id uuid not null references tenants(id),
current_status text not null,
current_approver_id uuid references auth.users(id),
n8n_execution_id text,
submitted_at timestamptz,
completed_at timestamptz,
created_at timestamptz default now(),
updated_at timestamptz default now()
);
```
`workflow_events` table (audit trail)
```sql
create table workflow_events (
id uuid primary key default gen_random_uuid(),
instance_id uuid not null references workflow_instances(id),
event_type text not null, -- submitted | approved_l1 | approved_l2 | rejected | delegated | escalated
actor_id uuid references auth.users(id),
comment text,
metadata jsonb,
created_at timestamptz default now()
);
```
Flow
```
Requester fills form in PulseBox (Leave Request collection item)
Hits "Submit" → status changes to "pending_l1"
→ Supabase DB trigger fires: POST n8n webhook { item_id, collection_slug, tenant_id, submitter_id }
n8n workflow:
→ reads item data via PulseBox API (GET /api/collections/leave-requests/items/{id})
→ determines approver (from collection data, org hierarchy, or config)
→ sends notification to approver (email / Lark message)
→ waits for approval webhook (n8n "Wait" node)
Approver opens PulseBox Approver Inbox → clicks Approve/Reject
→ PulseBox: POST /api/automata/workflow/[instance_id]/respond { decision, comment }
→ Server action: validate approver, advance status, resume n8n wait node
n8n resumes:
→ If multi-level: advance to L2, notify next approver, wait again
→ If final: mark approved/rejected, POST back to PulseBox
→ PulseBox updates item status to "approved" or "rejected"
→ Creates workflow_events record
```
UI Changes Required
1. Submit button on collection item
When `workflow_enabled: true` and item is in a submittable status:
2. Approver Inbox
Route: `/dashboard/inbox`
3. Requester status tracking
n8n Workflow Strategy
Next Novas vibe-codes approval workflows per use case:
These are published as platform apps (via #19 App Store), installed by tenants, and linked to their specific collections.
Tenants configure via Automata:
Acceptance Criteria
Note
This is a Phase 5+ feature — do not start until #18, #19, #20 are complete and stable.