{
  "name": "VOICE 02 - Retell Post-Call Processor",
  "nodes": [
    {
      "name": "Ingress Contract",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -1160,
        -440
      ],
      "parameters": {
        "content": "## Retell post-call processor\nRetell must send signed webhooks to the included verifier Worker. The Worker verifies the raw-body HMAC and forwards only authentic events with `X-Voice-Ingress-Secret`. This workflow acknowledges immediately, deduplicates call events, masks caller data, and stores operational—not full transcript—data.",
        "height": 340,
        "width": 760,
        "color": 5
      }
    },
    {
      "name": "Verified Retell Post-Call Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -1100,
        0
      ],
      "parameters": {
        "httpMethod": "POST",
        "path": "retell-post-call",
        "responseMode": "onReceived",
        "options": {}
      }
    },
    {
      "name": "Validate Forwarded Event",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -820,
        0
      ],
      "parameters": {
        "jsCode": "const h=$json.headers??{}; const b=$json.body??$json; const supplied=String(h[\"x-voice-ingress-secret\"]??\"\");\nif(supplied!==\"SET_N8N_INGRESS_SECRET\"||String(b.event)!==\"call_analyzed\") return [];\nconst c=b.call??{}; const a=c.call_analysis??{}; const custom=a.custom_analysis_data??a.post_call_analysis_data??{};\nconst digits=String(c.from_number??\"\").replace(/\\D/g,\"\"); const masked=digits?(\"••••\"+digits.slice(-4)):\"\";\nreturn [{json:{event_key:`retell:${c.call_id}:call_analyzed`,call_id:String(c.call_id??\"\"),started_at:c.start_timestamp?new Date(c.start_timestamp).toISOString():\"\",ended_at:c.end_timestamp?new Date(c.end_timestamp).toISOString():\"\",duration_seconds:Math.max(0,Math.round(Number(c.duration_ms??0)/1000)),direction:String(c.direction??\"inbound\"),caller_masked:masked,caller_name:String(custom.caller_name??\"\"),caller_email:String(custom.caller_email??\"\").toLowerCase(),intent:String(custom.intent??\"unknown\").toUpperCase(),outcome:String(custom.outcome??\"UNKNOWN\").toUpperCase(),booking_uid:String(custom.booking_uid??\"\"),appointment_start:String(custom.appointment_start??\"\"),appointment_timezone:String(custom.appointment_timezone??\"\"),transfer_status:String(custom.transfer_status??\"NOT_REQUESTED\").toUpperCase(),consent_recording:false,summary:String(a.call_summary??\"\").slice(0,800),success:custom.success===true||String(custom.success).toLowerCase()===\"true\",needs_follow_up:custom.needs_follow_up===true||String(custom.needs_follow_up).toLowerCase()===\"true\",failure_reason:String(custom.failure_reason??\"\").slice(0,300),created_at:new Date().toISOString()}}];"
      }
    },
    {
      "name": "Read Call Log",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        -560,
        0
      ],
      "parameters": {
        "authentication": "oAuth2",
        "resource": "sheet",
        "operation": "read",
        "documentId": {
          "mode": "id",
          "value": "SET_GOOGLE_SHEET_ID"
        },
        "sheetName": {
          "mode": "name",
          "value": "Call Log"
        },
        "filtersUI": {},
        "combineFilters": "AND",
        "options": {}
      },
      "alwaysOutputData": true
    },
    {
      "name": "Dedupe Call Event",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -300,
        0
      ],
      "parameters": {
        "jsCode": "const item=$(\"Validate Forwarded Event\").first().json; const exists=$input.all().some(x=>String(x.json.event_key)===item.event_key); return exists?[]:[{json:item}];"
      }
    },
    {
      "name": "Append Call Log",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        -40,
        0
      ],
      "parameters": {
        "authentication": "oAuth2",
        "resource": "sheet",
        "operation": "append",
        "documentId": {
          "mode": "id",
          "value": "SET_GOOGLE_SHEET_ID"
        },
        "sheetName": {
          "mode": "name",
          "value": "Call Log"
        },
        "dataMode": "autoMapInputData",
        "options": {
          "handlingExtraData": "ignoreIt"
        }
      }
    },
    {
      "name": "Create Exception When Needed",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        220,
        0
      ],
      "parameters": {
        "jsCode": "const x=$(\"Dedupe Call Event\").item.json; if(x.success&&!x.needs_follow_up&&[\"BOOKED\",\"RESCHEDULE_LINK_SENT\",\"CANCELLED\",\"FAQ_RESOLVED\"].includes(x.outcome)) return [];\nreturn [{json:{exception_id:`EXC-${x.call_id}`,event_key:x.event_key,call_id:x.call_id,priority:[\"TRANSFER_FAILED\",\"PROVIDER_ERROR\"].includes(x.outcome)?\"HIGH\":\"NORMAL\",category:x.outcome||\"FOLLOW_UP\",summary:x.failure_reason||x.summary||\"Owner follow-up requested.\",caller_masked:x.caller_masked,caller_name:x.caller_name,caller_email:x.caller_email,status:\"OPEN\",owner_notified_at:\"\",resolution_notes:\"\",created_at:x.created_at,updated_at:x.created_at}}];"
      }
    },
    {
      "name": "Upsert Exception Queue",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        480,
        0
      ],
      "parameters": {
        "authentication": "oAuth2",
        "resource": "sheet",
        "operation": "appendOrUpdate",
        "documentId": {
          "mode": "id",
          "value": "SET_GOOGLE_SHEET_ID"
        },
        "sheetName": {
          "mode": "name",
          "value": "Exception Queue"
        },
        "dataMode": "autoMapInputData",
        "columnToMatchOn": "exception_id",
        "options": {
          "handlingExtraData": "ignoreIt"
        }
      }
    }
  ],
  "connections": {
    "Verified Retell Post-Call Webhook": {
      "main": [
        [
          {
            "node": "Validate Forwarded Event",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Forwarded Event": {
      "main": [
        [
          {
            "node": "Read Call Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Call Log": {
      "main": [
        [
          {
            "node": "Dedupe Call Event",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dedupe Call Event": {
      "main": [
        [
          {
            "node": "Append Call Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append Call Log": {
      "main": [
        [
          {
            "node": "Create Exception When Needed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Exception When Needed": {
      "main": [
        [
          {
            "node": "Upsert Exception Queue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "timezone": "SET_BUSINESS_TIMEZONE",
    "saveManualExecutions": true,
    "saveExecutionProgress": true,
    "saveDataErrorExecution": "all",
    "saveDataSuccessExecution": "all",
    "executionTimeout": 300
  }
}
