AutomationFlowsAI & RAG › Agent Flight Recorder

Agent Flight Recorder

Agent Flight Recorder. Uses executeWorkflowTrigger, httpRequest, dataTable, slack. Event-driven trigger; 15 nodes.

Event trigger★★★★☆ complexity15 nodesExecute Workflow TriggerHTTP RequestData TableSlack
AI & RAG Trigger: Event Nodes: 15 Complexity: ★★★★☆ Added:

This workflow follows the Datatable → HTTP Request 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 →

Download .json
{
  "name": "Agent Flight Recorder",
  "nodes": [
    {
      "parameters": {
        "inputSource": "workflowInputs",
        "workflowInputs": {
          "values": [
            {
              "name": "execution_id",
              "type": "string"
            }
          ]
        }
      },
      "id": "1a5f2c60-0f11-4b8e-9a01-0d2b7c4e5f10",
      "name": "Called By Another Workflow",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "typeVersion": 1.2,
      "position": [
        -1440,
        200
      ]
    },
    {
      "parameters": {},
      "id": "2b6f3d71-1a22-4c9f-8b12-1e3c8d5f6a21",
      "name": "Test Manually",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -1440,
        440
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "d1",
              "name": "execution_id",
              "value": "112",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "3c7a4e82-2b33-4daa-9c23-2f4d9e60712b",
      "name": "Demo Execution Id",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.5,
      "position": [
        -1200,
        440
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "c1",
              "name": "execution_id",
              "value": "={{ $json.execution_id }}",
              "type": "string"
            },
            {
              "id": "c2",
              "name": "apiBase",
              "value": "https://YOUR-N8N-INSTANCE/api/v1",
              "type": "string"
            },
            {
              "id": "c3",
              "name": "retryStormThreshold",
              "value": 3,
              "type": "number"
            },
            {
              "id": "c4",
              "name": "maxSampleChars",
              "value": 2000,
              "type": "number"
            }
          ]
        },
        "options": {}
      },
      "id": "4d8b5f93-3c44-4ebb-8d34-304ea071823c",
      "name": "Config",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.5,
      "position": [
        -960,
        300
      ]
    },
    {
      "parameters": {
        "url": "={{ $json.apiBase }}/executions/{{ $json.execution_id }}",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "n8nApi",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "includeData",
              "value": "true"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          },
          "timeout": 30000
        }
      },
      "id": "5e9c60a4-4d55-4fcc-9e45-415fb182934d",
      "name": "Fetch Execution Record",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.5,
      "position": [
        -720,
        300
      ],
      "alwaysOutputData": true,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const first = $input.first();\nconst body = first ? first.json : null;\nconst cap = Number($(\"Config\").first().json.maxSampleChars) || 2000;\nif (!body || body.error || !body.data || !body.data.resultData) {\n  const why = body && body.error ? (body.error.message || \"API error\") : \"execution returned no runData\";\n  return [{ json: { recorder_blind: true, reason: why, tools: [], agents: [], evidence_truncated: false, nodes_seen: 0 } }];\n}\nconst runData = body.data.resultData.runData || {};\nconst wfNodes = body.workflowData && Array.isArray(body.workflowData.nodes) ? body.workflowData.nodes : [];\nconst typeByName = {};\nfor (const n of wfNodes) { typeByName[n.name] = n.type || \"\"; }\nfunction isTool(t) { return typeof t === \"string\" && t.toLowerCase().indexOf(\"tool\") !== -1; }\nfunction isAgent(t) { return typeof t === \"string\" && t.toLowerCase().indexOf(\"agent\") !== -1; }\nconst tools = [];\nconst agents = [];\nlet truncated = false;\nfor (const name of Object.keys(runData)) {\n  const t = typeByName[name] || \"\";\n  const runs = Array.isArray(runData[name]) ? runData[name] : [];\n  for (let i = 0; i < runs.length; i++) {\n    const r = runs[i] || {};\n    let outFull = \"\";\n    try { const m = r.data && r.data.main && Array.isArray(r.data.main[0]) ? r.data.main[0] : []; outFull = JSON.stringify(m); } catch (e) { outFull = \"\"; }\n    if (outFull.length > cap) truncated = true;\n    let inFull = \"\";\n    try { inFull = JSON.stringify(r.inputOverride || {}); } catch (e) { inFull = \"\"; }\n    if (inFull.length > cap) truncated = true;\n    const entry = { name: name, node_type: t, run: i, ok: r.executionStatus !== \"error\", status: r.executionStatus || \"unknown\", ms: Number(r.executionTime) || 0, started_at: r.startTime || null, input_sample: inFull.slice(0, cap), output_sample: outFull.slice(0, cap) };\n    if (isTool(t)) { tools.push(entry); } else if (isAgent(t)) { agents.push(entry); }\n  }\n}\nreturn [{ json: { recorder_blind: false, reason: \"\", tools: tools, agents: agents, evidence_truncated: truncated, nodes_seen: Object.keys(runData).length } }];"
      },
      "id": "6fad71b5-5e66-40dd-8f56-526ac2930a5e",
      "name": "Reconstruct Tool Ledger",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -480,
        300
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const e = $input.first().json;\nconst cfg = $(\"Config\").first().json;\nconst findings = [];\nif (e.recorder_blind) {\n  findings.push({ type: \"RECORDER_BLIND\", severity: \"CRITICAL\", detail: \"could not read the execution record: \" + e.reason + \". No claim was checked this run, so nothing here should be read as a clean bill of health.\" });\n  return [{ json: { findings: findings, ledger: [], claims: [], evidence_complete: false, redactions: 0, tools_called: 0, tools_ok: 0, tools_failed: 0, answer_present: false } }];\n}\nconst RED = [\n  { re: new RegExp(\"sk-[A-Za-z0-9]{12,}\", \"g\"), tag: \"api_key\" },\n  { re: new RegExp(\"(bearer\\\\s+)[A-Za-z0-9._-]{12,}\", \"gi\"), tag: \"bearer_token\" },\n  { re: new RegExp(\"(password|passwd|secret|token|api[_-]?key)\\\\W{1,4}([A-Za-z0-9._-]{8,})\", \"gi\"), tag: \"secret_field\" },\n  { re: new RegExp(\"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\\\.[A-Za-z]{2,}\", \"g\"), tag: \"email\" },\n  { re: new RegExp(\"\\\\b[A-Fa-f0-9]{32,}\\\\b\", \"g\"), tag: \"long_hex_token\" }\n];\nlet redactions = 0;\nconst redactedTags = {};\nfunction redact(s) {\n  let out = String(s || \"\");\n  for (const r of RED) {\n    out = out.replace(r.re, function () { redactions++; redactedTags[r.tag] = true; return \"[REDACTED:\" + r.tag + \"]\"; });\n  }\n  return out;\n}\nconst ledger = [];\nfor (const t of e.tools) {\n  ledger.push({ tool: t.name, node_type: t.node_type, run: t.run, ok: t.ok, status: t.status, ms: t.ms, input: redact(t.input_sample), output: redact(t.output_sample) });\n}\nlet answer = \"\";\nfor (const a of e.agents) { answer += \" \" + redact(a.output_sample); }\nconst lower = answer.toLowerCase();\nconst SIDE_EFFECT = [\"sent\", \"emailed\", \"created\", \"updated\", \"deleted\", \"posted\", \"booked\", \"cancelled\", \"refunded\", \"scheduled\", \"saved\", \"published\", \"uploaded\", \"notified\", \"charged\"];\nconst READ_ONLY = [\"searched\", \"looked up\", \"checked\", \"fetched\", \"retrieved\", \"queried\"];\nconst claims = [];\nfor (const v of SIDE_EFFECT) { if (lower.indexOf(v) !== -1) { claims.push({ verb: v, kind: \"side_effect\" }); } }\nfor (const v of READ_ONLY) { if (lower.indexOf(v) !== -1) { claims.push({ verb: v, kind: \"read\" }); } }\nconst seen = {};\nconst uniqueClaims = [];\nfor (const c of claims) { if (!seen[c.verb]) { seen[c.verb] = true; uniqueClaims.push(c); } }\nconst okCalls = ledger.filter(function (l) { return l.ok; });\nconst failedCalls = ledger.filter(function (l) { return !l.ok; });\nconst hasAnswer = answer.trim().length > 0;\nconst sideEffectClaims = uniqueClaims.filter(function (c) { return c.kind === \"side_effect\"; });\nif (!hasAnswer) {\n  findings.push({ type: \"NO_AGENT_OUTPUT\", severity: \"HIGH\", detail: \"no agent answer was found in this execution, so no claim could be compared against the tool ledger. Point the recorder at an execution that contains an AI Agent node.\" });\n} else if (sideEffectClaims.length > 0 && okCalls.length === 0) {\n  const verbs = sideEffectClaims.map(function (c) { return c.verb; }).join(\", \");\n  findings.push({ type: \"FABRICATED_ACTION\", severity: \"CRITICAL\", detail: \"the agent described doing something with real-world effect (\" + verbs + \") but the execution record contains no successful tool call at all. Either the action never happened, or it happened somewhere this recorder cannot see.\" });\n}\nif (failedCalls.length > 0 && hasAnswer) {\n  const admits = lower.indexOf(\"fail\") !== -1 || lower.indexOf(\"error\") !== -1 || lower.indexOf(\"could not\") !== -1 || lower.indexOf(\"couldn\") !== -1 || lower.indexOf(\"unable\") !== -1 || lower.indexOf(\"problem\") !== -1;\n  if (!admits) {\n    const names = failedCalls.map(function (l) { return l.tool; }).join(\", \");\n    findings.push({ type: \"SILENT_RECOVERY\", severity: \"HIGH\", detail: \"a tool failed (\" + names + \") but the agent answer never mentions any failure. The agent smoothed over a broken step, which reads to the user exactly like success.\" });\n  }\n}\nconst byToolInput = {};\nfor (const l of ledger) { const k = l.tool + \"|\" + l.input; byToolInput[k] = (byToolInput[k] || 0) + 1; }\nfor (const k of Object.keys(byToolInput)) {\n  if (byToolInput[k] > 1) {\n    const toolName = k.split(\"|\")[0];\n    findings.push({ type: \"DUPLICATE_SIDE_EFFECT\", severity: \"CRITICAL\", detail: \"tool \\\"\" + toolName + \"\\\" ran \" + byToolInput[k] + \" times with identical input. If that tool is not idempotent the action happened more than once - the classic retry-after-timeout double send.\" });\n  }\n}\nconst byTool = {};\nfor (const l of ledger) { byTool[l.tool] = (byTool[l.tool] || 0) + 1; }\nconst stormLimit = Number(cfg.retryStormThreshold) || 3;\nfor (const k of Object.keys(byTool)) {\n  if (byTool[k] > stormLimit) {\n    findings.push({ type: \"RETRY_STORM\", severity: \"HIGH\", detail: \"tool \\\"\" + k + \"\\\" was called \" + byTool[k] + \" times in one run, over the threshold of \" + stormLimit + \". An agent that is handed an error and told to cope will often just try again forever.\" });\n  }\n}\nif (redactions > 0) {\n  findings.push({ type: \"SECRET_IN_EVIDENCE\", severity: \"CRITICAL\", detail: redactions + \" secret-shaped value(s) (\" + Object.keys(redactedTags).join(\", \") + \") appeared in tool inputs or outputs and were masked before this record was stored. The values are out of the record, but they were in the agent context, so treat them as exposed and rotate.\" });\n}\nconst INJ = [\"ignore previous\", \"ignore all previous\", \"disregard the above\", \"system:\", \"you are now\", \"new instructions\"];\nfor (const l of ledger) {\n  const lo = String(l.output || \"\").toLowerCase();\n  for (const p of INJ) {\n    if (lo.indexOf(p) !== -1) {\n      findings.push({ type: \"INJECTION_ECHO\", severity: \"HIGH\", detail: \"the output of tool \\\"\" + l.tool + \"\\\" contains instruction-shaped text (\\\"\" + p + \"\\\"). Tool output is untrusted input. Once tool output is stored in memory it gets replayed into every later run.\" });\n      break;\n    }\n  }\n}\nif (e.evidence_truncated) {\n  findings.push({ type: \"EVIDENCE_TRUNCATED\", severity: \"HIGH\", detail: \"at least one tool input or output was larger than the sample cap and was stored only in part. Any verdict below rests on an incomplete record and is provisional.\" });\n}\nreturn [{ json: { findings: findings, ledger: ledger, claims: uniqueClaims, evidence_complete: !e.evidence_truncated, redactions: redactions, tools_called: ledger.length, tools_ok: okCalls.length, tools_failed: failedCalls.length, answer_present: hasAnswer } }];"
      },
      "id": "70be82c6-6f77-41ee-9067-637bd3a41b6f",
      "name": "Reconcile Claims Against Evidence",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -240,
        300
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const r = $input.first().json;\nconst cfg = $(\"Config\").first().json;\nconst nowIso = new Date().toISOString();\nconst findings = Array.isArray(r.findings) ? r.findings : [];\nlet critical = 0;\nlet high = 0;\nfor (const f of findings) { if (f.severity === \"CRITICAL\") critical++; if (f.severity === \"HIGH\") high++; }\nconst blind = findings.some(function (f) { return f.type === \"RECORDER_BLIND\"; });\nlet verdict = \"CLEAN\";\nif (critical > 0) { verdict = \"BREACH\"; } else if (high > 0) { verdict = \"SUSPECT\"; }\nif ((blind || r.evidence_complete === false) && verdict === \"CLEAN\") { verdict = \"UNVERIFIED\"; }\nconst canonical = JSON.stringify({ ledger: r.ledger || [], claims: r.claims || [], findings: findings });\nlet h1 = 2166136261;\nlet h2 = 5381;\nfor (let i = 0; i < canonical.length; i++) {\n  const c = canonical.charCodeAt(i);\n  h1 = (h1 ^ c) >>> 0;\n  h1 = (h1 + ((h1 << 1) + (h1 << 4) + (h1 << 7) + (h1 << 8) + (h1 << 24))) >>> 0;\n  h2 = (((h2 << 5) + h2) + c) >>> 0;\n}\nconst fingerprint = (\"00000000\" + h1.toString(16)).slice(-8) + (\"00000000\" + h2.toString(16)).slice(-8);\nconst coverage = blind ? \"no record - nothing was checked\" : (r.evidence_complete === false ? \"partial record - verdict is provisional\" : \"full record for \" + (r.tools_called || 0) + \" tool call(s)\");\nconst lines = [];\nfor (const f of findings) { lines.push(\"- [\" + f.severity + \"] \" + f.type + \": \" + f.detail); }\nconst alertText = \"Agent Flight Recorder: \" + verdict + \"\\nExecution: \" + cfg.execution_id + \"\\nCoverage: \" + coverage + \"\\nFingerprint: \" + fingerprint + \"\\n\\n\" + (lines.length ? lines.join(\"\\n\") : \"No findings.\");\nreturn [{ json: {\n  pack_key: \"PACK:\" + String(cfg.execution_id) + \":\" + fingerprint,\n  execution_id: String(cfg.execution_id),\n  workflow_id: \"\",\n  verdict: verdict,\n  fingerprint: fingerprint,\n  tools_called: Number(r.tools_called) || 0,\n  claims_made: Array.isArray(r.claims) ? r.claims.length : 0,\n  claims_unsupported: findings.filter(function (f) { return f.type === \"FABRICATED_ACTION\"; }).length,\n  coverage: coverage,\n  findings: JSON.stringify(findings),\n  recorded_at: nowIso,\n  alert_text: alertText,\n  critical_count: critical,\n  high_count: high\n} }];"
      },
      "id": "81cf93d7-7088-42ff-8178-748ce4b52c70",
      "name": "Seal Evidence Pack",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        0,
        300
      ]
    },
    {
      "parameters": {
        "operation": "rowNotExists",
        "dataTableId": {
          "__rl": true,
          "mode": "name",
          "value": "recorder_evidence_packs"
        },
        "matchType": "allConditions",
        "filters": {
          "conditions": [
            {
              "keyName": "pack_key",
              "keyValue": "={{ $json.pack_key }}"
            }
          ]
        }
      },
      "id": "92d0a4e8-8199-4300-9289-859df5c63d81",
      "name": "Only New Packs",
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        240,
        180
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "dataTableId": {
          "__rl": true,
          "mode": "name",
          "value": "recorder_evidence_packs"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "pack_key": "={{ $json.pack_key }}",
            "execution_id": "={{ $json.execution_id }}",
            "workflow_id": "={{ $json.workflow_id }}",
            "verdict": "={{ $json.verdict }}",
            "fingerprint": "={{ $json.fingerprint }}",
            "tools_called": "={{ $json.tools_called }}",
            "claims_made": "={{ $json.claims_made }}",
            "claims_unsupported": "={{ $json.claims_unsupported }}",
            "coverage": "={{ $json.coverage }}",
            "findings": "={{ $json.findings }}",
            "recorded_at": "={{ $json.recorded_at }}"
          },
          "schema": [
            {
              "id": "pack_key",
              "displayName": "pack_key",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "execution_id",
              "displayName": "execution_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "workflow_id",
              "displayName": "workflow_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "verdict",
              "displayName": "verdict",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "fingerprint",
              "displayName": "fingerprint",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "tools_called",
              "displayName": "tools_called",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "canBeUsedToMatch": true
            },
            {
              "id": "claims_made",
              "displayName": "claims_made",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "canBeUsedToMatch": true
            },
            {
              "id": "claims_unsupported",
              "displayName": "claims_unsupported",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "canBeUsedToMatch": true
            },
            {
              "id": "coverage",
              "displayName": "coverage",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "findings",
              "displayName": "findings",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "recorded_at",
              "displayName": "recorded_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            }
          ]
        },
        "options": {}
      },
      "id": "a3e1b5f9-92aa-4411-93a0-960ea6d74e92",
      "name": "Store Evidence Pack",
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        480,
        180
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "v1",
              "leftValue": "={{ $json.verdict }}",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "rightValue": "CLEAN"
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "b4f2c60a-a3bb-4522-84b1-a71fb7e85fa3",
      "name": "Verdict Needs Attention",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        240,
        440
      ]
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "name",
          "value": "#agent-trust"
        },
        "text": "={{ $json.alert_text }}",
        "otherOptions": {}
      },
      "id": "c5a3d71b-b4cc-4633-95c2-b82ac8f960b4",
      "name": "Send Recorder Alert",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.7,
      "position": [
        480,
        440
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "content": "## Input\nCall this after an agent run with an execution_id, or run it manually to test.\nIt reads the execution record, which is where the real tool calls survive even when memory and log streaming drop them (n8n-io/n8n #14361, #28532).",
        "height": 460,
        "width": 900,
        "color": 3
      },
      "id": "d6b4e82c-c5dd-4744-86d3-c93bd90a71c5",
      "name": "Sticky Input",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -1500,
        140
      ]
    },
    {
      "parameters": {
        "content": "## Ledger, then reconciliation\nRebuild what the agent ACTUALLY did, then compare it with what the agent SAID it did. Secrets are masked before anything is stored.",
        "height": 260,
        "width": 480,
        "color": 5
      },
      "id": "e7c5f93d-d6ee-4855-97e4-da4cea1b82d6",
      "name": "Sticky Core",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -540,
        240
      ]
    },
    {
      "parameters": {
        "content": "## Seal, store, alert\nOne pack per examination, keyed on execution + fingerprint. Re-checking an execution and reaching a different verdict appends a row rather than being blocked. CLEAN is unreachable when the record is partial or missing.",
        "height": 420,
        "width": 720,
        "color": 7
      },
      "id": "f8d60a4e-e7ff-4966-88f5-eb5dfb2c93e7",
      "name": "Sticky Seal",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -60,
        120
      ]
    }
  ],
  "connections": {
    "Called By Another Workflow": {
      "main": [
        [
          {
            "node": "Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Test Manually": {
      "main": [
        [
          {
            "node": "Demo Execution Id",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Demo Execution Id": {
      "main": [
        [
          {
            "node": "Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Config": {
      "main": [
        [
          {
            "node": "Fetch Execution Record",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Execution Record": {
      "main": [
        [
          {
            "node": "Reconstruct Tool Ledger",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reconstruct Tool Ledger": {
      "main": [
        [
          {
            "node": "Reconcile Claims Against Evidence",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reconcile Claims Against Evidence": {
      "main": [
        [
          {
            "node": "Seal Evidence Pack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Seal Evidence Pack": {
      "main": [
        [
          {
            "node": "Only New Packs",
            "type": "main",
            "index": 0
          },
          {
            "node": "Verdict Needs Attention",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Only New Packs": {
      "main": [
        [
          {
            "node": "Store Evidence Pack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Verdict Needs Attention": {
      "main": [
        [
          {
            "node": "Send Recorder Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "executionTimeout": 120,
    "saveDataSuccessExecution": "none"
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Agent Flight Recorder. Uses executeWorkflowTrigger, httpRequest, dataTable, slack. Event-driven trigger; 15 nodes.

Source: https://github.com/User0394934/trust-loop/blob/main/workflows/agent-flight-recorder.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Who is this for? Agencies, consultants, and service providers who conduct discovery calls and need to quickly turn conversations into professional proposals.

Tool Think, Tool Calculator, Agent Tool +18
AI & RAG

Content - Newsletter Agent. Uses formTrigger, chainLlm, outputParserStructured, httpRequest. Event-driven trigger; 91 nodes.

Form Trigger, Chain Llm, Output Parser Structured +8
AI & RAG

Template Nodes Example. Uses CUSTOM, formTrigger, executeWorkflowTrigger, chatTrigger. Event-driven trigger; 70 nodes.

Custom, Form Trigger, Execute Workflow Trigger +23
AI & RAG

Main. Uses httpRequest, agent, lmChatGoogleGemini, outputParserStructured. Event-driven trigger; 57 nodes.

HTTP Request, Agent, Google Gemini Chat +4
AI & RAG

This workflow scans an RSS blog feed on a schedule, logs newly found posts to Google Sheets, uses OpenAI to generate role-based LinkedIn drafts, and emails approval links via Gmail; approved drafts ar

Execute Workflow Trigger, Gmail, RSS Feed Read +4