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
contextas 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.figmaand 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
contextto 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
contextvars. - 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-alertswith 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 - publishHrsand approvals pending.
Handling All Cases (Branches & Edge Conditions)
- Multi-platform: Use Split In Batches to iterate
platformsarray; merge results. - Hotfix vs Release: Switch on
branchpattern; 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=falseand 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}}" }
}