This workflow follows the HTTP Request → Postgres recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "bridge_ceo_bot",
"nodes": [
{
"parameters": {
"updates": [
"message"
],
"additionalFields": {}
},
"id": "node_trigger_tg_ceo",
"name": "Telegram: user DM",
"type": "n8n-nodes-base.telegramTrigger",
"typeVersion": 1.1,
"position": [
240,
200
],
"disabled": true
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 0 */4 * * *"
}
]
}
},
"id": "node_trigger_daily",
"name": "Schedule: daily",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
240,
400
]
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 0 9 * * 1"
}
]
}
},
"id": "node_trigger_weekly",
"name": "Schedule: weekly",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
240,
600
]
},
{
"parameters": {
"httpMethod": "POST",
"path": "bridge-ceo-invoke",
"responseMode": "lastNode",
"options": {}
},
"id": "node_trigger_webhook_ceo",
"name": "Webhook: invoke",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
240,
800
]
},
{
"parameters": {
"jsCode": "// \u2500\u2500 V2 Task Envelope: task_id propagation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst _env = (typeof $input !== 'undefined' && $input.all()[0].json) || {};\nconst _envBody = _env.body || {};\nconst _inherited_task_id = _envBody.task_id || null;\nconst _gen_task_id = () => 'tid-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 9);\nconst _task_id = _inherited_task_id || _gen_task_id();\n// \u2500\u2500 End V2 Task Envelope \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst src = $input.all()[0];\n// \u2500\u2500 Loop prevention guard \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst _incomingBody = (src.json && src.json.body) ? src.json.body : {};\nconst _attemptCount = (_incomingBody.attempt_count !== undefined)\n ? parseInt(_incomingBody.attempt_count, 10) || 0 : 0;\nif (_attemptCount >= 1) {\n return [{ json: { loop_detected: true, original_task: _incomingBody,\n bot_name: 'ceo',\n task: 'loop_escalation', user_text: null, chat_id: null, from_agent: 'loop_guard',\n now_iso: new Date().toISOString(), daily_spec: [], weekly_spec: [] } }];\n}\n// \u2500\u2500 End loop prevention \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// \u2500\u2500 Deduplication guard \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst messageId = (src.json && src.json.message && src.json.message.message_id)\n ? String(src.json.message.message_id)\n : null;\n\n\n// Detect trigger source by inspecting the item shape.\nlet task = 'scheduled_tick';\nlet user_text = null;\nlet chat_id = null;\nlet from_agent = 'system';\n\nif (src.json && src.json.message && src.json.message.chat) {\n // Telegram trigger\n task = 'user_dm';\n let _utext = src.json.message.text || '';\n // Strip leading slash from Telegram commands so Super Agent does not classify them as shell commands.\n if (_utext.startsWith('/')) {\n const parts = _utext.slice(1).split(/\\s+/);\n const cmd = (parts[0] || '').toLowerCase();\n const rest = parts.slice(1).join(' ');\n if (cmd === 'start' || cmd === 'help') { _utext = 'Hello - please introduce yourself, your role in Bridge Digital, and what you can help with right now.'; }\n else if (cmd === 'status') { _utext = 'Give me a brief status update on your current focus and any open items in your inbox.'; }\n else { _utext = (cmd + ' ' + rest).trim(); }\n }\n user_text = _utext;\n chat_id = src.json.message.chat.id;\n from_agent = 'user';\n} else if (src.json && src.json.body && src.json.body.task) {\n // Webhook trigger\n task = src.json.body.task;\n user_text = src.json.body.message || null;\n from_agent = src.json.body.from_agent || 'system';\n chat_id = null; // webhooks do not carry a telegram chat\n} else if (src.json && src.json.timestamp) {\n // Schedule trigger\n task = 'scheduled_tick';\n user_text = null;\n from_agent = 'system';\n}\n\nreturn [{ json: { message_id: messageId,\n bot_name: \"ceo\",\n task,\n user_text,\n chat_id,\n from_agent,\n now_iso: new Date().toISOString(),\n daily_spec: [{\"hour\": 16, \"minute\": 0, \"task_name\": \"daily_exec_summary\"}],\n weekly_spec: [{\"weekday\": 1, \"hour\": 9, \"minute\": 0, \"task_name\": \"weekly_priority_alignment\"}],\n task_id: _task_id,\n} }];"
},
"id": "node_build_task",
"name": "Build task payload",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
460,
400
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT (value = 'true') AS enabled FROM bridge.system_limits WHERE key = 'ceo_bot_enabled';",
"options": {}
},
"id": "node_read_enabled",
"name": "Read enabled flag",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
680,
400
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "enabled_check",
"leftValue": "={{ $json.enabled }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "node_if_enabled",
"name": "If enabled",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
900,
400
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "WITH open_memos AS (\n SELECT COALESCE(jsonb_agg(row_to_json(m) ORDER BY m.priority_rank, m.created_at), '[]'::jsonb) AS open_inbox\n FROM (\n SELECT memo_id, from_agent, memo_type, priority, subject, body_json,\n created_at, related_lead_id,\n CASE priority WHEN 'urgent' THEN 0 WHEN 'high' THEN 1\n WHEN 'normal' THEN 2 ELSE 3 END AS priority_rank\n FROM bridge.agent_memos\n WHERE to_agent IN ('ceo', 'all') AND status = 'open'\n LIMIT 20\n ) m\n),\nteam_perf AS (\n SELECT COALESCE(jsonb_object_agg(\n p.bot_name,\n jsonb_build_object(\n 'runs_7d', p.total_runs,\n 'success_rate', ROUND(p.success_rate::numeric, 2),\n 'avg_latency_ms',ROUND(p.avg_latency_ms::numeric),\n 'last_run', p.last_run_at\n )\n ), '{}'::jsonb) AS perf_summary\n FROM (\n SELECT bot_name,\n COUNT(*) AS total_runs,\n AVG(CASE WHEN is_successful THEN 1.0 ELSE 0.0 END) AS success_rate,\n AVG(latency_ms) AS avg_latency_ms,\n MAX(created_at) AS last_run_at\n FROM bridge.agent_performance\n WHERE created_at >= NOW() - INTERVAL '7 days'\n GROUP BY bot_name\n ) p\n),\nactive_overrides AS (\n SELECT COALESCE(jsonb_object_agg(bot_name, context_hint), '{}'::jsonb) AS directives\n FROM bridge.bot_context_overrides\n),\nstale_counts AS (\n SELECT\n COUNT(*) FILTER (WHERE priority IN ('high','urgent') AND created_at < NOW() - INTERVAL '5 days') AS stale_high,\n COUNT(*) FILTER (WHERE status = 'open') AS total_open\n FROM bridge.agent_memos WHERE status = 'open'\n),\nproposals AS (\n SELECT COALESCE(jsonb_agg(row_to_json(p)), '[]'::jsonb) AS pending_proposals\n FROM (\n SELECT id, bot_name, change_type, proposed_change, rationale, created_at\n FROM bridge.bot_improvement_proposals\n WHERE status = 'pending'\n ORDER BY created_at DESC LIMIT 5\n ) p\n)\nSELECT\n (SELECT open_inbox FROM open_memos) AS open_inbox,\n (SELECT perf_summary FROM team_perf) AS team_performance_7d,\n (SELECT directives FROM active_overrides) AS active_ceo_directives,\n (SELECT stale_high FROM stale_counts) AS stale_high_priority_count,\n (SELECT total_open FROM stale_counts) AS total_open_memos,\n (SELECT pending_proposals FROM proposals) AS pending_proposals;",
"options": {}
},
"id": "node_fetch_inbox",
"name": "Fetch open inbox",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
1120,
400
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT jsonb_build_object(\n 'now_utc', NOW(),\n 'kpi_last_24h', (\n SELECT jsonb_build_object(\n 'leads_created', (SELECT COUNT(*)::int FROM bridge.leads WHERE created_at >= NOW() - INTERVAL '24 hours'),\n 'demos_built', (SELECT COUNT(*)::int FROM bridge.website_projects WHERE created_at >= NOW() - INTERVAL '24 hours'),\n 'outreach_sent', (SELECT COUNT(*)::int FROM bridge.outreach_messages WHERE direction='outbound' AND sent_at >= NOW() - INTERVAL '24 hours'),\n 'interested_replies',(SELECT COUNT(*)::int FROM bridge.leads WHERE marketing_status='Interested' AND updated_at >= NOW() - INTERVAL '24 hours'),\n 'invoices_paid', (SELECT COUNT(*)::int FROM bridge.leads WHERE finance_status='Paid' AND updated_at >= NOW() - INTERVAL '24 hours'),\n 'expenses_usd', (SELECT ROUND(COALESCE(SUM(amount_usd),0)::numeric, 4) FROM bridge.expenses WHERE occurred_at >= NOW() - INTERVAL '24 hours')\n )\n ),\n 'kpi_last_7d', (\n SELECT jsonb_build_object(\n 'leads_created', (SELECT COUNT(*)::int FROM bridge.leads WHERE created_at >= NOW() - INTERVAL '7 days'),\n 'demos_built', (SELECT COUNT(*)::int FROM bridge.website_projects WHERE created_at >= NOW() - INTERVAL '7 days'),\n 'outreach_sent', (SELECT COUNT(*)::int FROM bridge.outreach_messages WHERE direction='outbound' AND sent_at >= NOW() - INTERVAL '7 days'),\n 'interested_replies',(SELECT COUNT(*)::int FROM bridge.leads WHERE marketing_status='Interested' AND updated_at >= NOW() - INTERVAL '7 days'),\n 'invoices_paid', (SELECT COUNT(*)::int FROM bridge.leads WHERE finance_status='Paid' AND updated_at >= NOW() - INTERVAL '7 days'),\n 'expenses_usd', (SELECT ROUND(COALESCE(SUM(amount_usd),0)::numeric, 4) FROM bridge.expenses WHERE occurred_at >= NOW() - INTERVAL '7 days')\n )\n ),\n 'urgent_memos_across_ecosystem', (\n SELECT COALESCE(jsonb_agg(row_to_json(m)), '[]'::jsonb)\n FROM (\n SELECT memo_id, from_agent, to_agent, subject, priority, created_at, body_json\n FROM bridge.agent_memos\n WHERE status='open' AND priority IN ('urgent','high')\n ORDER BY priority, created_at\n LIMIT 15\n ) m\n ),\n 'researcher_proposals_open', (\n SELECT COALESCE(jsonb_agg(row_to_json(p)), '[]'::jsonb)\n FROM (\n SELECT memo_id, subject, body_json, created_at\n FROM bridge.agent_memos\n WHERE from_agent='researcher' AND memo_type='proposal' AND status='open'\n ORDER BY created_at\n LIMIT 10\n ) p\n ),\n 'campaign_priorities', (\n SELECT COALESCE(jsonb_agg(row_to_json(c)), '[]'::jsonb)\n FROM (\n SELECT campaign_target_id, niche, city, priority, active_flag,\n daily_lead_target, daily_website_limit\n FROM bridge.campaign_targets\n ORDER BY priority DESC, niche, city\n LIMIT 20\n ) c\n )\n) AS context;",
"options": {}
},
"id": "node_fetch_context",
"name": "Fetch bot context",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
1120,
600
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const _buildTask = $('Build task payload').first().json;\nif (_buildTask.loop_detected) {\n return [{ json: {\n message: JSON.stringify({ reply_text: '', actions: [{ type: 'no_op', payload: {\n reason: 'loop_detected', attempt_count: (_buildTask.original_task && _buildTask.original_task.attempt_count) || 1\n }}]}),\n session_id: 'bridge-loop-guard-' + Date.now(),\n task_kind: 'loop_escalation', user_chat_id: null,\n bot_name: _buildTask.bot_name || 'unknown', _pre_formed: true\n }}];\n}\n\nconst task = $('Build task payload').first().json;\n// \u2500\u2500 Memory auto-block check (non-blocking) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nlet _autoBlockActive = false;\nconst _querySubject = task.user_text || task.subject || '';\nif (_querySubject && _querySubject.length > 5) {\n try {\n const _memCheck = await $http.post(\n `${$env.SUPER_AGENT_URL || 'https://super-agent-production.up.railway.app'}/webhook/memory-query`,\n { project_name: _querySubject.slice(0, 100), api_key: $env.N8N_API_KEY || '' }\n );\n if (_memCheck && _memCheck.data && _memCheck.data.auto_block === true) {\n _autoBlockActive = true;\n return [{ json: {\n message: JSON.stringify({\n reply_text: `\ud83d\udeab AUTO-BLOCK: Project has been rejected ${_memCheck.data.rejection_count} time(s). Route to CRO then CEO for override.`,\n actions: [{ type: 'no_op', payload: {\n reason: 'auto_block_repeated_rejection',\n rejection_count: _memCheck.data.rejection_count,\n similar_projects: _memCheck.data.similar_projects\n }}]\n }),\n session_id: `bridge-auto-block-${Date.now()}`,\n task_kind: 'auto_block', user_chat_id: task.chat_id,\n bot_name: task.bot_name, _pre_formed: true\n }}];\n }\n } catch(e) { /* non-blocking */ }\n}\n\n\nconst inboxRow = $('Fetch open inbox').first().json || {};\nconst inbox = Array.isArray(inboxRow.open_inbox) ? inboxRow.open_inbox : [];\nconst teamPerf = inboxRow.team_performance_7d || {};\nconst activeDirectives = inboxRow.active_ceo_directives || {};\nconst staleHighCount = inboxRow.stale_high_priority_count || 0;\nconst totalOpenMemos = inboxRow.total_open_memos || 0;\nconst pendingProposals = Array.isArray(inboxRow.pending_proposals) ? inboxRow.pending_proposals : [];\nconst ctxRow = $('Fetch bot context').first().json || {};\nconst ctx = ctxRow.context ? [ctxRow.context] : [ctxRow];\n\n\nconst globalProtocol = `\n\nGLOBAL OPERATING PROTOCOL (non-negotiable):\n1. ALWAYS respond in valid JSON only. No prose, no markdown outside JSON strings.\n2. EXACT response structure: {\"reply_text\": \"...\", \"actions\": [...]}\n3. reply_text: human-readable Telegram message. Brief, role-appropriate.\n4. actions: array of {type, payload} objects. Empty array if no actions.\n5. NEVER include PLACEHOLDER, PASTE_, TBD, INSERT_HERE values in any field.\n6. NEVER mention internal tools (n8n, shell, SQL, workflow IDs) in reply_text.\n7. If required data is missing \u2192 return no_op with status \"need_input\" + list exact missing fields.\n8. If blocked \u2192 escalate to Chief of Staff. NEVER retry autonomously.\n9. MAX_RETRIES = 1. If attempt_count >= 1 in incoming body_json \u2192 return no_op immediately.\n`;\n\nconst system = \"You are Bridge_CEO_BOT, the strategic control system of Bridge Digital Solutions. You are not an idea generator. You are a profit-focused CEO who decides what the company pursues, drops, and doubles down on.\\n\\nROLE\\nLead the AI enterprise with strategic clarity, discipline, and profit focus. Identify and propose new opportunities, prioritise projects, allocate budget with Finance, resolve agent disagreements, kill or pause underperforming projects, learn from outcomes, define strategic focus areas, and ensure only high-quality opportunities reach the human.\\n\\nDECISION FRAMEWORK\\nFor every opportunity (yours or proposed by Researcher/PM/BizDev), evaluate:\\n - profit potential\\n - time to revenue\\n - execution complexity\\n - resource cost (API tokens, infra hours, agent time)\\n - strategic alignment with current focus area\\n\\nProduce a PRIORITY SCORE 0-100 with the reason. Reject low-leverage work explicitly.\\n\\nREQUIRED OUTPUT (when proposing or arbitrating)\\n- OPPORTUNITY NAME\\n- SUMMARY\\n- PRIORITY SCORE (0-100) + REASON\\n- STRATEGIC FIT (current focus / out-of-focus)\\n- HORIZON (short_term_cash | long_term_asset)\\n- RESOURCE DEMAND (estimated tokens, infra, agent hours)\\n- KEY RISKS\\n- GO / NO-GO RECOMMENDATION\\n\\nLEARNING LOOP\\nAfter project outcomes (use bridge.workflow_events and billing_records to read recent results), record insight memos with:\\n LESSONS LEARNED, WHAT WORKED, WHAT FAILED, STRATEGIC ADJUSTMENT.\\nFeed these back into future scoring decisions.\\n\\nKILL SWITCH AUTHORITY\\nYou may pause projects, cancel them mid-way, reallocate resources, and override non-critical agent disagreements. Triggers: budget overrun, low response rate, poor ROI signals, security blocks repeating, strategic drift. Issue a kill_switch directive memo to PM and Chief of Staff with the project_id and reason.\\n\\nCAPITAL ALLOCATION\\nWhen Finance reports budget options, decide where it goes. Prefer fast cash flow over slow assets unless strategic horizon is explicitly long-term. State the choice and the rejected alternatives.\\n\\nTRADE-OFF DECISION MAKER\\nWhen Finance, Marketing, BizDev, Security disagree, you are the final internal arbiter before human approval. Read the latest open memos from each conflicting party in the inbox before deciding.\\n\\nSTRATEGIC FOCUS\\nMaintain a single explicit focus area at any time (e.g. local services, AI SaaS, high-ticket B2B). Track it in a memo with subject Current strategic focus so other agents can read it. Reject opportunities outside this focus unless ROI score >= 80.\\n\\nPERFORMANCE DASHBOARD\\nFrom bot_context (kpi_last_24h and kpi_last_7d), monitor: leads_created, demos_built, outreach_sent, interested_replies, invoices_paid, expenses_usd. React when any metric trends down for 3 consecutive periods.\\n\\nOPPORTUNITY EVOLUTION\\nBefore proposing a new idea, ask whether an existing project can be iterated to capture the same value. Prefer optimising live funnels over launching cold starts.\\n\\nPRESSURE TEST MODE\\nBefore endorsing any project for human approval, answer: what could go wrong? what would kill this? what assumption is weakest? Document the answers in body_json.pressure_test before issuing the approval_request.\\n\\nREVENUE VALIDATION POLICY\\nWhen reviewing a project for human approval, you MUST include the latest revenue_review memo from bizdev. The Medium-risk approval DM template injects this automatically. If a project has no resolved revenue_review from bizdev, route a revenue_review_request to bizdev instead of approving; do not send an approval DM without revenue validation.\\n\\nINTERACTION RULES\\nWork with Bridge_Chief_Of_Staff_bot as your main coordination proxy. Receive opportunities from Bridge_Researcher_bot and Bridge_Business_Development_bot. Coordinate with Finance / Bridge_Finance_KPI_Monitor for cost and revenue numbers. Use Bridge_Chief_Sec_Off_bot for enterprise risk posture. Use Bridge_PM_BOT for execution capacity.\\n\\nESCALATE OR INTERVENE WHEN priorities conflict materially, profit or budget performance deteriorates, a new idea shows major upside, strategic assumptions appear wrong, risk rises to leadership level, departments are misaligned, or the same project has been blocked by Security twice.\\n\\nOUTPUT STYLE\\nThink and communicate like an owner: strategic, concise, decisive, high-signal. Reject more than you approve. Avoid spreading resources. Focus on highest-leverage actions.\\n\\nSUCCESS DEFINITION\\nThe company allocates effort toward the highest-value opportunities, kills weak projects early, learns from each outcome, and ships fewer but more profitable initiatives over time.\\n\\n\\nCRO GATE (MANDATORY \\u2014 enforced on all project approvals):\\nWhen receiving any project proposal or approval_request:\\n 1. Check body_json.cro_score \\u2014 MUST be present and >= 70\\n 2. Check body_json.recommendation \\u2014 MUST be 'APPROVE'\\n 3. If cro_score missing or < 70 \\u2192 route memo_type='cro_review_request' to CRO, do NOT approve\\n 4. If cro_score >= 70 \\u2192 proceed with normal approval flow\\n\\nFINAL PRIORITY SCORE = Base_PM_Score * 0.40 + cro_score * 0.40 + historical_confidence * 0.20\\n\\nPERFORMANCE AWARENESS:\\n Daily check /webhook/performance-dashboard for agent success rates.\\n If any agent success_rate < 60% for 3 consecutive days \\u2192 include in strategic memo to COS.\\n If authority_level < 3 for any agent \\u2192 require COS co-approval for that agent's decisions.\\n\\nCTO AWARENESS:\\nThe CTO bot owns all technical decisions and architecture. Nothing gets built without CTO approval.\\n\\nWHEN RECEIVING PROJECT PROPOSALS:\\n1. Verify cto_review_complete exists in body_json (alongside cro_score)\\n2. If missing \u2192 ask COS to trigger CTO review before you approve\\n3. CTO recommendation = REJECT overrides your approval authority on technical grounds\\n\\nFINAL APPROVAL SCORING:\\n CRO score (40%) + CTO feasibility (20%) + Finance (20%) + Security (20%) = approval decision\\n Minimum combined threshold: 70 points to approve\\n\\nTECHNICAL VETO:\\nIf CTO flags a fundamental technical risk (infrastructure failure, security hole, unresolvable dependency), you MUST respect the veto even if CRO score is high.\\nDocument veto reason in bridge.project_memory for future reference.\";\n\nconst contextBlock = JSON.stringify({\n now: task.now_iso,\n bot: task.bot_name,\n task_kind: task.task,\n open_inbox: inbox,\n total_open_memos: totalOpenMemos,\n stale_high_priority_count: staleHighCount,\n team_performance_7d: teamPerf,\n active_ceo_directives: activeDirectives,\n pending_improvement_proposals: pendingProposals,\n bot_context: ctx,\n daily_cadence: task.daily_spec,\n weekly_cadence: task.weekly_spec,\n}, null, 2);\n\nconst taskBlock = task.task === 'user_dm'\n ? `The user sent you this message on Telegram:\\n${task.user_text}\\n\\nRespond helpfully and consider issuing actions if appropriate.`\n : task.task === 'scheduled_tick'\n ? (task.task === 'weekly_priority_alignment' ? `WEEKLY TEAM INTELLIGENCE REVIEW: Analyse team_performance_7d for each bot. Identify the single most impactful improvement. If success_rate < 0.70 or a bot has had no runs in 48h, create a bot_improvement_proposal memo to chief_of_staff. Also process all open inbox items and archive resolved ones.` : `This is a scheduled cadence run. Process all open inbox items, escalate stale high-priority memos (stale_high_priority_count > 3), and archive anything resolved.`)\n : `An inter-agent invocation arrived from '${task.from_agent}': ${task.user_text || 'no message body'}. Decide how to respond.`;\n\nconst outputGuard = `\\n\\n[OUTPUT FORMAT]\\nReturn ONLY a JSON object (no prose, no markdown fences):\\n{\\n \"reply_text\": \"<what to send back via Telegram; empty string when a scheduled run should stay silent>\",\\n \"actions\": [\\n {\"type\": \"memo\", \"payload\": {\"to_agent\": \"researcher|chief_of_staff|cso|ceo|cleaner|all\", \"memo_type\": \"status|proposal|directive|question|decision\", \"priority\": \"urgent|high|normal|low\", \"subject\": \"...\", \"body_json\": {...}}},\\n {\"type\": \"archive\", \"payload\": {\"memo_id\": \"<uuid-from-open-inbox>\", \"reason\": \"...\"}},\\n {\"type\": \"query\", \"payload\": {\"sql\": \"<safe single SELECT>\"}},\\n {\"type\": \"escalate\", \"payload\": {\"subject\": \"...\", \"body_json\": {...}}},\\n {\"type\": \"cleanup\", \"payload\": {\"slug\": \"...\", \"reason\": \"...\"}},\\n {\"type\": \"no_op\", \"payload\": {\"reason\": \"...\"}}\\n ]\\n}\\nKeep actions <= 5. Never include non-whitelisted action types. User-facing commentary belongs in reply_text, not in alert actions.`;\n\nconst fullMessage = `${system}${globalProtocol}\\n\\n[CONTEXT]\\n${contextBlock}\\n\\n[TASK]\\n${taskBlock}${outputGuard}`;\n\nreturn [{ json: {\n message: fullMessage,\n session_id: `bridge-${task.bot_name}-${task.now_iso.slice(0,16).replace(/[:T-]/g,'')}`,\n task_kind: task.task,\n user_chat_id: task.chat_id,\n bot_name: task.bot_name,\n} }];"
},
"id": "node_assemble_prompt",
"name": "Assemble prompt",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1340,
400
]
},
{
"id": "node_chat_direct",
"name": "super-agent /webhook/bot-engine",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1560,
400
],
"parameters": {
"method": "POST",
"url": "={{ $env.SUPER_AGENT_URL || 'https://super-agent-production.up.railway.app' }}/webhook/bot-engine",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={ \"bot_name\": {{ JSON.stringify($json.bot_name || \"unknown\") }}, \"task_block\": {{ JSON.stringify($json.message || \"\") }}, \"session_id\": {{ JSON.stringify($json.session_id || \"default\") }}, \"task_kind\": {{ JSON.stringify($json.task || \"agent_invoke\") }}, \"api_key\": {{ JSON.stringify($env.N8N_API_KEY || \"\") }} }",
"options": {
"timeout": 180000,
"response": {
"response": {
"neverError": true
}
},
"retryOnFail": true,
"maxTries": 3,
"waitBetweenTries": 4000
}
}
},
{
"parameters": {
"jsCode": "// \u2500\u2500 Pre-formed short-circuit (loop guard) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst _upstream = $('Assemble prompt').first().json;\nif (_upstream && _upstream._pre_formed) {\n let _pfActions;\n try { _pfActions = JSON.parse(_upstream.message).actions; } catch(e) { _pfActions = []; }\n return [{ json: {\n reply_text: '',\n actions: [{ type: 'no_op', risk: 'low',\n payload: (_pfActions[0] && _pfActions[0].payload) || { reason: 'loop_guard' } }],\n bot_name: _upstream.bot_name, user_chat_id: _upstream.user_chat_id,\n task_kind: 'loop_escalation', model_used: 'loop_guard', parse_error: null\n }}];\n}\n\n// \u2500\u2500 Standard parse + risk + validation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst raw = $json.response || '';\nconst riskMap = { \"memo\": \"low\", \"archive\": \"low\", \"no_op\": \"low\",\n \"escalate\": \"low\", \"query\": \"medium\", \"cleanup\": \"high\" };\nconst defaultRisk = 'low';\n\nlet parsed = null, parseError = null;\ntry {\n let cleaned = raw.trim().replace(/^```(?:json)?/, '').replace(/```$/, '').trim();\n parsed = JSON.parse(cleaned);\n} catch (e) { parseError = e.message; }\nif (!parsed) {\n try { const m = raw.match(/\\{[\\s\\S]*\\}/); if (m) parsed = JSON.parse(m[0]); } catch (e) {}\n}\n\nlet reply_text = '', actions = [];\nif (parsed && typeof parsed === 'object' && parsed.reply_text !== undefined) {\n reply_text = typeof parsed.reply_text === 'string' ? parsed.reply_text : '';\n actions = Array.isArray(parsed.actions) ? parsed.actions.slice(0, 5) : [];\n} else {\n reply_text = '';\n actions = [{ type: 'no_op', payload: {\n reason: 'json_parse_failed',\n parse_error: parseError || 'no structured response from model',\n raw_preview: String(raw).slice(0, 200)\n }}];\n}\n\n// \u2500\u2500 Placeholder validation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst BANNED = ['PASTE_', 'TBD', 'PLACEHOLDER', 'INSERT_HERE', 'YOUR_URL'];\nconst hasPlaceholder = (str) => BANNED.some(p => String(str).includes(p));\nif (hasPlaceholder(reply_text) || actions.some(a => hasPlaceholder(JSON.stringify(a)))) {\n reply_text = '[Output validation failed \u2014 response contained placeholder values. Please provide the actual value.]';\n actions = [{ type: 'no_op', payload: { reason: 'placeholder_detected' }}];\n}\n\n// \u2500\u2500 Internal tool leakage scrubbing (non-blocking) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst leakageMap = {\n 'n8n workflow creation': 'automation system modification',\n 'n8n workflow modification': 'automation system modification',\n 'shell command (destructive)': 'system operation',\n 'shell command': 'system operation',\n 'SQL query': 'data operation',\n};\nfor (const [internal, safe] of Object.entries(leakageMap)) {\n if (reply_text.toLowerCase().includes(internal.toLowerCase()))\n reply_text = reply_text.replace(new RegExp(internal, 'gi'), safe);\n}\n\n// \u2500\u2500 Risk annotation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst annotated = actions.map(a => {\n const type = (a && typeof a.type === 'string') ? a.type : 'no_op';\n let risk = riskMap[type] !== undefined ? riskMap[type] : defaultRisk;\n return { type, risk, payload: a.payload || {} };\n});\n\nconst upstream = $('Assemble prompt').first().json;\n\n// \u2500\u2500 Telegram personality formatting \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction _formatTgMsg(raw, taskKind) {\n if (!raw || raw.length < 3) return raw;\n\n // Parse ISO timestamp for the footer\n const _ts = new Date().toLocaleTimeString('en-GB', {hour:'2-digit',minute:'2-digit',timeZone:'UTC'}) + ' UTC';\n\n // Build structured header\n const _kind = (taskKind || 'report').replace(/_/g,' ');\n let header = '\ud83d\udc51 *CEO \u2014 Gelson Mascarenhas*\\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n';\n\n // Transform the body: bold lines that look like labels, add section icons\n let body = raw\n .replace(/\\*\\*/g, '*') // normalise bold markers\n .replace(/^(#+)\\s+(.+)$/gm, (_, h, t) => '*' + t + '*') // ## heading \u2192 bold\n .replace(/^[-\u2022]\\s+/gm, ' \ud83d\udccc ') // bullet \u2192 section icon\n .replace(/^(\\w[^:\\n]{2,40}):\\s*(.+)$/gm, '*$1:* $2') // Key: val \u2192 bold key\n .replace(/ERROR|FAILED|CRITICAL|BLOCKED/g, '\ud83d\udea8 $&') // flag errors\n .replace(/SUCCESS|COMPLETE|DONE|APPROVED/gi, '\u2705 $&') // flag successes\n .replace(/ACTION:|action:/g, '\ud83c\udfaf *ACTION:*') // highlight actions\n ;\n\n // Escape Markdown special chars NOT inside existing bold spans\n // (Telegram Markdown v1: escape [ ] only)\n body = body.replace(/([\\[\\]])/g, '\\\\$1');\n\n const footer = '\\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n_\u26a1 Bridge Digital Solution_ \u00b7 _' + _ts + '_';\n\n return header + body + footer;\n}\n\n// Apply formatting to reply_text if there is one\nif ($json && $json.reply_text && $json.reply_text.length > 3) {\n const _taskKind = $json.task_kind || upstream && upstream.task_kind || '';\n $json.reply_text = _formatTgMsg($json.reply_text, _taskKind);\n}\n\nreturn [{ json: {\n reply_text, actions: annotated,\n bot_name: upstream.bot_name, user_chat_id: upstream.user_chat_id,\n task_kind: upstream.task_kind, model_used: $json.model_used || 'unknown',\n parse_error: parseError\n}}];"
},
"id": "node_parse_response",
"name": "Parse response + risk tag",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1780,
400
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"typeValidation": "strict"
},
"conditions": [
{
"id": "has_reply",
"leftValue": "={{ ($json.reply_text || '').trim().length }}",
"rightValue": 0,
"operator": {
"type": "number",
"operation": "gt"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "node_if_has_reply",
"name": "If has reply",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
2000,
200
]
},
{
"parameters": {
"method": "POST",
"url": "=https://api.telegram.org/bot{{$env.Bridge_CEO_BOT || $env.Bridge_CEO_BOT}}/sendMessage",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json; charset=utf-8"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={ \"chat_id\": {{ $('Parse response + risk tag').first().json.user_chat_id || $env.BRIDGE_ADMIN_TELEGRAM_CHAT_ID }}, \"text\": \"{{ $('Parse response + risk tag').first().json.reply_text || '(no reply)' }}\" , \"parse_mode\": \"Markdown\" }",
"options": {
"timeout": 15000,
"response": {
"response": {
"neverError": true
}
}
}
},
"id": "node_reply_tg",
"name": "Reply on Telegram",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2880,
400
]
},
{
"parameters": {
"method": "POST",
"url": "https://super-agent-production.up.railway.app/memory/ingest",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "X-Memory-Secret",
"value": "={{$env.MEMORY_INGEST_SECRET}}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={ \"memories\": [ { \"content\": {{ JSON.stringify(($('Parse response + risk tag').first().json.reply_text || '(no reply)') + ' [actions=' + (($('Parse response + risk tag').first().json.actions || []).map(a => a.type).join(',')) + ']') }}, \"memory_type\": \"decision\", \"importance\": 3, \"source\": \"bridge_ceo_bot\"}]}",
"options": {
"timeout": 15000,
"response": {
"response": {
"neverError": true
}
}
}
},
"id": "node_memory_ingest",
"name": "Memory ingest",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
3100,
400
]
},
{
"parameters": {
"jsCode": "const p = $json;\nconst out = [];\nfor (const a of (p.actions || [])) {\n out.push({ json: {\n action_type: a.type,\n action_risk: a.risk,\n action_payload: a.payload,\n bot_name: p.bot_name,\n user_chat_id: p.user_chat_id,\n task_kind: p.task_kind,\n model_used: p.model_used,\n reply_text: p.reply_text,\n }});\n}\n// Always emit at least one item so the reply path runs even when no actions.\nif (out.length === 0) {\n out.push({ json: {\n action_type: 'no_op',\n action_risk: 'low',\n action_payload: { reason: 'no actions from LLM' },\n bot_name: p.bot_name,\n user_chat_id: p.user_chat_id,\n task_kind: p.task_kind,\n model_used: p.model_used,\n reply_text: p.reply_text,\n }});\n}\nreturn out;"
},
"id": "node_split_actions",
"name": "Split actions",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2000,
400
]
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"typeValidation": "strict"
},
"conditions": [
{
"leftValue": "={{ $json.action_risk }}",
"rightValue": "low",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"outputKey": "low"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"typeValidation": "strict"
},
"conditions": [
{
"leftValue": "={{ $json.action_risk }}",
"rightValue": "medium",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"outputKey": "medium"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"typeValidation": "strict"
},
"conditions": [
{
"leftValue": "={{ $json.action_risk }}",
"rightValue": "high",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"outputKey": "high"
}
]
},
"options": {
"allMatchingOutputs": false,
"fallbackOutput": 2
}
},
"id": "node_switch_risk",
"name": "Switch by risk",
"type": "n8n-nodes-base.switch",
"typeVersion": 3.2,
"position": [
2220,
400
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "\nWITH input AS (\n SELECT $1::jsonb AS p\n),\nmemo_insert AS (\n INSERT INTO bridge.agent_memos\n (from_agent, to_agent, memo_type, priority, subject, body_json, related_lead_id)\n SELECT\n $2,\n COALESCE(p->>'to_agent','all'),\n COALESCE(p->>'memo_type','status'),\n COALESCE(p->>'priority','normal'),\n COALESCE(p->>'subject','(no subject)'),\n COALESCE(p->'body_json','{}'::jsonb),\n NULLIF(p->>'related_lead_id','')::uuid\n FROM input\n WHERE $3 = 'memo'\n AND NOT (\n COALESCE(p->>'memo_type','status') = 'bot_improvement_proposal'\n AND EXISTS (\n SELECT 1 FROM bridge.agent_memos dup\n WHERE dup.memo_type = 'bot_improvement_proposal'\n AND dup.status = 'open'\n AND dup.body_json->>'target_bot' = (p->'body_json'->>'target_bot')\n )\n )\n RETURNING memo_id\n),\ncos_trigger AS (\n INSERT INTO bridge.agent_memos (from_agent, to_agent, memo_type, priority, subject, body_json)\n SELECT 'ceo', 'chief_of_staff', 'execution_approved', 'high',\n 'CEO approval granted \u2014 begin execution chain',\n jsonb_build_object('approved_memo_id', memo_id, 'approved_to_agent', to_agent,\n 'original_body', (SELECT p FROM input), 'status', 'execution_approved',\n 'attempt_count', 0)\n FROM memo_insert WHERE memo_type = 'approval_granted'\n RETURNING memo_id AS cos_memo_id\n),\narchive_memo AS (\n UPDATE bridge.agent_memos\n SET status = 'resolved', resolved_at = NOW(), resolved_by = $2,\n resolution_notes = COALESCE((SELECT p->>'reason' FROM input), 'archived by bot')\n WHERE $3 = 'archive'\n AND memo_id = (SELECT NULLIF((p->>'memo_id'),'')::uuid FROM input)\n RETURNING memo_id\n),\nevent_log AS (\n INSERT INTO bridge.workflow_events\n (workflow_name, event_type, details_json)\n SELECT\n $2 || '_bot',\n CASE $3\n WHEN 'memo' THEN 'memo_created'\n WHEN 'archive' THEN 'memo_archived'\n WHEN 'no_op' THEN 'no_op'\n ELSE 'other_low_risk'\n END,\n (SELECT p FROM input)\n RETURNING event_id\n)\nSELECT\n (SELECT memo_id FROM memo_insert) AS memo_created,\n (SELECT cos_memo_id FROM cos_trigger) AS cos_triggered,\n (SELECT memo_id FROM archive_memo) AS memo_archived,\n (SELECT event_id FROM event_log) AS event_logged;\n",
"options": {
"queryReplacement": "={{ JSON.stringify($json.action_payload) }},{{ $json.bot_name }},{{ $json.action_type }}"
}
},
"id": "node_exec_low_risk",
"name": "Execute low-risk action",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
2440,
300
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "=https://api.telegram.org/bot{{$env.Bridge_CEO_BOT || $env.Bridge_CEO_BOT}}/sendMessage",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json; charset=utf-8"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={ \"chat_id\": {{$env.BRIDGE_ADMIN_TELEGRAM_CHAT_ID}}, \"text\": {{ JSON.stringify((function(){ const inbox = ($('Fetch open inbox').first().json && $('Fetch open inbox').first().json.open_inbox) || []; const reviews = inbox.filter(m => m.from_agent==='bizdev' && m.memo_type==='revenue_review'); const latest = reviews.length ? reviews[reviews.length-1] : null; const r = latest && latest.body_json || null; const fromAgent = $json.bot_name || 'agent'; const action = $json.action_type || 'unknown'; const payload = $json.action_payload || {}; const subject = payload.subject || 'No subject'; const lines = []; lines.push('NEW PROJECT APPROVAL REQUEST'); lines.push(''); lines.push('From: ' + fromAgent + ' | Action: ' + action); lines.push('Subject: ' + subject); lines.push(''); if (r) { lines.push('--- REVENUE VALIDATION (Business Dev) ---'); if (r.customer_segments) lines.push('Customers: ' + (Array.isArray(r.customer_segments) ? r.customer_segments.join(', ') : r.customer_segments)); if (r.distribution_channels) lines.push('Channels: ' + (Array.isArray(r.distribution_channels) ? r.distribution_channels.join(', ') : r.distribution_channels)); if (r.revenue_model) lines.push('Revenue Model: ' + r.revenue_model); if (r.partnership_opportunities) lines.push('Partnerships: ' + (Array.isArray(r.partnership_opportunities) ? r.partnership_opportunities.join(', ') : r.partnership_opportunities)); if (r.go_to_market) lines.push('Go-To-Market: ' + r.go_to_market); if (r.time_to_first_revenue_weeks) lines.push('Time to Revenue: ' + r.time_to_first_revenue_weeks + ' weeks'); if (r.revenue_risks) lines.push('Risks: ' + (Array.isArray(r.revenue_risks) ? r.revenue_risks.join('; ') : r.revenue_risks)); if (r.confidence) lines.push('Confidence: ' + r.confidence); if (r.verdict) lines.push('BizDev Verdict: ' + r.verdict); } else { lines.push('!! NO RESOLVED REVENUE_REVIEW FROM BIZDEV !!'); lines.push('This action has been routed to bizdev for revenue validation.'); lines.push('No human approval will be requested until validation completes.'); } lines.push(''); lines.push('Payload: ' + JSON.stringify(payload).slice(0,400)); lines.push(''); lines.push('Reply APPROVE ' + fromAgent + ' ' + action + ' to proceed, or ignore to reject.'); return lines.join('\\n'); })()) }} , \"parse_mode\": \"Markdown\" }",
"options": {
"timeout": 15000,
"response": {
"response": {
"neverError": true
}
}
}
},
"id": "node_medium_approval",
"name": "Medium-risk: approval DM",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2440,
500
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "INSERT INTO bridge.agent_memos (from_agent, to_agent, memo_type, priority, subject, body_json) VALUES ($1, 'chief_of_staff', 'approval_request', 'high', 'Pending medium-risk action awaiting user approval', $2::jsonb) RETURNING memo_id;",
"options": {
"queryReplacement": "={{ $json.bot_name }},{{ JSON.stringify({action_type: $json.action_type, action_payload: $json.action_payload}) }}"
}
},
"id": "node_medium_memo",
"name": "Medium-risk: log pending",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
2660,
500
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "executeQuery",
"query": "INSERT INTO bridge.agent_memos (from_agent, to_agent, memo_type, priority, subject, body_json) VALUES ('ceo', 'ceo', 'escalation', 'urgent', 'HIGH-RISK action requires manual execution', $1::jsonb) RETURNING memo_id;",
"options": {
"queryReplacement": "={{ JSON.stringify({action_type: $json.action_type, action_payload: $json.action_payload}) }}"
}
},
"id": "node_high_escalate",
"name": "High-risk: escalate to CEO",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
2440,
700
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "=https://api.telegram.org/bot{{$env.Bridge_CEO_BOT || $env.Bridge_CEO_BOT}}/sendMessage",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json; charset=utf-8"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={ \"chat_id\": {{$env.BRIDGE_ADMIN_TELEGRAM_CHAT_ID}}, \"text\": \"\\ud83d\\udea8 *HIGH-RISK action blocked (escalated to CEO)*\\n\\n From: {{$json.bot_name}}\\n Action: `{{$json.action_type}}`\\n Payload: {{ JSON.stringify($json.action_payload).slice(0,600) }}\\n\\nThis action was NOT executed. Review the agent_memos table for the escalation.\\n\\u2014 Bridge Agents\", \"parse_mode\": \"Markdown\" }",
"options": {
"timeout": 15000,
"response": {
"response": {
"neverError": true
}
}
}
},
"id": "node_high_alert",
"name": "High-risk: urgent DM",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2660,
700
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={\"ok\": true, \"bot\": {{JSON.stringify($(\"Parse response + risk tag\").first().json.bot_name)}}}",
"options": {}
},
"id": "node_respond",
"name": "Respond OK",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
3320,
400
]
}
],
"connections": {
"Telegram: user DM": {
"main": [
[
{
"node": "Build task payload",
"type": "main",
"index": 0
}
]
]
},
"Schedule: daily": {
"main": [
[
{
"node": "Build task payload",
"type": "main",
"index": 0
}
]
]
},
"Schedule: weekly": {
"main": [
[
{
"node": "Build task payload",
"type": "main",
"index": 0
}
]
]
},
"Webhook: invoke": {
"main": [
[
{
"node": "Build task payload",
"type": "main",
"index": 0
}
]
]
},
"Build task payload": {
"main": [
[
{
"node": "Read enabled flag",
"type": "main",
"index": 0
}
]
]
},
"Read enabled flag": {
"main": [
[
{
"node": "If enabled",
"type": "main",
"index": 0
}
]
]
},
"If enabled": {
"main": [
[
{
"node": "Fetch open inbox",
"type": "main",
"index": 0
}
],
[]
]
},
"Fetch open inbox": {
"main": [
[
{
"node": "Fetch bot context",
"type": "main",
"index": 0
}
]
]
},
"Fetch bot context": {
"main": [
[
{
"node": "Assemble prompt",
"type": "main",
"index": 0
}
]
]
},
"Assemble prompt": {
"main": [
[
{
"node": "super-agent /webhook/bot-engine",
"type": "main",
"index": 0
}
]
]
},
"Parse response + risk tag": {
"main": [
[
{
"node": "If has reply",
"type": "main",
"index": 0
},
{
"node": "Memory ingest",
"type": "main",
"index": 0
},
{
"node": "Split actions",
"type": "main",
"index": 0
}
]
]
},
"If has reply": {
"main": [
[
{
"node": "Reply on Telegram",
"type": "main",
"index": 0
}
],
[]
]
},
"Reply on Telegram": {
"main": [
[
{
"node": "Respond OK",
"type": "main",
"index": 0
}
]
]
},
"Memory ingest": {
"main": [
[
{
"node": "Respond OK",
"type": "main",
"index": 0
}
]
]
},
"Split actions": {
"main": [
[
{
"node": "Switch by risk",
"type": "main",
"index": 0
}
]
]
},
"Switch by risk": {
"main": [
[
{
"node": "Execute low-risk action",
"type": "main",
"index": 0
}
],
[
{
"node": "Medium-risk: approval DM",
"type": "main",
"index": 0
}
],
[
{
"node": "High-risk: escalate to CEO",
"type": "main",
"index": 0
}
]
]
},
"Execute low-risk action": {
"main": [
[
{
"node": "Respond OK",
"type": "main",
"index": 0
}
]
]
},
"Medium-risk: approval DM": {
"main": [
[
{
"node": "Medium-risk: log pending",
"type": "main",
"index": 0
}
]
]
},
"Medium-risk: log pending": {
"main": [
[
{
"node": "Respond OK",
"type": "main",
"index": 0
}
]
]
},
"High-risk: escalate to CEO": {
"main": [
[
{
"node": "High-risk: urgent DM",
"type": "main",
"index": 0
}
]
]
},
"High-risk: urgent DM": {
"main": [
[
{
"node": "Respond OK",
"type": "main",
"index": 0
}
]
]
},
"super-agent /webhook/bot-engine": {
"main": [
[
{
"node": "Parse response + risk tag",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
postgres
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
bridge_ceo_bot. Uses telegramTrigger, postgres, httpRequest. Event-driven trigger; 23 nodes.
Source: https://github.com/gelson12/super-agent/blob/be2fc04ab758f1c8f3cf0702604ae85cd5dbebb7/n8n/bridge_ceo_bot.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Bridge_Finance_BOT. Uses telegramTrigger, postgres, httpRequest. Event-driven trigger; 23 nodes.
bridge_pm_bot. Uses telegramTrigger, postgres, httpRequest. Event-driven trigger; 23 nodes.
Gmail-Calendar. Uses executeWorkflowTrigger, postgres, googleCalendar, httpRequest. Event-driven trigger; 12 nodes.
Gmail-Triage. Uses executeWorkflowTrigger, httpRequest, postgres. Event-driven trigger; 10 nodes.
Phase 0 — Telegram Foundation. Uses telegramTrigger, telegram, httpRequest. Event-driven trigger; 9 nodes.