{
  "name": "WAIX · OTP → статус кода",
  "nodes": [
    {
      "id": "124c9ea0-9d8d-46f3-99bf-693c4f01e980",
      "name": "Server webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "parameters": {
        "httpMethod": "POST",
        "path": "waix-otp-status",
        "authentication": "headerAuth",
        "responseMode": "responseNode",
        "options": {}
      },
      "webhookId": "20a2f827-a3ef-4bde-a27d-01fcf2ee3312"
    },
    {
      "id": "1d94a26f-f763-479c-84d0-7f9403be8a24",
      "name": "Prepare request",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        460,
        0
      ],
      "parameters": {
        "jsCode": "const b = $json.body;\nconst invalid = (error, status = 400, code = 'INVALID_INPUT') => [{json:{valid:false,error,status,code}}];\nconst uuid = /^[a-f0-9]{8}-[a-f0-9]{4}-[1-8][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/i;\nif (!b || typeof b !== 'object' || Array.isArray(b)) return invalid('Передайте JSON-объект.');\nconst validId = value => typeof value === 'string' && uuid.test(value);\nif (!validId(b.id)) return invalid('id должен быть UUID запроса OTP.');\nreturn [{json:{valid:true,body:{id:b.id}}}];"
      }
    },
    {
      "id": "dabdb392-bf98-4ce9-8ae8-93551b188223",
      "name": "Valid request",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        700,
        0
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "1ca5b526-2d6f-4624-90a6-54773f46ce29",
              "leftValue": "={{ $json.valid }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        }
      }
    },
    {
      "id": "ba2bc8e8-114e-4410-be6c-3482f5bfe5f7",
      "name": "WAIX API",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        940,
        -100
      ],
      "parameters": {
        "method": "GET",
        "url": "={{ 'https://waix.kz/api/v1/otp/' + $json.body.id }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": []
        },
        "sendBody": false,
        "specifyBody": "json",
        "options": {
          "timeout": 30000,
          "redirect": {
            "redirect": {
              "followRedirects": false
            }
          },
          "response": {
            "response": {
              "fullResponse": true,
              "neverError": true,
              "responseFormat": "text",
              "outputPropertyName": "body"
            }
          }
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "id": "1c8d651a-471b-47e7-9541-55fb3e7a7ab3",
      "name": "Return API result",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        1420,
        -100
      ],
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ $json.body }}",
        "options": {
          "responseCode": "={{ $json.responseCode }}",
          "responseHeaders": {
            "entries": [
              {
                "name": "Cache-Control",
                "value": "no-store"
              },
              {
                "name": "={{ $json.responseHeaders.some(h => h.name === 'X-Request-Id') ? 'X-Request-Id' : 'Cache-Control' }}",
                "value": "={{ $json.responseHeaders.find(h => h.name === 'X-Request-Id')?.value || 'no-store' }}"
              },
              {
                "name": "={{ $json.responseHeaders.some(h => h.name === 'Retry-After') ? 'Retry-After' : 'Cache-Control' }}",
                "value": "={{ $json.responseHeaders.find(h => h.name === 'Retry-After')?.value || 'no-store' }}"
              }
            ]
          }
        }
      }
    },
    {
      "id": "22776d54-32bb-460b-a67c-9210f3769399",
      "name": "Invalid request",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        940,
        150
      ],
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ {error:$json.error,code:$json.code} }}",
        "options": {
          "responseCode": "={{ $json.status }}",
          "responseHeaders": {
            "entries": [
              {
                "name": "Cache-Control",
                "value": "no-store"
              }
            ]
          }
        }
      }
    },
    {
      "id": "02ecf2db-7729-45d3-b42c-c1809b29720c",
      "name": "Setup",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        -320
      ],
      "parameters": {
        "width": 1500,
        "height": 250,
        "content": "## WAIX · OTP → статус кода\n1. Server webhook: Header Auth, X-Workflow-Key = ваш секрет. Только для вызова с backend.\n2. WAIX API: отдельный Header Auth, Authorization = Bearer <ключ WAIX>.\n3. WAIX settings: задайте Connection ID, шаблон и язык; для OTP — срок кода. OTP использует ключ проекта.\n4. event_id сохраняется до первого запроса. При сетевой ошибке — то же тело и тот же UUID.\n5. История и ручные данные отключены. Ответы не содержат sandbox test_code.\n6. 202 — очередь, а не доставка. 400 — проверьте поля, 401/403 — ключ и права, 429 — дождитесь Retry-After.\nИнструкция: https://github.com/ivanpukhov/waix-integrations/blob/main/n8n/otp-status.README.md"
      }
    },
    {
      "id": "1de92e75-2f5e-44d0-93d9-5513b7eff99e",
      "name": "WAIX settings",
      "type": "n8n-nodes-base.set",
      "typeVersion": 2,
      "position": [
        220,
        0
      ],
      "parameters": {
        "keepOnlySet": false,
        "values": {
          "string": [],
          "number": []
        },
        "options": {}
      }
    },
    {
      "id": "2a61cc31-39f0-4893-a26b-1008e378b1ad",
      "name": "Normalize response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1180,
        -100
      ],
      "parameters": {
        "jsCode": "// HTTP Request возвращает текст: даже HTML от прокси станет понятной HTTP-ошибкой.\nconst status = Number($json.statusCode);\nconst headers = $json.headers || {};\nconst requestId = headers['x-request-id'] || headers['X-Request-Id'];\nconst retryAfter = headers['retry-after'] || headers['Retry-After'];\nconst meta = { request_id: typeof requestId === 'string' ? requestId : null };\nlet body = $json.body;\ntry { if (typeof body === 'string') body = JSON.parse(body); } catch { body = null; }\nconst object = body && typeof body === 'object' && !Array.isArray(body);\nlet responseCode = Number.isInteger(status) && status >= 400 && status <= 599 ? status : 502;\nif (status >= 200 && status < 300 && object) responseCode = status;\nif (!object) body = {error: 'WAIX вернул ответ, который не удалось прочитать.', code: 'INVALID_RESPONSE', ...meta};\nif (status >= 300 && status < 400) body = {error: 'WAIX вернул перенаправление. Проверьте адрес API.', code: 'REDIRECT_DISALLOWED', ...meta};\nif (body.data && typeof body.data === 'object' && !Array.isArray(body.data)) {\n  const {test_code, ...safe} = body.data;\n  body = {...body, data: safe};\n}\nconst responseHeaders = [{name: 'Cache-Control', value: 'no-store'}];\nif (typeof requestId === 'string' && /^[A-Za-z0-9_.:-]{1,160}$/.test(requestId)) responseHeaders.push({name:'X-Request-Id',value:requestId});\nif (typeof retryAfter === 'string' && (/^[0-9]{1,8}$/.test(retryAfter) || /^[A-Za-z]{3}, [0-9]{2} [A-Za-z]{3} [0-9]{4} [0-9:]{8} GMT$/.test(retryAfter))) responseHeaders.push({name:'Retry-After',value:retryAfter});\nreturn [{json:{body,responseCode,responseHeaders}}];"
      }
    },
    {
      "id": "58eb2ee9-0ef0-4edb-b491-40d00d01c330",
      "name": "Transport error",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        1180,
        160
      ],
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ {error:'Не удалось получить ответ WAIX. Для отправки результат неизвестен; повторите прежнее тело с тем же event_id. Не создавайте новый UUID.',code:'UPSTREAM_UNAVAILABLE',outcome_unknown:false} }}",
        "options": {
          "responseCode": 502,
          "responseHeaders": {
            "entries": [
              {
                "name": "Cache-Control",
                "value": "no-store"
              }
            ]
          }
        }
      }
    }
  ],
  "connections": {
    "Server webhook": {
      "main": [
        [
          {
            "node": "WAIX settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare request": {
      "main": [
        [
          {
            "node": "Valid request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Valid request": {
      "main": [
        [
          {
            "node": "WAIX API",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Invalid request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "WAIX API": {
      "main": [
        [
          {
            "node": "Normalize response",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Transport error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "WAIX settings": {
      "main": [
        [
          {
            "node": "Prepare request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize response": {
      "main": [
        [
          {
            "node": "Return API result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "saveDataErrorExecution": "none",
    "saveDataSuccessExecution": "none",
    "saveExecutionProgress": false,
    "saveManualExecutions": false
  },
  "pinData": {},
  "tags": []
}
