Context
Depends on #18 (DB) and #19 (install flow). This is the "phone home screen" for all installed apps — where tenants monitor, configure, and operate their automation.
Screen 1 — Automata Dashboard
Route: `/dashboard/studio/automata`
Header strip
```
⚡ Automata [Browse App Store]
8 installed · 6 active · 1 error · 1 paused
```
App list
Each row:
```
● [Icon] KFC EMP Import Integration · Daily Last: 2h ago ✓ Next: 6:00AM [···]
● [Icon] Workday GPC Sync Integration · Daily Last: 1d ago ✗ FAILED [Logs] [···]
○ [Icon] MY Payroll Extension Country Pack · Manual Paused [···]
● [Icon] Leave Report PDF Report · Weekly Last: Mon ✓ Next: Mon [···]
```
Status dot: green (active, last run success), red (last run failed), grey (paused/manual)
`[···]` context menu items (note: destructive actions in dialog only per design rules):
- Configure
- Pause / Resume
- Run Now
- View Logs
- Uninstall → opens confirmation dialog
Activity feed (below app list)
```
Today
08:00 KFC EMP Import ✓ 1,240 rows processed
06:30 Workday GPC Sync ✗ SFTP connection timeout
Yesterday
08:00 KFC EMP Import ✓ 1,198 rows processed
```
Pulled from `integration_job_runs` — last 20 entries across all installed apps.
Screen 2 — Per-App Detail
Route: `/dashboard/studio/automata/[tenant_installed_app_id]`
Tabs
Overview
- App name, publisher, version, description
- Installed date, installed by
- Current status chip (Active / Paused / Error)
- Quick stats: total runs, success rate (last 30 days), last run time
Schedule
- Cron expression editor with plain-English preview
- Enable / Disable toggle
- "Run Now" button (triggers n8n webhook immediately via server action)
- Next scheduled run (calculated from cron expression)
Credentials
Each credential slot:
```
SFTP Password ●●●●●●●● [Update] Last updated: 2026-03-01
API Client Secret ●●●●●●●● [Update] Last updated: 2026-02-14
```
- No "reveal" — update only
- "Update" opens small dialog → masked input → PATCH n8n credential via server action
Config
- Non-sensitive fields from config_schema (SFTP host, target collection, notification emails, etc.)
- Editable form, save button
Access
- View workflow definition: role/user multi-select
- View logs: role/user multi-select
- Save button
Workflow (only shown if user has view_definition access)
- Read-only node list proxied from n8n:
```
- Schedule Trigger — Cron: 0 6 * * *
- SFTP Read — sftp.acme.com/exports/emp.csv
- Parse CSV — delimiter: comma, header: row 1
- Validate Rows — required: emp_id, name, dept
- Upsert to Collection — target: employees
- HTTP Request — POST /api/integration/jobs/{id}/run-result
- Error Handler — email: ops@acme.com
```
- No credential values visible
- Fetched via: `GET {pulsebox}/api/automata/[id]/workflow-nodes` → server proxies n8n API
Logs (only shown if user has view_logs access)
- Execution table: Date/time | Duration | Status | Rows processed | Actions
- Row expand: per-node log from n8n execution detail
- "Download CSV" for error rows
- Pulled via: `GET {pulsebox}/api/automata/[id]/logs` → server proxies n8n executions API
API Proxy Routes (server-side, n8n key never exposed)
```
GET /api/automata/[id]/workflow-nodes
→ validates tenant owns this tenant_installed_app_id
→ validates user has view_definition access per access_policy
→ calls n8n GET /workflows/{n8n_workflow_id}
→ returns simplified node list (name, type, parameters — no credentials)
GET /api/automata/[id]/logs?page=1
→ validates tenant + view_logs access
→ calls n8n GET /executions?workflowId={id}&limit=20
→ merges with integration_job_runs for PulseBox-side summary data
POST /api/automata/[id]/trigger
→ validates tenant owns app, app is enabled
→ calls n8n POST /workflows/{id}/run
→ creates integration_job_runs record with status=running
POST /api/integration/jobs/[id]/run-result (called BY n8n, not by tenant)
→ validates request with shared secret header (N8N_CALLBACK_SECRET env var)
→ upserts integration_job_runs record with final status + summary
→ inserts integration_job_errors rows if any
```
Acceptance Criteria
Context
Depends on #18 (DB) and #19 (install flow). This is the "phone home screen" for all installed apps — where tenants monitor, configure, and operate their automation.
Screen 1 — Automata Dashboard
Route: `/dashboard/studio/automata`
Header strip
```
⚡ Automata [Browse App Store]
8 installed · 6 active · 1 error · 1 paused
```
App list
Each row:
```
● [Icon] KFC EMP Import Integration · Daily Last: 2h ago ✓ Next: 6:00AM [···]
● [Icon] Workday GPC Sync Integration · Daily Last: 1d ago ✗ FAILED [Logs] [···]
○ [Icon] MY Payroll Extension Country Pack · Manual Paused [···]
● [Icon] Leave Report PDF Report · Weekly Last: Mon ✓ Next: Mon [···]
```
Status dot: green (active, last run success), red (last run failed), grey (paused/manual)
`[···]` context menu items (note: destructive actions in dialog only per design rules):
Activity feed (below app list)
```
Today
08:00 KFC EMP Import ✓ 1,240 rows processed
06:30 Workday GPC Sync ✗ SFTP connection timeout
Yesterday
08:00 KFC EMP Import ✓ 1,198 rows processed
```
Pulled from `integration_job_runs` — last 20 entries across all installed apps.
Screen 2 — Per-App Detail
Route: `/dashboard/studio/automata/[tenant_installed_app_id]`
Tabs
Overview
Schedule
Credentials
Each credential slot:
```
SFTP Password ●●●●●●●● [Update] Last updated: 2026-03-01
API Client Secret ●●●●●●●● [Update] Last updated: 2026-02-14
```
Config
Access
Workflow (only shown if user has view_definition access)
```
```
Logs (only shown if user has view_logs access)
API Proxy Routes (server-side, n8n key never exposed)
```
GET /api/automata/[id]/workflow-nodes
→ validates tenant owns this tenant_installed_app_id
→ validates user has view_definition access per access_policy
→ calls n8n GET /workflows/{n8n_workflow_id}
→ returns simplified node list (name, type, parameters — no credentials)
GET /api/automata/[id]/logs?page=1
→ validates tenant + view_logs access
→ calls n8n GET /executions?workflowId={id}&limit=20
→ merges with integration_job_runs for PulseBox-side summary data
POST /api/automata/[id]/trigger
→ validates tenant owns app, app is enabled
→ calls n8n POST /workflows/{id}/run
→ creates integration_job_runs record with status=running
POST /api/integration/jobs/[id]/run-result (called BY n8n, not by tenant)
→ validates request with shared secret header (N8N_CALLBACK_SECRET env var)
→ upserts integration_job_runs record with final status + summary
→ inserts integration_job_errors rows if any
```
Acceptance Criteria