Skip to main content

Overview

Three core n8n workflows automate the Devarno V01T integration:
  1. Ecosystem Sync Workflow — Monitor DEVARNO.json changes, seed organizations
  2. Article Publish Workflow — Detect article status changes, update Flight Path metadata
  3. Quality Gate Workflow — Validate data before sync, run tests, report failures
All workflows are importable JSON and support both scheduled and webhook-triggered execution.

Workflow 1: Ecosystem Sync

Purpose: Periodically check DEVARNO.json for updates and sync organizations to V01T. Trigger: Scheduled (daily at 2 AM UTC) or manual webhook Flow:
Schedule/Webhook → Fetch DEVARNO.json → Validate Schema → Detect Changes → Run seed_ecosystem → Notify → Quality Gate

Node Configuration

1.1 Trigger Node

Type: Schedule or Webhook
{
  "name": "Daily Sync Trigger",
  "type": "n8n-nodes-base.schedule",
  "parameters": {
    "rule": {
      "interval": [1],
      "intervalUnit": "day",
      "startTime": "02:00",
      "timezone": "UTC"
    }
  },
  "position": [50, 200]
}
Or for webhook:
{
  "name": "Manual Sync Webhook",
  "type": "n8n-nodes-base.webhook",
  "parameters": {
    "path": "v01t/ecosystem-sync",
    "method": "POST"
  },
  "position": [50, 200]
}

1.2 Fetch DEVARNO.json

Type: HTTP Request
{
  "name": "Fetch DEVARNO.json",
  "type": "n8n-nodes-base.httpRequest",
  "parameters": {
    "url": "https://raw.githubusercontent.com/devarno-cloud/atlas/main/DEVARNO.json",
    "method": "GET",
    "headers": {
      "Accept": "application/json"
    },
    "responseFormat": "json"
  },
  "position": [250, 200]
}

1.3 Validate Schema

Type: Function
{
  "name": "Validate Schema",
  "type": "n8n-nodes-base.function",
  "parameters": {
    "functionCode": "// Validate DEVARNO.json structure\nconst data = $input.first().json;\nif (!data.organisations || !Array.isArray(data.organisations)) {\n  throw new Error('Invalid DEVARNO.json: missing organisations array');\n}\ndata.organisations.forEach((org, idx) => {\n  if (!org.name || !org.url) {\n    throw new Error(`Org ${idx}: missing name or url`);\n  }\n});\nreturn { valid: true, count: data.organisations.length };"
  },
  "position": [450, 200]
}

1.4 Detect Changes

Type: Function Compares fetched data with last synced version (stored in Telegram/Redis/state):
{
  "name": "Detect Changes",
  "type": "n8n-nodes-base.function",
  "parameters": {
    "functionCode": "const current = $input.first().json;\nconst lastHash = $workflow.variables['DEVARNO_LAST_HASH'] || null;\nconst currentHash = require('crypto')\n  .createHash('sha256')\n  .update(JSON.stringify(current))\n  .digest('hex');\n\nif (lastHash === currentHash) {\n  return { changed: false, message: 'No changes detected' };\n}\n\nreturn {\n  changed: true,\n  currentHash,\n  previousHash: lastHash,\n  organisationCount: current.organisations.length\n};"
  },
  "position": [650, 200]
}

1.5 Run seed_ecosystem

Type: SSH or HTTP Request (to Django management endpoint) Option A: SSH to server
{
  "name": "Run seed_ecosystem (SSH)",
  "type": "n8n-nodes-base.ssh",
  "credentials": {
    "name": "V01T Server SSH"
  },
  "parameters": {
    "command": "cd /Users/alexarno/code/v01t-io/server && python api/manage.py seed_ecosystem",
    "timeout": 60
  },
  "position": [850, 200]
}
Option B: HTTP endpoint
{
  "name": "Run seed_ecosystem (HTTP)",
  "type": "n8n-nodes-base.httpRequest",
  "parameters": {
    "url": "https://v01t-api.devarno.cloud/admin/seed-ecosystem",
    "method": "POST",
    "headers": {
      "Authorization": "Bearer {{ env.V01T_ADMIN_TOKEN }}",
      "Content-Type": "application/json"
    },
    "body": {
      "dryRun": false,
      "skipExisting": false
    },
    "responseFormat": "json"
  },
  "position": [850, 200]
}

1.6 Notify Success

Type: Slack (or email, Telegram)
{
  "name": "Notify Slack",
  "type": "n8n-nodes-base.slack",
  "credentials": {
    "name": "Slack Webhook"
  },
  "parameters": {
    "channel": "#data-ops",
    "text": "✅ Ecosystem sync completed: {{ $nodeData.Fetch_DEVARNO.json.data.organisations.length }} organizations updated"
  },
  "position": [1050, 200]
}

1.7 Quality Gate

Type: Execute Workflow (calls Workflow 3)
{
  "name": "Run Quality Gate",
  "type": "n8n-nodes-base.executeWorkflow",
  "parameters": {
    "workflowId": "quality-gate-workflow-id",
    "parameters": {
      "syncType": "ecosystem",
      "recordCount": "{{ $nodeData.Validate_Schema.json.count }}"
    }
  },
  "position": [1250, 200]
}

Connections

"connections": {
  "Daily_Sync_Trigger": {
    "main": [[{ "node": "Fetch_DEVARNO.json", "type": "main", "index": 0 }]]
  },
  "Fetch_DEVARNO.json": {
    "main": [[{ "node": "Validate_Schema", "type": "main", "index": 0 }]]
  },
  "Validate_Schema": {
    "main": [[{ "node": "Detect_Changes", "type": "main", "index": 0 }]]
  },
  "Detect_Changes": {
    "main": [[{ "node": "Run_seed_ecosystem", "type": "main", "index": 0 }]]
  },
  "Run_seed_ecosystem": {
    "main": [[{ "node": "Notify_Slack", "type": "main", "index": 0 }]]
  },
  "Notify_Slack": {
    "main": [[{ "node": "Run_Quality_Gate", "type": "main", "index": 0 }]]
  }
}

Workflow Definition (JSON)

{
  "name": "Ecosystem Sync Workflow",
  "active": true,
  "nodes": [
    // 1.1 through 1.7 node definitions above
  ],
  "connections": {
    // Connection definitions above
  },
  "settings": {
    "saveExecutionProgress": true,
    "timeoutPerExecution": "3600",
    "errorWorkflow": "error-notification-workflow-id"
  }
}

Workflow 2: Article Publish

Purpose: Monitor article status changes (via webhook or polling) and update V01T Flight Path metadata. Trigger: Webhook from external CMS (Notion integration) or manual trigger Flow:
Article Status Change → Validate Article → Run seed_flight_path → Clear Cache → Notify Slack

Node Configuration

2.1 Webhook Trigger

Type: Webhook
{
  "name": "Article Webhook",
  "type": "n8n-nodes-base.webhook",
  "parameters": {
    "path": "v01t/article-publish",
    "method": "POST",
    "auth": "Bearer"
  },
  "position": [50, 200]
}
Notion Integration (optional): Configure Notion webhook to POST to this endpoint when article status changes. Payload Schema:
{
  "notionPageId": "23426de561ee801bb696ec8809c75950",
  "slug": "dje-skyflow-1.1",
  "title": "Building Skyflow 1.1",
  "excerpt": "Design Journal Entry #1: Take Off",
  "publishedAt": "2025-02-05T19:35:07.322Z",
  "status": "published",
  "tags": ["webdev", "devops"],
  "keywords": ["skyflow", "analytics"]
}

2.2 Validate Article

Type: Function
{
  "name": "Validate Article",
  "type": "n8n-nodes-base.function",
  "parameters": {
    "functionCode": "const article = $input.first().json.body;\nif (!article.notionPageId || !article.slug || !article.status) {\n  throw new Error('Missing required fields: notionPageId, slug, status');\n}\nif (!['draft', 'published', 'archived'].includes(article.status)) {\n  throw new Error(`Invalid status: ${article.status}`);\n}\nreturn { valid: true, article };"
  },
  "position": [250, 200]
}

2.3 Run seed_flight_path

Type: HTTP Request
{
  "name": "Seed Article",
  "type": "n8n-nodes-base.httpRequest",
  "parameters": {
    "url": "https://v01t-api.devarno.cloud/admin/seed-flight-path",
    "method": "POST",
    "headers": {
      "Authorization": "Bearer {{ env.V01T_ADMIN_TOKEN }}",
      "Content-Type": "application/json"
    },
    "body": {
      "article": "{{ $nodeData.Validate_Article.json.article }}"
    },
    "responseFormat": "json"
  },
  "position": [450, 200]
}

2.4 Clear Cache

Type: HTTP Request Invalidate CDN/ISR cache for blog pages:
{
  "name": "Clear Cache",
  "type": "n8n-nodes-base.httpRequest",
  "parameters": {
    "url": "https://devarno-landing.devarno.cloud/api/revalidate",
    "method": "POST",
    "headers": {
      "Authorization": "Bearer {{ env.NEXT_REVALIDATE_TOKEN }}",
      "Content-Type": "application/json"
    },
    "body": {
      "tags": ["v01t-articles", "blog"]
    },
    "responseFormat": "json"
  },
  "position": [650, 200]
}

2.5 Notify Slack

Type: Slack
{
  "name": "Notify Slack",
  "type": "n8n-nodes-base.slack",
  "credentials": {
    "name": "Slack Webhook"
  },
  "parameters": {
    "channel": "#content",
    "text": "📝 Article {{ $nodeData.Validate_Article.json.article.title }} is now {{ $nodeData.Validate_Article.json.article.status }}"
  },
  "position": [850, 200]
}

Connections

"connections": {
  "Article_Webhook": {
    "main": [[{ "node": "Validate_Article", "type": "main", "index": 0 }]]
  },
  "Validate_Article": {
    "main": [[{ "node": "Seed_Article", "type": "main", "index": 0 }]]
  },
  "Seed_Article": {
    "main": [[{ "node": "Clear_Cache", "type": "main", "index": 0 }]]
  },
  "Clear_Cache": {
    "main": [[{ "node": "Notify_Slack", "type": "main", "index": 0 }]]
  }
}

Workflow 3: Quality Gate

Purpose: Validate synced data, run API tests, and report failures. Trigger: Called from Workflow 1/2, or manual webhook Flow:
Validate Data → Test V01T API → Assert Records → Report Metrics → Failure Alert (conditional)

Node Configuration

3.1 Validate Data Integrity

Type: Function
{
  "name": "Validate Data",
  "type": "n8n-nodes-base.function",
  "parameters": {
    "functionCode": "const syncType = $input.first().json.syncType;\nconst recordCount = $input.first().json.recordCount;\n\nif (syncType === 'ecosystem' && recordCount !== 18) {\n  throw new Error(`Expected 18 organizations, got ${recordCount}`);\n}\n\nif (syncType === 'articles' && recordCount !== 4) {\n  throw new Error(`Expected 4 articles, got ${recordCount}`);\n}\n\nreturn { valid: true, syncType, recordCount };"
  },
  "position": [50, 200]
}

3.2 Test V01T API

Type: HTTP Request
{
  "name": "Test API",
  "type": "n8n-nodes-base.httpRequest",
  "parameters": {
    "url": "https://v01t-api.devarno.cloud/trace/{{ $nodeData.Validate_Data.json.syncType === 'ecosystem' ? 'devarno-ecosystem' : 'flight-path' }}/{{ $nodeData.Validate_Data.json.syncType === 'ecosystem' ? 'node' : 'artefact' }}/?page_size=50",
    "method": "GET",
    "headers": {
      "Accept": "application/json"
    },
    "responseFormat": "json"
  },
  "position": [250, 200]
}

3.3 Assert Records

Type: Function
{
  "name": "Assert Records",
  "type": "n8n-nodes-base.function",
  "parameters": {
    "functionCode": "const response = $input.first().json;\nif (!response.success) {\n  throw new Error(`API call failed: ${response.error}`);\n}\n\nconst recordCount = response.data.length;\nconst expectedCount = $nodeData.Validate_Data.json.recordCount;\n\nif (recordCount < expectedCount * 0.95) {\n  throw new Error(`Record count mismatch: expected ~${expectedCount}, got ${recordCount}`);\n}\n\nreturn {\n  passed: true,\n  recordCount,\n  timestamp: response.timestamp,\n  cacheStatus: response.metadata.cache_hit ? 'HIT' : 'MISS'\n};"
  },
  "position": [450, 200]
}

3.4 Report Metrics

Type: HTTP Request (to monitoring service)
{
  "name": "Report Metrics",
  "type": "n8n-nodes-base.httpRequest",
  "parameters": {
    "url": "https://metrics.devarno.cloud/api/v1/events",
    "method": "POST",
    "headers": {
      "Authorization": "Bearer {{ env.METRICS_API_TOKEN }}",
      "Content-Type": "application/json"
    },
    "body": {
      "event": "v01t_sync_quality_gate",
      "syncType": "{{ $nodeData.Validate_Data.json.syncType }}",
      "recordCount": "{{ $nodeData.Assert_Records.json.recordCount }}",
      "cacheStatus": "{{ $nodeData.Assert_Records.json.cacheStatus }}",
      "passed": true,
      "timestamp": "{{ now().toISOString() }}"
    },
    "responseFormat": "json"
  },
  "position": [650, 200]
}

3.5 Failure Alert (conditional)

Type: Slack (only if Assert Records fails)
{
  "name": "Alert Failure",
  "type": "n8n-nodes-base.slack",
  "credentials": {
    "name": "Slack Webhook"
  },
  "parameters": {
    "channel": "#alerts",
    "text": "🚨 Quality gate failed for {{ $nodeData.Validate_Data.json.syncType }} sync"
  },
  "position": [850, 200]
}

Connections

"connections": {
  "Validate_Data": {
    "main": [[{ "node": "Test_API", "type": "main", "index": 0 }]]
  },
  "Test_API": {
    "main": [[{ "node": "Assert_Records", "type": "main", "index": 0 }]]
  },
  "Assert_Records": {
    "main": [[{ "node": "Report_Metrics", "type": "main", "index": 0 }]],
    "error": [[{ "node": "Alert_Failure", "type": "main", "index": 0 }]]
  }
}

Environment Variables

All workflows require these environment variables set in n8n:
VariableExampleUsed By
V01T_ADMIN_TOKENv01t_sk_abc123xyzseed_ecosystem, seed_flight_path
V01T_API_URLhttps://v01t-api.devarno.cloudAll workflows
SLACK_WEBHOOK_URLhttps://hooks.slack.com/services/...Slack notifications
NEXT_REVALIDATE_TOKENisr_token_123Cache invalidation
METRICS_API_TOKENmetrics_key_abcMetrics reporting
DEVARNO_GITHUB_URLhttps://raw.githubusercontent.com/devarno-cloud/atlas/mainFetch DEVARNO.json

Deployment

Import to n8n

  1. via UI: Admin → Workflows → Import from File
  2. via API:
curl -X POST https://n8n.devarno.cloud/api/v1/workflows \
  -H "Authorization: Bearer $N8N_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d @ecosystem-sync.workflow.json

Enable Workflows

# Via n8n CLI
n8n workflow:activate --id=<workflow-id>

# Via API
curl -X PATCH https://n8n.devarno.cloud/api/v1/workflows/<id>/active \
  -H "Authorization: Bearer $N8N_API_TOKEN"

Schedule Execution

Workflows automatically run on schedules defined in nodes. For manual triggers, use webhooks: Ecosystem sync:
curl -X POST https://n8n.devarno.cloud/webhook/v01t/ecosystem-sync \
  -H "Content-Type: application/json"
Article publish:
curl -X POST https://n8n.devarno.cloud/webhook/v01t/article-publish \
  -H "Content-Type: application/json" \
  -d '{
    "notionPageId": "...",
    "slug": "article-slug",
    "status": "published"
  }'

Monitoring

Success Indicators

  • ✅ All nodes complete without errors
  • ✅ Slack notifications received
  • ✅ Metrics recorded in monitoring service
  • ✅ Cache invalidation successful

Failure Scenarios

ScenarioDetectionRecovery
DEVARNO.json fetch failsHTTP error in node 1.2Retry after 5 min, alert
Validation failsSchema error in node 1.3Rollback, notify team
seed_ecosystem failsSSH/HTTP error in node 1.5Manual intervention, post incident report
API test failsAssertion error in node 3.3Pause Workflow 1/2, investigate

Logging

All executions logged to n8n database. Query via:
# Recent executions
curl https://n8n.devarno.cloud/api/v1/workflows/<id>/executions \
  -H "Authorization: Bearer $N8N_API_TOKEN" \
  | jq '.[] | {id, status, duration, finishedAt}'