{
  "name": "LiveTP staging — Roteiro pronto",
  "nodes": [
    {
      "id": "ba70046f-85d5-4035-a823-d34edeb17b5b",
      "name": "LiveTP webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        0,
        0
      ],
      "parameters": {
        "httpMethod": "POST",
        "path": "livetp-staging",
        "authentication": "headerAuth",
        "responseMode": "responseNode",
        "options": {
          "rawBody": true
        }
      },
      "webhookId": "2fb69ffd-0d5f-4575-a06c-13b6bc51322e"
    },
    {
      "id": "9bec32aa-e79a-4a26-ad3e-6a0f8a614add",
      "name": "Preservar corpo bruto",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        240,
        0
      ],
      "parameters": {
        "jsCode": "const item = $input.first();\nconst raw = await this.helpers.getBinaryDataBuffer(0, 'data');\nconst h = item.json.headers;\nreturn [{json:{rawBody:raw.toString('utf8'),id:h['webhook-id'],timestamp:h['webhook-timestamp'],signature:h['webhook-signature']}}];"
      }
    },
    {
      "id": "fef75128-dddc-4c38-8a7d-c198e73c6137",
      "name": "Verificar assinatura LiveTP",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        480,
        0
      ],
      "parameters": {
        "method": "POST",
        "url": "https://staging.livetp.app/api/webhooks/verify/CONFIGURE_ENDPOINT_ID",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json) }}",
        "options": {
          "timeout": 8000,
          "response": {
            "response": {
              "fullResponse": true,
              "neverError": true,
              "responseFormat": "json"
            }
          }
        }
      }
    },
    {
      "id": "935f6a15-46f3-409c-8905-0e49408f655c",
      "name": "Assinatura válida",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        720,
        0
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "80ab82c1-c06c-41c2-86cf-9faa27566432",
              "leftValue": "={{ $json.body?.valid === true }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    },
    {
      "id": "2ffdbd47-0075-426c-90f7-03a1375c8ddc",
      "name": "É teste de conexão",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        960,
        -80
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "b172c84d-bc31-40c0-abcb-3aa293460b84",
              "leftValue": "={{ $json.body.event.type }}",
              "rightValue": "webhook.test",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    },
    {
      "id": "5ae4f1df-02b6-4d4a-89d5-ee215bab3986",
      "name": "Confirmar challenge",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        1200,
        -200
      ],
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({challenge:$json.body.event.data.challenge}) }}",
        "options": {
          "responseCode": 200
        }
      }
    },
    {
      "id": "c7c0fff6-01fa-44ed-84f2-899bdcba9e47",
      "name": "Persistir evento sem duplicar",
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1,
      "position": [
        1200,
        40
      ],
      "parameters": {
        "resource": "row",
        "operation": "upsert",
        "dataTableId": {
          "__rl": true,
          "value": "CONFIGURE_DATA_TABLE_ID",
          "mode": "id"
        },
        "matchType": "allConditions",
        "filters": {
          "conditions": [
            {
              "keyName": "event_id",
              "condition": "eq",
              "keyValue": "={{ $json.body.event.id }}"
            }
          ]
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "event_id": "={{ $json.body.event.id }}",
            "environment": "={{ $json.body.event.environment }}",
            "event_type": "={{ $json.body.event.type }}",
            "script_id": "={{ $json.body.event.data.script?.id ?? $json.body.event.data.video?.scriptId ?? \"\" }}",
            "revision": "={{ $json.body.event.data.script?.revision ?? 0 }}",
            "video_id": "={{ $json.body.event.data.video?.id ?? \"\" }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "event_id",
              "displayName": "event_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "environment",
              "displayName": "environment",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "event_type",
              "displayName": "event_type",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "script_id",
              "displayName": "script_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "revision",
              "displayName": "revision",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "video_id",
              "displayName": "video_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "2454ca00-1a64-407d-b9d7-0aaede591638",
      "name": "Confirmar recebimento",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        1440,
        40
      ],
      "parameters": {
        "respondWith": "json",
        "responseBody": "{\"received\":true}",
        "options": {
          "responseCode": 202
        }
      }
    },
    {
      "id": "e209975e-7ac1-4cb0-a82a-d7c0e100f2f6",
      "name": "Recusar evento",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        960,
        200
      ],
      "parameters": {
        "respondWith": "json",
        "responseBody": "{\"error\":\"verification_failed\"}",
        "options": {
          "responseCode": "={{ $json.statusCode === 401 ? 401 : 503 }}"
        }
      }
    },
    {
      "id": "26343d9c-79a1-42aa-a0ee-07d1ca191fb4",
      "name": "Como configurar",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        -500
      ],
      "parameters": {
        "content": "## LiveTP staging\n1. Crie uma credencial Header Auth com Authorization = Bearer TOKEN_DO_RECEPTOR. Use-a no Webhook e no HTTP Request.\n2. Cadastre a Production URL no LiveTP, com o mesmo token no campo Bearer.\n3. Substitua CONFIGURE_ENDPOINT_ID pelo ID do destino LiveTP.\n4. Crie uma Data Table com event_id, environment, event_type, script_id, video_id (texto) e revision (número). Selecione-a no nó de persistência.\n5. Publique, envie teste e ative no LiveTP.\nAssinatura é verificada antes de persistir. Upsert mantém uma linha por ID nas entregas sequenciais do remetente. Ao anexar efeitos externos, use event.id como chave de idempotência no serviço de destino.",
        "height": 420,
        "width": 600
      }
    }
  ],
  "connections": {
    "LiveTP webhook": {
      "main": [
        [
          {
            "node": "Preservar corpo bruto",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Preservar corpo bruto": {
      "main": [
        [
          {
            "node": "Verificar assinatura LiveTP",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Verificar assinatura LiveTP": {
      "main": [
        [
          {
            "node": "Assinatura válida",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Assinatura válida": {
      "main": [
        [
          {
            "node": "É teste de conexão",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Recusar evento",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "É teste de conexão": {
      "main": [
        [
          {
            "node": "Confirmar challenge",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Persistir evento sem duplicar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Persistir evento sem duplicar": {
      "main": [
        [
          {
            "node": "Confirmar recebimento",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "saveDataErrorExecution": "none",
    "saveDataSuccessExecution": "none",
    "saveManualExecutions": false,
    "saveExecutionProgress": false
  },
  "active": false,
  "pinData": {}
}
