{
  "name": "Protected Outbound AI Review Agent — BiDigest",
  "nodes": [
    {
      "parameters": {},
      "id": "22222222-2222-2222-2222-222222222201",
      "name": "When outbound send proposed",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "out-ch",
              "name": "channel",
              "value": "email_sequence",
              "type": "string"
            },
            {
              "id": "out-rc",
              "name": "recipientCount",
              "value": 120,
              "type": "number"
            },
            {
              "id": "out-tpl",
              "name": "templateId",
              "value": "sdr-cold-v3",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "22222222-2222-2222-2222-222222222202",
      "name": "Sample outbound payload",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        220,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://bidigest.com/api/v1/admissibility/verify",
        "authentication": "none",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ $env.BIDIGEST_API_KEY || 'Bearer PASTE_YOUR_BIDIGEST_KEY' }}"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({\n  jurisdiction: { regionCode: 'us-fed', industrySector: 'financial' },\n  intent: {\n    agentId: 'agency-agent:' + $json.templateId,\n    actionType: 'outbound_send',\n    proposedPayload: {\n      action: 'outbound_send',\n      channel: $json.channel,\n      recipientCount: $json.recipientCount,\n      templateId: $json.templateId,\n      synthesizedClaims: [\n        'BiDigest Trustee program BDG-TRUSTEE-001 references the published governance framework.',\n      ],\n      shadowCitations: ['https://bidigest.com/governance'],\n    },\n  },\n  sessionContext: {\n    sessionId: 'session-' + $json.templateId + '-outbound',\n    userId: 'agency_agent',\n    sessionStartTime: $now.toISO(),\n  },\n  groundTruthBinding: {\n    domain: 'bidigest.com',\n    regulatoryId: 'BDG-TRUSTEE-001',\n  },\n}) }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "22222222-2222-2222-2222-222222222203",
      "name": "BiDigest verify",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        440,
        0
      ],
      "notesInFlow": true,
      "notes": "Auth: None + Send Headers. Set n8n variable BIDIGEST_API_KEY (Bearer bid_live_...) from bidigest.com/partners/ai-automation/recipes — or paste that full line into Authorization below. Save → Execute workflow."
    },
    {
      "parameters": {
        "jsCode": "const INDETERMINATE_REASON_CODES = new Set([\n  'VALIDATION_UNAVAILABLE',\n  'TIMEOUT',\n  'DEPENDENCY_UNAVAILABLE',\n  'DEPENDENCY_FAILURE',\n  'INTERNAL_ERROR',\n  'SIGNING_UNAVAILABLE',\n  'POLICY_EVAL_UNAVAILABLE',\n  'RATE_LIMITED',\n]);\n\nconst raw = $input.first().json;\nconst body = raw.legacy ?? raw.data ?? raw;\nconst errMsg = typeof raw.error === 'string' ? raw.error : (body?.error ?? '');\nconst statusCode = raw.statusCode ?? raw.status ?? null;\n\nif (statusCode === 401 || errMsg.includes('API key')) {\n  return {\n    verdict: 'REJECTED',\n    receipt_id: null,\n    retry_eligible: false,\n    recompute_required: false,\n    hitl_eligible: false,\n    auth_error: true,\n    setup_hint:\n      'Set n8n variable BIDIGEST_API_KEY to your full Bearer bid_live_... line (recipes page), OR paste it in BiDigest verify → Send Headers → Authorization. Then Save and Execute workflow.',\n    raw,\n  };\n}\n\nconst reason_code = body.reason_code ?? null;\nlet verdict = 'REJECTED';\nlet retry_eligible = false;\nlet recompute_required = false;\nlet hitl_eligible = false;\nlet setup_hint = null;\n\nif (body.reason_code === 'TENANT_NOT_PROVISIONED') {\n  setup_hint =\n    'Ground truth not provisioned on production. Re-import template JSON — verify must use bidigest.com + BDG-TRUSTEE-001 (not agency-automation-demo.bidigest.com).';\n}\n\nif (body.reason_code === '403_REBONDING_REQUIRED') {\n  verdict = 'REVIEW_REQUIRED';\n  hitl_eligible = true;\n} else if (body.admissibility_status === 'APPROVED') {\n  verdict = 'APPROVED';\n} else if (reason_code && INDETERMINATE_REASON_CODES.has(reason_code)) {\n  verdict = 'VALIDATION_UNAVAILABLE';\n  retry_eligible = true;\n  recompute_required = true;\n  hitl_eligible = false;\n} else {\n  verdict = 'REJECTED';\n}\n\nconst p = $('Sample outbound payload').first().json;\n// Agency policy: escalate only when verify already APPROVED\nif (verdict === 'APPROVED' && p.recipientCount > 25) {\n  verdict = 'REVIEW_REQUIRED';\n  hitl_eligible = true;\n  retry_eligible = false;\n  recompute_required = false;\n}\nif (verdict === 'APPROVED' && String(p.templateId).includes('cold')) {\n  verdict = 'REVIEW_REQUIRED';\n  hitl_eligible = true;\n  retry_eligible = false;\n  recompute_required = false;\n}\nconst receipt_id = body.decision_receipt?.receipt_id ?? raw.merkleReceipt?.receiptId ?? null;\nconst verify_status = body.admissibility_status ?? null;\n\n// Continuity doctrine — replay harness cases A–E (distinct from retry_eligible)\nconst forward_receipt_authorizes_compensation = false;\nlet continuation_permitted = false;\nlet resume_without_fresh_verify = false;\nif (recompute_required || verdict === 'VALIDATION_UNAVAILABLE') {\n  continuation_permitted = false;\n  resume_without_fresh_verify = receipt_id != null;\n} else if (verdict === 'APPROVED' && !hitl_eligible) {\n  continuation_permitted = true;\n}\n\nconst branch =\n  verdict === 'APPROVED'\n    ? 'Continue'\n    : verdict === 'REVIEW_REQUIRED'\n      ? 'Human approval'\n      : verdict === 'VALIDATION_UNAVAILABLE'\n        ? 'Recompute · Not HITL'\n        : 'Hard stop';\nlet user_message = 'Execution blocked.';\nif (statusCode === 401 || errMsg.includes('API key')) {\n  user_message = setup_hint ?? 'Fix API key: set BIDIGEST_API_KEY or paste Bearer bid_live_… in Authorization.';\n} else if (setup_hint) {\n  user_message = setup_hint;\n} else if (verdict === 'REVIEW_REQUIRED') {\n  user_message =\n    'BiDigest authorized this attempt; agency policy requires human sign-off before payment.';\n} else if (verdict === 'APPROVED') {\n  user_message = 'Authorized — proceed to payment or ERP.';\n} else if (verdict === 'VALIDATION_UNAVAILABLE') {\n  user_message = 'Verify indeterminate — run fresh verify. Not a human-approval path.';\n} else if (p.operationType === 'compensation_reversal' && !p.compensationVerifyComplete) {\n  user_message =\n    'Compensation requires fresh verify boundary — forward receipt is evidence only.';\n} else {\n  user_message = `Blocked (${reason_code ?? 'DENIED'}). Hard stop — do not route to human review.`;\n}\nreturn {\n  verdict,\n  branch,\n  user_message,\n  verify_status,\n  receipt_id,\n  retry_eligible,\n  recompute_required,\n  hitl_eligible,\n  continuation_permitted,\n  forward_receipt_authorizes_compensation,\n  resume_without_fresh_verify,\n  reason_code,\n  setup_hint,\n  raw,\n};"
      },
      "id": "22222222-2222-2222-2222-222222222204",
      "name": "Map to agency verdict",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        660,
        0
      ]
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.verdict }}",
                    "rightValue": "APPROVED",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Continue"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.verdict }}",
                    "rightValue": "REVIEW_REQUIRED",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Human approval"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.verdict }}",
                    "rightValue": "REJECTED",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Hard stop"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.verdict }}",
                    "rightValue": "VALIDATION_UNAVAILABLE",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Recompute · Not HITL"
            }
          ],
          "options": {
            "fallbackOutput": "extra"
          }
        }
      },
      "id": "22222222-2222-2222-2222-222222222205",
      "name": "Branch on verdict",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        880,
        0
      ]
    },
    {
      "parameters": {
        "content": "## READ FIRST — 3 steps\n1. **API key** from https://bidigest.com/partners/ai-automation/recipes\n2. n8n → Settings → Variables → **BIDIGEST_API_KEY** = `Bearer bid_live_…` (full line)\n   **OR** paste that line into BiDigest verify → Send Headers → Authorization\n3. **Save** → **Execute workflow** → open **Map to agency verdict**\n\n**Where you land:** Map output **branch** + **user_message** (from BiDigest verify + agency policy).\nSample amount/vendor do **not** pick the branch by themselves."
      },
      "id": "22222222-2222-2222-2222-222222222221",
      "name": "READ FIRST",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -320,
        -320
      ]
    },
    {
      "parameters": {
        "content": "## Setup details\n✓ BiDigest verify: Authentication = None + Send Headers (correct).\n✓ Ground truth: **bidigest.com** + **BDG-TRUSTEE-001** only.\n✗ **agency-automation-demo.bidigest.com** → TENANT_NOT_PROVISIONED → Hard stop.\n\n**One JSON file per workflow** — download from recipes; do not maintain duplicate imports in n8n.\n\nIf verify JSON fails to parse: re-download template (uses JSON.stringify)."
      },
      "id": "22222222-2222-2222-2222-222222222209",
      "name": "SETUP — API key",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -320,
        -120
      ]
    },
    {
      "parameters": {
        "content": "## APPROVED — Continue\nSend via email provider / CRM.\nStore receipt_id: {{ $json.receipt_id }}"
      },
      "id": "22222222-2222-2222-2222-222222222206",
      "name": "APPROVED — Continue",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1100,
        -120
      ]
    },
    {
      "parameters": {
        "content": "## REVIEW_REQUIRED — Human approval\nPause bulk/cold outbound.\nReceipt: {{ $json.receipt_id }}"
      },
      "id": "22222222-2222-2222-2222-222222222207",
      "name": "REVIEW_REQUIRED — Human approval",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1100,
        0
      ]
    },
    {
      "parameters": {
        "content": "## REJECTED — Hard stop\nDo not send.\nDo not route to human review.\n{{ $json.receipt_id }}"
      },
      "id": "22222222-2222-2222-2222-222222222208",
      "name": "REJECTED — Hard stop",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1100,
        120
      ]
    },
    {
      "parameters": {
        "content": "## VALIDATION_UNAVAILABLE — Recompute — Not HITL\nAuthorization state indeterminate.\nFresh verify required before continuation.\nHuman approval cannot resume this execution.\nDo not approve, retry, or manually continue on this branch.\nrecompute_required: true\nReceipt: {{ $json.receipt_id }}"
      },
      "id": "22222222-2222-2222-2222-222222222210",
      "name": "VALIDATION_UNAVAILABLE — Recompute — Not HITL",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1100,
        240
      ]
    }
  ],
  "connections": {
    "When outbound send proposed": {
      "main": [
        [
          {
            "node": "Sample outbound payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sample outbound payload": {
      "main": [
        [
          {
            "node": "BiDigest verify",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "BiDigest verify": {
      "main": [
        [
          {
            "node": "Map to agency verdict",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Map to agency verdict": {
      "main": [
        [
          {
            "node": "Branch on verdict",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "bidigest-protected-outbound-agent-v2"
  },
  "settings": {
    "executionOrder": "v1"
  }
}