cosmicstack-labs/workflow automation
Master workflow design, n8n patterns, automation triggers, error handling, and monitoring for reliable business process automation
npx skills add https://github.com/cosmicstack-labs/mercury-agent-skills --skill Workflow Automation
Every workflow must handle failure gracefully. Automations should degrade predictably, not silently. A workflow that fails noisily is better than one that fails silently — at least you know to fix it.
Design every action to be safe to run multiple times. If a workflow retries mid-way, running the same step twice should produce the same result as running it once. This is the single most important property for building reliable automations.
You cannot improve what you cannot see. Every workflow must log key events, expose execution traces, and alert on failures. Treat your workflows as production systems — because they are.
Workflow steps should communicate through well-defined interfaces (files, databases, APIs), not through shared mutable state. This allows steps to be replaced, tested, and scaled independently.
Validate inputs immediately at the start of a workflow. Catch known error conditions early. For unexpected errors, have a fallback path — don't let a single failure cascade through the entire system.
| Level | Name | Description |
|-------|------|-------------|
| 0 | Ad-hoc | Manual processes, no automation. Everything done by hand. |
| 1 | Basic | Simple single-step automations. No error handling. Manual retries. |
| 2 | Structured | Multi-step workflows with basic error handling. Logs exist but aren't monitored. |
| 3 | Reliable | Idempotent steps, retry with backoff, dead letter queues. Alerts on failure. |
| 4 | Observable | Full execution tracing, dashboards, performance metrics. Proactive alerting. |
| 5 | Self-healing | Automatic rollback, compensating transactions, adaptive error handling. |
Target at least Level 3 for any workflow that touches production data.
Every workflow follows a three-phase structure:
// Conceptual workflow structure
Phase 1: TRIGGER — webhook receives event, cron fires, form submitted
Phase 2: ACTION — transform data, call APIs, update databases, send notifications
Phase 3: HANDLE — on success: log, confirm. On error: retry, notify, dead-letter
Pattern: Guard Clause at Entry
Before executing any actions, validate that you have everything you need:
// n8n pseudocode
if (!input.payload.email) {
throw new Error('Missing required field: email');
// This routes to error handler, not the success path
}
Make every operation idempotent by design:
// Idempotent webhook handler pattern
// n8n: Before creating a record, search for duplicates
const existing = await searchDatabase({ email: $json.email });
if (existing) {
// Update existing record instead of creating duplicate
return { id: existing.id, action: 'updated' };
}
return { id: await createRecord($json), action: 'created' };
Distribute work across parallel paths, then aggregate results:
// n8n pattern: Loop Over Items node
// Input: [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
// Each item is processed independently by subsequent nodes
// Results merge back automatically in n8n's item-based execution model
// For explicit fan-in with aggregation:
const results = $input.all();
const summary = {
total: results.length,
succeeded: results.filter(r => r.success).length,
failed: results.filter(r => !r.success).length
};
Best Practice: Set concurrency limits on fan-outs. Don't fire 10,000 requests at once — use batch sizes of 10–50.
For operations spanning multiple systems, use the Saga pattern to maintain data consistency:
Choreography-based Saga: Each service publishes events that trigger the next step. If a step fails, each previous step runs a compensating action.
// Saga transaction example
Steps:
1. Order Service: Reserve inventory → publish "InventoryReserved"
2. Payment Service: Charge customer → publish "PaymentCharged"
3. Shipping Service: Create shipment → publish "ShipmentCreated"
4. Notification: Send confirmation → complete
// On failure at step 2:
// → Trigger compensating transactions:
// - Payment service: Void charge
// - Order service: Release inventory reservation
Orchestration-based Saga: A central coordinator (your n8n workflow) calls each service and manages compensation.
// n8n orchestration pattern
try {
await reserveInventory(orderId, items);
await chargeCustomer(orderId, amount);
await createShipment(orderId);
} catch (error) {
// Compensating transactions in reverse order
await voidShipment(orderId); // if created
await refundCustomer(orderId); // if charged
await releaseInventory(orderId); // if reserved
throw error; // Re-raise after cleanup
}
Rule: Compensating transactions must themselves be idempotent and reliable.
// n8n Webhook node — receive and respond
Configuration:
- HTTP Method: POST
- Path: /orders/new
- Response: Respond to Webhook node
// Best practice: Always validate webhook signatures
function verifyWebhookSignature(payload, signature, secret) {
const crypto = require('crypto');
const expected = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
Production Checklist for Webhooks:
202 Accepted and process async// Cron expression examples in n8n Schedule node
Every hour: 0 * * * *
Daily at 9 AM: 0 9 * * *
Weekdays only: 0 9 * * 1-5
Every 15 min: */15 * * * *
First of month: 0 9 1 * *
Best Practice: Add a 5-minute buffer for time-sensitive jobs. Use timezone-aware scheduling. Avoid "every minute" in production.
n8n supports dedicated error workflows that execute when a regular workflow fails:
// Error workflow receives:
// {
// _error: { message, description, timestamp, workflowId, executionId },
// input_data: { ... } // snapshot of input when error occurred
// }
// Error workflow actions:
// 1. Log to monitoring system
// 2. Send notification (Slack, Email, PagerDuty)
// 3. Write to dead letter queue
// 4. Optionally: attempt recovery or rollback
Every workflow should have an error workflow assigned.
Break large workflows into reusable sub-workflows:
// Main workflow calls sub-workflow
// Sub-workflow ("Send Notification") receives inputs and returns outputs
// Benefits: reusable, testable in isolation, cleaner main flow
// Sub-workflow pattern:
// Input: { to: string, subject: string, body: string }
// Process: validate → format → send → log
// Output: { sent: boolean, messageId: string, timestamp: string }
Sub-workflow guidelines:
When working with files, images, or attachments:
// n8n binary data pattern
// Read File node or HTTP Request node (response format: file)
// Process with: Extract from File, Spreadsheet File, etc.
// Binary data considerations:
// - Memory: Large files (>50MB) can cause OOM errors
// - Use temp storage for large payloads
// - Stream where possible instead of loading into memory
// - Clean up temp files after processing
// Example: Process uploaded CSV
const items = $input.all();
for (const item of items) {
const binaryData = item.binary?.file;
if (!binaryData) continue;
// binaryData.data is already available as a buffer
const rows = parseCSV(binaryData.data.toString());
// ... process rows
}
// Webhook trigger checklist
□ Public endpoint accessible
□ SSL/TLS enabled (HTTPS)
□ Authentication configured (API key, Basic Auth, JWT)
□ Payload validation in place
□ Response configured (200, 202, or custom)
□ Error workflow assigned
□ Rate limiting considered
// Polling pattern: incremental fetch
const lastChecked = await getLastCheckedTimestamp();
const newItems = await fetchChangesSince(lastChecked);
await setLastCheckedTimestamp(Date.now());
return newItems;
// Exponential backoff configuration
// Retry 1: wait 1s
// Retry 2: wait 2s
// Retry 3: wait 4s
// Retry 4: wait 8s
// Retry 5: wait 16s (cap here)
// n8n Error Trigger settings:
// - Retry on failure: YES
// - Max retries: 3-5
// - Wait between retries: exponential
// - Error workflow: [your error workflow]
// Custom backoff in code:
function shouldRetry(attempt, error) {
if (attempt >= 5) return false; // max attempts
if (error.status >= 400 && error.status < 500) return false; // client errors don't retry
return true; // server errors and network issues → retry
}
Retry Policy Rules:
Items that fail all retry attempts go to a dead letter queue (DLQ):
// n8n DLQ pattern using Error Workflow
// Error workflow writes to:
// 1. A spreadsheet or database table marked as "failed"
// 2. A dedicated Slack channel
// 3. An SQS/S3 dead letter bucket
// DLQ record structure:
{
originalPayload: { ... },
error: { message: "...", stack: "..." },
attempts: 5,
timestamp: "2025-01-15T10:30:00Z",
workflowId: "123",
executionId: "456"
}
DLQ Best Practices:
Multi-channel alerting ensures someone is always notified:
// Error notification channels (ordered by severity)
// Critical (production data loss risk):
// - PagerDuty/OpsGenie
// - SMS
// - Phone call
// Warning (retryable or non-critical):
// - Slack/Teams channel
// - Email to team
// - Ticket in help desk system
// Informational (non-urgent):
// - Log to monitoring dashboard
// - Daily digest email
When a workflow fails partway through, you need to undo partial work:
| Strategy | When to Use | Example |
|----------|-------------|---------|
| Compensating Transaction | Saga pattern, distributed systems | Refund payment after shipping fails |
| State Restoration | Idempotent operations | Restore original field value |
| Skip & Log | Non-critical side effects | Failed notification? Log and continue |
| Manual Intervention | Complex or risky rollback | Financial reconciliation |
Every workflow execution generates a record. Use these for debugging:
// Key metrics to track per workflow:
{
executionId: "uuid",
workflowId: "uuid",
workflowName: "Order Processing",
status: "success" | "error" | "running" | "waiting",
startedAt: "2025-01-15T10:00:00Z",
finishedAt: "2025-01-15T10:00:03Z",
duration: 3042, // ms
nodeExecutions: [
{ node: "Webhook", duration: 120, status: "success" },
{ node: "HTTP Request", duration: 2800, status: "success" },
{ node: "Send Email", duration: 122, status: "success" }
],
error: null
}
Set up alerts for these conditions:
// Alert thresholds
□ Workflow failure rate > 1% in 5-minute window
□ Any workflow stuck in "running" state for > 30 minutes
□ Execution count drops to 0 (trigger may be down)
□ Average duration increases by > 2x from baseline
□ Dead letter queue size exceeds threshold
Build a monitoring dashboard with these panels:
// DO NOT hardcode credentials
// ❌ Bad:
const apiKey = 'sk-live-abc123';
// ✅ Good: Use n8n credentials system
// Store in: Settings → Credentials
// Reference by name in nodes
// n8n handles encryption at rest and in transit
// ✅ Good: Use environment variables for deployment-specific values
// process.env.SLACK_WEBHOOK_URL
Credential Rules:
// PII handling in workflows
// 1. Mask or redact sensitive fields in logs
const safeLog = { ...payload, password: '***', ssn: '***-***-1234' };
// 2. Use field-level encryption for sensitive data
const encrypted = encrypt(payload.ssn, encryptionKey);
// 3. Set data retention policies
// - Delete workflow execution data after 30 days
// - Never store raw PII in error logs
// 4. Be GDPR/CCPA aware
// - Support data deletion requests
// - Document what data flows through each workflow
// - Get consent before processing personal data
Security Checklist:
□ All webhooks use HTTPS
□ Credentials stored in n8n credential store (not in code)
□ Error messages don't leak sensitive data
□ Input validation prevents injection attacks
□ Rate limiting on exposed endpoints
□ Audit logging for sensitive operations
□ Regular credential rotation scheduled
□ Network segmentation (don't expose n8n to the internet directly)
Problem: Workflow fails silently. No one knows until a customer complains.
Fix: Every workflow must have an error workflow assigned. This is non-negotiable.
Problem: A malformed payload causes cryptic errors deep in the workflow.
Fix: Validate inputs in the first node after the trigger. Fail fast with clear messages.
Problem: Workflow calls an API 100 times per second and gets throttled.
Fix: Add rate limiting awareness — check Retry-After headers, add delays between calls.
Problem: Changing one API requires updating every workflow that calls it.
Fix: Use sub-workflows for shared logic. One change propagates automatically.
Problem: A retry creates duplicate records, sends duplicate emails, or charges twice.
Fix: Design every mutation to be safe to run multiple times.
Problem: A single workflow has 50+ nodes and is impossible to debug.
Fix: Break into sub-workflows (10-15 nodes max per workflow). Each sub-workflow does one thing well.
Problem: Workflow has been silently failing for 3 days, processing zero items.
Fix: Set up basic health monitoring — failure alerts, execution count tracking, duration metrics.
Problem: API URLs, email addresses, and thresholds embedded in code.
Fix: Use environment variables, n8n settings, or a configuration workflow.
Problem: Processing large files (100MB+) causes out-of-memory errors.
Fix: Stream data, use external processing services, or implement chunking.
Problem: Workflow works perfectly in tests with perfect data, fails on the first real payload.
Fix: Test with invalid data, missing fields, slow APIs, timeouts, and duplicate inputs.
Take cosmicstack-labs/workflow automation from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.