From project intake to design, QA, development, and publishing—this hands-on tutorial shows how to wire an opinionated, production-ready workflow in n8n with approvals, rollbacks, observability, and SLAs.
Prerequisites
- n8n (self-hosted or cloud), with Webhook, HTTP Request, Slack, Notion/Trello/Jira, GitHub, Google Drive/S3, Function, IF, Wait, Merge, Execute Workflow nodes.
- Credentials: Slack Bot token, GitHub PAT, Jira/Notion API, Store APIs (Google Play Developer API / App Store Connect) or deployment webhooks for web apps.
- Environment variables in n8n:
N8N_BASE_URL
,SLACK_CHANNEL_ENGINEERING
,GITHUB_ORG
,ARTIFACT_BUCKET
.
Architecture Overview
[Webhook: Project Intake] → [Function: Normalize Payload] → [Execute Workflow: DESIGN] → [Execute Workflow: DESIGN_QA (approval loop)] → [Execute Workflow: DEVELOPMENT] → [Execute Workflow: DEV_QA (test gates)] → [Execute Workflow: PUBLISH] ↘ on_error → [Error Workflow: Alert + Ticket + Retry/Abort]
Keep each phase in a dedicated workflow for reusability. The orchestrator passes a context
object (project id, platform, repo, build number, SLA timestamps).
Canonical Context Object
{ "projectId": "PRJ-2417", "client": "Acme Co", "platforms": ["ios","android","web"], "repo": "github.com/{{GITHUB_ORG}}/acme-app", "branch": "release/1.4.0", "designSpecUrl": "https://notion.so/.../spec", "deadline": "2025-09-10T23:59:00Z", "sla": { "designHrs": 24, "qaHrs": 16, "publishHrs": 8 }, "artifacts": { "figma": "", "builds": [], "testReport": "" }, "approvals": { "designLead": false, "productOwner": false, "qaLead": false } }
Workflow 0 — Orchestrator & Intake
- Webhook (POST
/intake
): Accept new project payload or manual start. - Function: validate inputs; hydrate defaults; compute SLA timestamps.
- Jira/Notion Node: create master ticket/page; store
context
. - Slack: announce kickoff with deep links and “Approve/Block” buttons (use Slack interactivity to hit an Approve Webhook in n8n).
- Execute Workflow → DESIGN, pass
context
as JSON.
// Function: Normalize Payload (snippet) const body = items[0].json; if (!body.projectId) throw new Error("projectId required"); const now = new Date(); return [{ json: { ...body, sla: body.sla ?? { designHrs:24, qaHrs:16, publishHrs:8 }, createdAt: now.toISOString() }}];
Workflow 1 — DESIGN (Spec, Assets, Handoff)
- HTTP Request → Figma API: export redlines & assets; save to S3/Drive.
- Set: write
artifacts.figma
and asset URLs intocontext
. - Slack: post design preview; include Approve/Request-Changes buttons.
- Wait for Webhook: /design/approve or /design/changes.
- IF: if changes → loop back to design node (limit 3 cycles with a counter).
- Merge (Pass-through): return updated
context
to orchestrator.
// Slack button payload (example) { "text": "Design ready for PRJ-2417", "actions": [ { "type":"button","text":"✅ Approve","url":"{{N8N_BASE_URL}}/webhook/design/approve?projectId=PRJ-2417" }, { "type":"button","text":"📝 Request Changes","url":"{{N8N_BASE_URL}}/webhook/design/changes?projectId=PRJ-2417" } ] }
Workflow 2 — DESIGN-QA (Pixel Checks, Accessibility)
- HTTP Request → Visual regression service (Percy/Chromatic) start build.
- IF: accessibility scan (Stark/axe API) score < threshold → open ticket + notify.
- Wait: block until QA lead hits /design-qa/approve.
- Slack: “Design-QA Passed” with report links.
Workflow 3 — DEVELOPMENT (Branch, Build, Artifact)
- GitHub: create branch
release/x.y.z
; open PR with checklist. - HTTP Request: trigger CI (GitHub Actions/CircleCI) with
context
vars. - Wait: poll CI status endpoint; on failure → notify & retry up to 2.
- HTTP Request: fetch build artifacts (APK/IPA/web bundle); push to S3/Drive.
- Set: append artifact URLs to
artifacts.builds
.
# Example CI dispatch (GitHub Actions) POST https://api.github.com/repos/{{GITHUB_ORG}}/acme-app/actions/workflows/release.yml/dispatches { "ref": "release/1.4.0", "inputs": { "platforms":"ios,android,web", "projectId":"PRJ-2417" } }
Workflow 4 — DEV-QA (Automated & Manual Gates)
Failures open/attach to the release ticket and trigger auto-rollback of the CI environment if needed.
Workflow 5 — PUBLISH (Stores & Web)
- IF platform contains android → HTTP Google Play: upload bundle, track “internal” → promote to “production”.
- IF platform contains ios → HTTP App Store Connect: upload via CI; submit for review.
- IF platform contains web → HTTP to hosting (Vercel/Netlify/CloudFront invalidation) to deploy.
- Slack: publish summary with build numbers, changelog, links.
- Notion/Jira: update release page with artifacts & audit trail.
// Example publish summary payload { "projectId":"PRJ-2417", "version":"1.4.0", "ios":"App Store submission #1023", "android":"Play rollout 20% staged", "web":"Deployed to prod @ 14:20 UTC", "artifacts":["s3://artifacts/acme/1.4.0/app-release.apk","..."], "changelog":"Bug fixes, performance improvements" }
Global Error Handling & Resilience
- Error Trigger workflow: catches any failure; posts to
#eng-alerts
with run URL. - Function: categorize error (network, validation, auth, store review) → choose path.
- Retry policy: 3 attempts, exponential backoff (15s, 60s, 5m) for transient HTTP 5xx.
- Dead-letter: write failed payload + logs to S3 with
projectId/runId
. - Ticket: auto-create/attach incident to release ticket with labels.
Approvals & Human-in-the-Loop
Use Slack interactive buttons that hit n8n Webhooks. Persist decisions inside the context.approvals
object and enforce at each gate.
// Example gate check (Function node) const c = items[0].json.context; if (!c.approvals.designLead) throw new Error("Design approval missing"); return items;
SLAs, Metrics, and Reporting
- Emit metrics per step: start/end timestamps, retries, latency. Store in a sheet/DB.
- Daily digest workflow: aggregates cycle times by phase; posts a burn-down to Slack.
- Auto-escalate if
now > deadline - publishHrs
and approvals pending.
Handling All Cases (Branches & Edge Conditions)
- Multi-platform: Use Split In Batches to iterate
platforms
array; merge results. - Hotfix vs Release: Switch on
branch
pattern; hotfix skips design phases. - Design changes late: If a change arrives after DEV-QA, reopen DESIGN-QA sub-workflow and bump version (e.g., 1.4.1-rc1).
- Store rejection: Route to “Rework” lane, create tasks, loop back to DEV-QA automatically.
- Client veto: Approve button writes
approvals.productOwner=false
and halts downstream nodes with a clear Slack summary.
Export & Reuse
Tag workflows with team=mobile
, type=qa
, lane=publish
. Keep a Template folder; export JSONs to Git for version control and peer review.
// Minimal Execute Workflow mapping { "workflowId": "WORKFLOW_DEV_QA", "options": { "waitForExecution": true }, "jsonParameters": true, "parameters": { "context": "={{$json}}" } }