{
  "id": "patchwellWebhookReceiver",
  "name": "Patchwell: receive results by webhook (signature checked)",
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "nodes": [
    {
      "id": "r1",
      "name": "Patchwell webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "webhookId": "patchwell-results",
      "parameters": {
        "httpMethod": "POST",
        "path": "patchwell-results",
        "responseMode": "onReceived",
        "options": {
          "rawBody": true
        }
      },
      "notes": "Use this node's Production URL as callback_url in your Patchwell requests (or in a saved preset)."
    },
    {
      "id": "r2",
      "name": "Verify signature",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        240,
        0
      ],
      "parameters": {
        "jsCode": "// Checks Patchwell's signature on the exact bytes received. Put your secret below:\n// GET https://patchwell.io/v1/account/webhook-secret  (self-hosted n8n: set NODE_FUNCTION_ALLOW_BUILTIN=crypto)\nconst crypto = require('crypto');\nconst SECRET = 'whsec_PASTE_YOUR_SECRET_HERE';\nconst items = $input.all();\nconst out = [];\nfor (let i = 0; i < items.length; i++) {\n  const raw = await this.helpers.getBinaryDataBuffer(i, 'data');\n  const h = items[i].json.headers || {};\n  const parts = Object.fromEntries(String(h['patchwell-signature'] || '').split(',').map((p) => p.split('=')));\n  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;\n  const good = crypto.createHmac('sha256', SECRET).update(String(parts.t) + '.').update(raw).digest('hex');\n  const valid = Boolean(fresh && parts.v1 && good.length === parts.v1.length &&\n    crypto.timingSafeEqual(Buffer.from(good), Buffer.from(parts.v1)));\n  let event = {};\n  try { event = JSON.parse(raw.toString('utf8')); } catch (e) {}\n  out.push({ json: { valid, delivery_id: h['patchwell-delivery'] || null, event: event.event || null, result: event.result || null } });\n}\nreturn out;"
      }
    },
    {
      "id": "r3",
      "name": "Genuine?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        480,
        0
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "g1",
              "leftValue": "={{ $json.valid && $json.event === 'transcribe.completed' }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    },
    {
      "id": "r4",
      "name": "Notes (Markdown)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        720,
        -100
      ],
      "parameters": {
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "method": "POST",
        "url": "https://patchwell.io/v1/export?format=md",
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify($json.result) }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "text",
              "outputPropertyName": "notes"
            }
          }
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "patchwellApi",
          "name": "Patchwell API"
        }
      }
    },
    {
      "id": "r5",
      "name": "Ignored (bad signature or other event)",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        720,
        100
      ],
      "parameters": {}
    }
  ],
  "connections": {
    "Patchwell webhook": {
      "main": [
        [
          {
            "node": "Verify signature",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Verify signature": {
      "main": [
        [
          {
            "node": "Genuine?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Genuine?": {
      "main": [
        [
          {
            "node": "Notes (Markdown)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Ignored (bad signature or other event)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}