{
  "name": "KDEFY SMS API",
  "version": "1.0",
  "baseUrl": "https://kdefy-sms.com/api/v1",
  "summary": "API REST para comprar números virtuais e receber SMS. Usa o mesmo saldo e as mesmas ativações da sua conta no site.",
  "auth": {
    "text": "Gere o token em API, no menu do site, e mande no header X-API-Key em toda requisição. Authorization: Bearer e ?api_key= também funcionam, mas a query string vai parar em logs de proxy e no histórico do navegador. Gerar outro token invalida o anterior na hora. Não use o token em front-end.",
    "examples": [
      "curl \"https://kdefy-sms.com/api/v1/balance\" -H \"X-API-Key: SEU_TOKEN\"",
      "curl \"https://kdefy-sms.com/api/v1/balance\" -H \"Authorization: Bearer SEU_TOKEN\"",
      "curl \"https://kdefy-sms.com/api/v1/balance?api_key=SEU_TOKEN\""
    ]
  },
  "conventions": [
    "Toda resposta é JSON. Sucesso: { \"status\": \"success\", ... }. Erro: { \"status\": \"error\", \"error\": \"codigo\", \"message\": \"texto\" }.",
    "Decida pelo campo error. Ele é estável. message é texto para humano e pode mudar.",
    "message e state_label seguem o idioma da conta (pt ou en). Para forçar: ?lang=pt ou ?lang=en.",
    "As chaves do JSON e os valores de state não mudam com o idioma.",
    "Valores em BRL com ponto decimal. Datas em ISO 8601 (UTC)."
  ],
  "limits": [
    "120 requisições por minuto por conta.",
    "Compra: até 50 números por requisição e 50 por minuto. Acima disso: limite_de_numeros.",
    "Status: consulte a cada 5 s. Mais rápido não antecipa o código."
  ],
  "flow": [
    "GET /services?country=73: serviços do país e preço inicial.",
    "GET /offers?country=73&service=wa (opcional): faixas de preço e estoque.",
    "GET /number/get?country=73&service=wa&request_id=...: compra e devolve phone e id.",
    "GET /number/status?id=ID a cada 5 s, até code vir preenchido.",
    "Com código: GET /number/finish?id=ID. Sem código: GET /number/cancel?id=ID (libera 2 min após a compra) e o valor volta ao saldo."
  ],
  "states": [
    [
      "waiting",
      "Esperando o primeiro código."
    ],
    [
      "code_received",
      "Código recebido. Códigos novos entram em codes enquanto a ativação estiver aberta."
    ],
    [
      "completed",
      "Finalizada por você (/number/finish)."
    ],
    [
      "cancelled",
      "Cancelada. Valor devolvido."
    ],
    [
      "expired",
      "Sem código em 20 min. Valor devolvido automaticamente."
    ]
  ],
  "errors": [
    [
      401,
      "token_nao_informado / token_invalido",
      "Token ausente, inválido ou revogado.",
      "Corrija o token. Não repita."
    ],
    [
      403,
      "conta_bloqueada / token_suspenso",
      "Conta banida ou API suspensa pelo admin.",
      "Fale com o suporte."
    ],
    [
      400,
      "pais_invalido / servico_invalido / id_invalido / quantidade_invalida / preco_invalido / operador_invalido / request_id_invalido",
      "Parâmetro ausente ou inválido.",
      "Corrija a chamada."
    ],
    [
      402,
      "saldo_insuficiente",
      "Saldo menor que o preço.",
      "Recarregue no site."
    ],
    [
      404,
      "sem_numeros_disponiveis / oferta_indisponivel",
      "Sem estoque nessa faixa agora.",
      "Suba max_price_brl, troque o país ou tente depois."
    ],
    [
      404,
      "ativacao_nao_encontrada",
      "ID inexistente ou de outra conta.",
      "Confira o id."
    ],
    [
      409,
      "requisicao_em_andamento",
      "A request_id ainda está sendo processada.",
      "Espere e repita a mesma chamada."
    ],
    [
      409,
      "cancelamento_muito_cedo",
      "Menos de 2 min desde a compra.",
      "Espere retry_in_seconds."
    ],
    [
      409,
      "ativacao_nao_cancelavel / ativacao_nao_finalizavel",
      "O estado atual não permite a ação.",
      "Consulte /number/status."
    ],
    [
      422,
      "request_id_reutilizada",
      "Mesma request_id com parâmetros diferentes.",
      "Use uma request_id nova."
    ],
    [
      422,
      "preco_abaixo_do_minimo",
      "max_price_brl menor que a oferta mais barata.",
      "Suba para min_price_brl ou mais."
    ],
    [
      429,
      "limite_de_requisicoes / limite_de_numeros",
      "Passou do limite.",
      "Espere retry_in_seconds."
    ],
    [
      502,
      "falha_temporaria",
      "Instabilidade. Você não é cobrado.",
      "Repita com espera crescente, mesma request_id."
    ]
  ],
  "groups": [
    "Conta",
    "Catálogo",
    "Números",
    "Histórico"
  ],
  "endpoints": [
    {
      "id": "balance",
      "group": "Conta",
      "title": "Saldo",
      "methods": [
        "GET"
      ],
      "path": "/balance",
      "summary": "Saldo e totais da conta.",
      "params": [],
      "response": "{\n  \"status\": \"success\",\n  \"balance_brl\": 25.4,\n  \"total_spent_brl\": 12.1,\n  \"total_recharged_brl\": 37.5,\n  \"currency\": \"BRL\"\n}",
      "fields": [
        [
          "balance_brl",
          "number",
          "Saldo disponível."
        ],
        [
          "total_spent_brl",
          "number",
          "Total gasto na conta."
        ],
        [
          "total_recharged_brl",
          "number",
          "Total recarregado."
        ],
        [
          "currency",
          "string",
          "Sempre \"BRL\"."
        ]
      ],
      "errorCodes": [],
      "samples": {
        "curl": "curl \"https://kdefy-sms.com/api/v1/balance\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\"",
        "js": "const res = await fetch(\"https://kdefy-sms.com/api/v1/balance\", {\n  headers: { \"X-API-Key\": process.env.KDEFY_API_KEY },\n});\nconst data = await res.json();\nif (data.status !== \"success\") throw new Error(data.error);\nconsole.log(data);",
        "python": "import os, requests\n\nr = requests.get(\n    \"https://kdefy-sms.com/api/v1/balance\",\n    headers={\"X-API-Key\": os.environ[\"KDEFY_API_KEY\"]},\n    timeout=20,\n)\ndata = r.json()\nif data[\"status\"] != \"success\":\n    raise RuntimeError(data[\"error\"])\nprint(data)",
        "php": "<?php\n$ch = curl_init(\"https://kdefy-sms.com/api/v1/balance\");\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 20,\n    CURLOPT_HTTPHEADER => [\"X-API-Key: \" . getenv(\"KDEFY_API_KEY\")],\n]);\n$data = json_decode(curl_exec($ch), true);\nif (($data[\"status\"] ?? null) !== \"success\") {\n    throw new RuntimeException($data[\"error\"] ?? \"erro_desconhecido\");\n}\nprint_r($data);",
        "ruby": "require \"net/http\"\nrequire \"json\"\n\nuri = URI(\"https://kdefy-sms.com/api/v1/balance\")\nreq = Net::HTTP::Get.new(uri, \"X-API-Key\" => ENV.fetch(\"KDEFY_API_KEY\"))\nres = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", read_timeout: 20) { |http| http.request(req) }\ndata = JSON.parse(res.body)\nabort(data[\"error\"]) unless data[\"status\"] == \"success\"\nputs JSON.pretty_generate(data)",
        "shell": "#!/usr/bin/env bash\ncurl -sS --max-time 20 -G \"https://kdefy-sms.com/api/v1/balance\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\" | jq ."
      }
    },
    {
      "id": "profile",
      "group": "Conta",
      "title": "Perfil",
      "methods": [
        "GET"
      ],
      "path": "/profile",
      "summary": "Usuário, saldo e data de criação.",
      "params": [],
      "response": "{\n  \"status\": \"success\",\n  \"username\": \"maria\",\n  \"balance_brl\": 25.4,\n  \"created_at\": \"2026-09-01T12:00:00.000Z\"\n}",
      "fields": [
        [
          "username",
          "string",
          "Usuário."
        ],
        [
          "balance_brl",
          "number",
          "Saldo disponível."
        ],
        [
          "created_at",
          "string",
          "Criação da conta (ISO 8601)."
        ]
      ],
      "errorCodes": [],
      "samples": {
        "curl": "curl \"https://kdefy-sms.com/api/v1/profile\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\"",
        "js": "const res = await fetch(\"https://kdefy-sms.com/api/v1/profile\", {\n  headers: { \"X-API-Key\": process.env.KDEFY_API_KEY },\n});\nconst data = await res.json();\nif (data.status !== \"success\") throw new Error(data.error);\nconsole.log(data);",
        "python": "import os, requests\n\nr = requests.get(\n    \"https://kdefy-sms.com/api/v1/profile\",\n    headers={\"X-API-Key\": os.environ[\"KDEFY_API_KEY\"]},\n    timeout=20,\n)\ndata = r.json()\nif data[\"status\"] != \"success\":\n    raise RuntimeError(data[\"error\"])\nprint(data)",
        "php": "<?php\n$ch = curl_init(\"https://kdefy-sms.com/api/v1/profile\");\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 20,\n    CURLOPT_HTTPHEADER => [\"X-API-Key: \" . getenv(\"KDEFY_API_KEY\")],\n]);\n$data = json_decode(curl_exec($ch), true);\nif (($data[\"status\"] ?? null) !== \"success\") {\n    throw new RuntimeException($data[\"error\"] ?? \"erro_desconhecido\");\n}\nprint_r($data);",
        "ruby": "require \"net/http\"\nrequire \"json\"\n\nuri = URI(\"https://kdefy-sms.com/api/v1/profile\")\nreq = Net::HTTP::Get.new(uri, \"X-API-Key\" => ENV.fetch(\"KDEFY_API_KEY\"))\nres = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", read_timeout: 20) { |http| http.request(req) }\ndata = JSON.parse(res.body)\nabort(data[\"error\"]) unless data[\"status\"] == \"success\"\nputs JSON.pretty_generate(data)",
        "shell": "#!/usr/bin/env bash\ncurl -sS --max-time 20 -G \"https://kdefy-sms.com/api/v1/profile\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\" | jq ."
      }
    },
    {
      "id": "countries",
      "group": "Catálogo",
      "title": "Países",
      "methods": [
        "GET"
      ],
      "path": "/countries",
      "summary": "Lista de países. Use o id em country.",
      "params": [],
      "response": "{\n  \"status\": \"success\",\n  \"countries\": [\n    {\n      \"id\": 73,\n      \"name\": \"Brasil\",\n      \"dial_code\": \"+55\"\n    }\n  ]\n}",
      "fields": [
        [
          "countries[].id",
          "integer",
          "ID do país. Use em country."
        ],
        [
          "countries[].name",
          "string",
          "Nome."
        ],
        [
          "countries[].dial_code",
          "string | null",
          "DDI, ex.: +55."
        ]
      ],
      "errorCodes": [],
      "samples": {
        "curl": "curl \"https://kdefy-sms.com/api/v1/countries\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\"",
        "js": "const res = await fetch(\"https://kdefy-sms.com/api/v1/countries\", {\n  headers: { \"X-API-Key\": process.env.KDEFY_API_KEY },\n});\nconst data = await res.json();\nif (data.status !== \"success\") throw new Error(data.error);\nconsole.log(data);",
        "python": "import os, requests\n\nr = requests.get(\n    \"https://kdefy-sms.com/api/v1/countries\",\n    headers={\"X-API-Key\": os.environ[\"KDEFY_API_KEY\"]},\n    timeout=20,\n)\ndata = r.json()\nif data[\"status\"] != \"success\":\n    raise RuntimeError(data[\"error\"])\nprint(data)",
        "php": "<?php\n$ch = curl_init(\"https://kdefy-sms.com/api/v1/countries\");\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 20,\n    CURLOPT_HTTPHEADER => [\"X-API-Key: \" . getenv(\"KDEFY_API_KEY\")],\n]);\n$data = json_decode(curl_exec($ch), true);\nif (($data[\"status\"] ?? null) !== \"success\") {\n    throw new RuntimeException($data[\"error\"] ?? \"erro_desconhecido\");\n}\nprint_r($data);",
        "ruby": "require \"net/http\"\nrequire \"json\"\n\nuri = URI(\"https://kdefy-sms.com/api/v1/countries\")\nreq = Net::HTTP::Get.new(uri, \"X-API-Key\" => ENV.fetch(\"KDEFY_API_KEY\"))\nres = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", read_timeout: 20) { |http| http.request(req) }\ndata = JSON.parse(res.body)\nabort(data[\"error\"]) unless data[\"status\"] == \"success\"\nputs JSON.pretty_generate(data)",
        "shell": "#!/usr/bin/env bash\ncurl -sS --max-time 20 -G \"https://kdefy-sms.com/api/v1/countries\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\" | jq ."
      }
    },
    {
      "id": "services",
      "group": "Catálogo",
      "title": "Serviços",
      "methods": [
        "GET"
      ],
      "path": "/services",
      "summary": "Serviços de um país, com estoque e preço inicial. Use code em service.",
      "params": [
        {
          "name": "country",
          "required": true,
          "description": "ID do país."
        }
      ],
      "response": "{\n  \"status\": \"success\",\n  \"country\": 73,\n  \"services\": [\n    {\n      \"code\": \"wa\",\n      \"name\": \"WhatsApp\",\n      \"available\": 1240,\n      \"from_price_brl\": 7.1\n    }\n  ]\n}",
      "fields": [
        [
          "country",
          "integer",
          "País consultado."
        ],
        [
          "services[].code",
          "string",
          "Código. Use em service."
        ],
        [
          "services[].name",
          "string",
          "Nome."
        ],
        [
          "services[].available",
          "integer",
          "Estoque."
        ],
        [
          "services[].from_price_brl",
          "number | null",
          "Preço da oferta mais barata."
        ]
      ],
      "errorCodes": [
        "pais_invalido"
      ],
      "samples": {
        "curl": "curl \"https://kdefy-sms.com/api/v1/services?country=73\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\"",
        "js": "const res = await fetch(\"https://kdefy-sms.com/api/v1/services?country=73\", {\n  headers: { \"X-API-Key\": process.env.KDEFY_API_KEY },\n});\nconst data = await res.json();\nif (data.status !== \"success\") throw new Error(data.error);\nconsole.log(data);",
        "python": "import os, requests\n\nr = requests.get(\n    \"https://kdefy-sms.com/api/v1/services\",\n    params={\"country\": 73},\n    headers={\"X-API-Key\": os.environ[\"KDEFY_API_KEY\"]},\n    timeout=20,\n)\ndata = r.json()\nif data[\"status\"] != \"success\":\n    raise RuntimeError(data[\"error\"])\nprint(data)",
        "php": "<?php\n$ch = curl_init(\"https://kdefy-sms.com/api/v1/services?country=73\");\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 20,\n    CURLOPT_HTTPHEADER => [\"X-API-Key: \" . getenv(\"KDEFY_API_KEY\")],\n]);\n$data = json_decode(curl_exec($ch), true);\nif (($data[\"status\"] ?? null) !== \"success\") {\n    throw new RuntimeException($data[\"error\"] ?? \"erro_desconhecido\");\n}\nprint_r($data);",
        "ruby": "require \"net/http\"\nrequire \"json\"\n\nuri = URI(\"https://kdefy-sms.com/api/v1/services?country=73\")\nreq = Net::HTTP::Get.new(uri, \"X-API-Key\" => ENV.fetch(\"KDEFY_API_KEY\"))\nres = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", read_timeout: 20) { |http| http.request(req) }\ndata = JSON.parse(res.body)\nabort(data[\"error\"]) unless data[\"status\"] == \"success\"\nputs JSON.pretty_generate(data)",
        "shell": "#!/usr/bin/env bash\ncurl -sS --max-time 20 -G \"https://kdefy-sms.com/api/v1/services\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\" \\\n  --data-urlencode \"country=73\" | jq ."
      }
    },
    {
      "id": "operators",
      "group": "Catálogo",
      "title": "Operadoras",
      "methods": [
        "GET"
      ],
      "path": "/operators",
      "summary": "Operadoras de um país. Uso opcional em operator na compra.",
      "params": [
        {
          "name": "country",
          "required": true,
          "description": "ID do país."
        }
      ],
      "response": "{\n  \"status\": \"success\",\n  \"country\": 73,\n  \"operators\": [\n    {\n      \"id\": \"vivo\",\n      \"name\": \"vivo\"\n    }\n  ]\n}",
      "fields": [
        [
          "country",
          "integer",
          "País consultado."
        ],
        [
          "operators[].id",
          "string",
          "ID da operadora. Use em operator."
        ],
        [
          "operators[].name",
          "string",
          "Nome."
        ]
      ],
      "errorCodes": [
        "pais_invalido"
      ],
      "samples": {
        "curl": "curl \"https://kdefy-sms.com/api/v1/operators?country=73\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\"",
        "js": "const res = await fetch(\"https://kdefy-sms.com/api/v1/operators?country=73\", {\n  headers: { \"X-API-Key\": process.env.KDEFY_API_KEY },\n});\nconst data = await res.json();\nif (data.status !== \"success\") throw new Error(data.error);\nconsole.log(data);",
        "python": "import os, requests\n\nr = requests.get(\n    \"https://kdefy-sms.com/api/v1/operators\",\n    params={\"country\": 73},\n    headers={\"X-API-Key\": os.environ[\"KDEFY_API_KEY\"]},\n    timeout=20,\n)\ndata = r.json()\nif data[\"status\"] != \"success\":\n    raise RuntimeError(data[\"error\"])\nprint(data)",
        "php": "<?php\n$ch = curl_init(\"https://kdefy-sms.com/api/v1/operators?country=73\");\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 20,\n    CURLOPT_HTTPHEADER => [\"X-API-Key: \" . getenv(\"KDEFY_API_KEY\")],\n]);\n$data = json_decode(curl_exec($ch), true);\nif (($data[\"status\"] ?? null) !== \"success\") {\n    throw new RuntimeException($data[\"error\"] ?? \"erro_desconhecido\");\n}\nprint_r($data);",
        "ruby": "require \"net/http\"\nrequire \"json\"\n\nuri = URI(\"https://kdefy-sms.com/api/v1/operators?country=73\")\nreq = Net::HTTP::Get.new(uri, \"X-API-Key\" => ENV.fetch(\"KDEFY_API_KEY\"))\nres = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", read_timeout: 20) { |http| http.request(req) }\ndata = JSON.parse(res.body)\nabort(data[\"error\"]) unless data[\"status\"] == \"success\"\nputs JSON.pretty_generate(data)",
        "shell": "#!/usr/bin/env bash\ncurl -sS --max-time 20 -G \"https://kdefy-sms.com/api/v1/operators\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\" \\\n  --data-urlencode \"country=73\" | jq ."
      }
    },
    {
      "id": "offers",
      "group": "Catálogo",
      "title": "Ofertas",
      "methods": [
        "GET"
      ],
      "path": "/offers",
      "summary": "Faixas de preço e estoque de um serviço num país. Faixas baratas têm pouco estoque; as mais caras tendem a ter número.",
      "params": [
        {
          "name": "country",
          "required": true,
          "description": "ID do país."
        },
        {
          "name": "service",
          "required": true,
          "description": "Código do serviço."
        }
      ],
      "response": "{\n  \"status\": \"success\",\n  \"country\": 73,\n  \"service\": \"wa\",\n  \"offers\": [\n    {\n      \"price_brl\": 7.1,\n      \"available\": 12\n    },\n    {\n      \"price_brl\": 8.4,\n      \"available\": 340\n    }\n  ]\n}",
      "fields": [
        [
          "offers[].price_brl",
          "number",
          "Preço da faixa."
        ],
        [
          "offers[].available",
          "integer",
          "Estoque da faixa."
        ]
      ],
      "errorCodes": [
        "pais_invalido",
        "servico_invalido",
        "sem_numeros_disponiveis"
      ],
      "samples": {
        "curl": "curl \"https://kdefy-sms.com/api/v1/offers?country=73&service=wa\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\"",
        "js": "const res = await fetch(\"https://kdefy-sms.com/api/v1/offers?country=73&service=wa\", {\n  headers: { \"X-API-Key\": process.env.KDEFY_API_KEY },\n});\nconst data = await res.json();\nif (data.status !== \"success\") throw new Error(data.error);\nconsole.log(data);",
        "python": "import os, requests\n\nr = requests.get(\n    \"https://kdefy-sms.com/api/v1/offers\",\n    params={\"country\": 73, \"service\": \"wa\"},\n    headers={\"X-API-Key\": os.environ[\"KDEFY_API_KEY\"]},\n    timeout=20,\n)\ndata = r.json()\nif data[\"status\"] != \"success\":\n    raise RuntimeError(data[\"error\"])\nprint(data)",
        "php": "<?php\n$ch = curl_init(\"https://kdefy-sms.com/api/v1/offers?country=73&service=wa\");\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 20,\n    CURLOPT_HTTPHEADER => [\"X-API-Key: \" . getenv(\"KDEFY_API_KEY\")],\n]);\n$data = json_decode(curl_exec($ch), true);\nif (($data[\"status\"] ?? null) !== \"success\") {\n    throw new RuntimeException($data[\"error\"] ?? \"erro_desconhecido\");\n}\nprint_r($data);",
        "ruby": "require \"net/http\"\nrequire \"json\"\n\nuri = URI(\"https://kdefy-sms.com/api/v1/offers?country=73&service=wa\")\nreq = Net::HTTP::Get.new(uri, \"X-API-Key\" => ENV.fetch(\"KDEFY_API_KEY\"))\nres = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", read_timeout: 20) { |http| http.request(req) }\ndata = JSON.parse(res.body)\nabort(data[\"error\"]) unless data[\"status\"] == \"success\"\nputs JSON.pretty_generate(data)",
        "shell": "#!/usr/bin/env bash\ncurl -sS --max-time 20 -G \"https://kdefy-sms.com/api/v1/offers\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\" \\\n  --data-urlencode \"country=73\" \\\n  --data-urlencode \"service=wa\" | jq ."
      }
    },
    {
      "id": "get",
      "group": "Números",
      "title": "Comprar número",
      "methods": [
        "GET",
        "POST"
      ],
      "path": "/number/get",
      "summary": "Compra um número, ou vários com quantity. Debita na hora. Se o número cancelar ou expirar sem código, o valor volta ao saldo.",
      "danger": "Isto COMPRA um número de verdade e debita do seu saldo. Continuar?",
      "params": [
        {
          "name": "country",
          "required": true,
          "description": "ID do país."
        },
        {
          "name": "service",
          "required": true,
          "description": "Código do serviço."
        },
        {
          "name": "max_price_brl",
          "required": false,
          "description": "Teto de preço. Usa a faixa mais cara que couber nele. Sem isso, usa a mais barata."
        },
        {
          "name": "operator",
          "required": false,
          "description": "Operadora. Padrão: qualquer. Restringe o estoque."
        },
        {
          "name": "quantity",
          "required": false,
          "description": "Quantidade, de 1 a 50. Sem isso compra 1 e a resposta vem no formato simples."
        },
        {
          "name": "request_id",
          "required": false,
          "description": "Chave anti-duplicação, 8 a 80 caracteres. O header Idempotency-Key também vale. Recomendada."
        }
      ],
      "response": "{\n  \"id\": \"1842\",\n  \"service\": \"wa\",\n  \"service_name\": \"WhatsApp\",\n  \"country\": 73,\n  \"country_name\": \"Brasil\",\n  \"phone\": \"5511999999999\",\n  \"state\": \"waiting\",\n  \"state_label\": \"Aguardando código\",\n  \"code\": null,\n  \"codes\": [],\n  \"price_brl\": 8.4,\n  \"seconds_left\": 1200,\n  \"cancel_in_seconds\": 120,\n  \"status\": \"success\",\n  \"balance_brl\": 17\n}",
      "notes": [
        "Lote e repetição segura: veja Compra em lote e Idempotência em Guias."
      ],
      "extraResponses": [
        {
          "label": "Resposta com quantity=2",
          "body": "{\n  \"status\": \"success\",\n  \"requested\": 2,\n  \"purchased\": 2,\n  \"failed\": 0,\n  \"numbers\": [\n    {\n      \"id\": \"1842\",\n      \"service\": \"wa\",\n      \"service_name\": \"WhatsApp\",\n      \"country\": 73,\n      \"country_name\": \"Brasil\",\n      \"phone\": \"5511999999999\",\n      \"state\": \"waiting\",\n      \"state_label\": \"Aguardando código\",\n      \"code\": null,\n      \"codes\": [],\n      \"price_brl\": 8.4,\n      \"seconds_left\": 1200,\n      \"cancel_in_seconds\": 120\n    },\n    {\n      \"id\": \"1843\",\n      \"service\": \"wa\",\n      \"service_name\": \"WhatsApp\",\n      \"country\": 73,\n      \"country_name\": \"Brasil\",\n      \"phone\": \"5511988887777\",\n      \"state\": \"waiting\",\n      \"state_label\": \"Aguardando código\",\n      \"code\": null,\n      \"codes\": [],\n      \"price_brl\": 8.4,\n      \"seconds_left\": 1200,\n      \"cancel_in_seconds\": 120\n    }\n  ],\n  \"balance_brl\": 8.6\n}"
        }
      ],
      "fields": [
        [
          "id",
          "string",
          "ID da ativação. Vai em /number/status, /number/cancel e /number/finish."
        ],
        [
          "service",
          "string",
          "Código do serviço."
        ],
        [
          "service_name",
          "string",
          "Nome do serviço. Vem do provedor, não é traduzido."
        ],
        [
          "country",
          "integer",
          "ID do país."
        ],
        [
          "country_name",
          "string",
          "Nome do país."
        ],
        [
          "phone",
          "string",
          "Número com DDI, só dígitos (5511999999999)."
        ],
        [
          "state",
          "string",
          "Estado da ativação (veja Estados). Valor fixo em inglês; use este na lógica."
        ],
        [
          "state_label",
          "string",
          "Estado em texto, no idioma da conta. Só para exibir."
        ],
        [
          "code",
          "string | null",
          "Último código recebido. null enquanto não chegou."
        ],
        [
          "codes",
          "array",
          "Todos os códigos, [{ code, received_at }], do mais antigo ao mais novo."
        ],
        [
          "price_brl",
          "number",
          "Valor cobrado, em reais."
        ],
        [
          "seconds_left",
          "integer",
          "Segundos até a ativação expirar."
        ],
        [
          "cancel_in_seconds",
          "integer",
          "Segundos até liberar o cancelamento. 0 = já pode."
        ],
        [
          "balance_brl",
          "number",
          "Saldo depois da compra."
        ],
        [
          "— com quantity —",
          "",
          ""
        ],
        [
          "requested",
          "integer",
          "Quantidade pedida."
        ],
        [
          "purchased",
          "integer",
          "Quantidade comprada."
        ],
        [
          "failed",
          "integer",
          "Quantidade não comprada (não é cobrada)."
        ],
        [
          "numbers",
          "array",
          "Ativações, com os mesmos campos acima."
        ],
        [
          "errors",
          "array",
          "Códigos de erro das compras que falharam (compra parcial)."
        ]
      ],
      "errorCodes": [
        "pais_invalido",
        "servico_invalido",
        "quantidade_invalida",
        "preco_invalido",
        "operador_invalido",
        "request_id_invalido",
        "request_id_reutilizada",
        "requisicao_em_andamento",
        "saldo_insuficiente",
        "sem_numeros_disponiveis",
        "preco_abaixo_do_minimo",
        "limite_de_numeros",
        "falha_temporaria"
      ],
      "samples": {
        "curl": "curl \"https://kdefy-sms.com/api/v1/number/get?country=73&service=wa&request_id=pedido-0001\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\"",
        "js": "const res = await fetch(\"https://kdefy-sms.com/api/v1/number/get?country=73&service=wa&request_id=pedido-0001\", {\n  headers: { \"X-API-Key\": process.env.KDEFY_API_KEY },\n});\nconst data = await res.json();\nif (data.status !== \"success\") throw new Error(data.error);\nconsole.log(data);",
        "python": "import os, requests\n\nr = requests.get(\n    \"https://kdefy-sms.com/api/v1/number/get\",\n    params={\"country\": 73, \"service\": \"wa\", \"request_id\": \"pedido-0001\"},\n    headers={\"X-API-Key\": os.environ[\"KDEFY_API_KEY\"]},\n    timeout=20,\n)\ndata = r.json()\nif data[\"status\"] != \"success\":\n    raise RuntimeError(data[\"error\"])\nprint(data)",
        "php": "<?php\n$ch = curl_init(\"https://kdefy-sms.com/api/v1/number/get?country=73&service=wa&request_id=pedido-0001\");\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 20,\n    CURLOPT_HTTPHEADER => [\"X-API-Key: \" . getenv(\"KDEFY_API_KEY\")],\n]);\n$data = json_decode(curl_exec($ch), true);\nif (($data[\"status\"] ?? null) !== \"success\") {\n    throw new RuntimeException($data[\"error\"] ?? \"erro_desconhecido\");\n}\nprint_r($data);",
        "ruby": "require \"net/http\"\nrequire \"json\"\n\nuri = URI(\"https://kdefy-sms.com/api/v1/number/get?country=73&service=wa&request_id=pedido-0001\")\nreq = Net::HTTP::Get.new(uri, \"X-API-Key\" => ENV.fetch(\"KDEFY_API_KEY\"))\nres = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", read_timeout: 20) { |http| http.request(req) }\ndata = JSON.parse(res.body)\nabort(data[\"error\"]) unless data[\"status\"] == \"success\"\nputs JSON.pretty_generate(data)",
        "shell": "#!/usr/bin/env bash\ncurl -sS --max-time 20 -G \"https://kdefy-sms.com/api/v1/number/get\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\" \\\n  --data-urlencode \"country=73\" \\\n  --data-urlencode \"service=wa\" \\\n  --data-urlencode \"request_id=pedido-0001\" | jq ."
      }
    },
    {
      "id": "status",
      "group": "Números",
      "title": "Status e código",
      "methods": [
        "GET"
      ],
      "path": "/number/status",
      "summary": "Estado da ativação e códigos recebidos.",
      "params": [
        {
          "name": "id",
          "required": true,
          "description": "ID da ativação, vem da compra."
        }
      ],
      "response": "{\n  \"status\": \"success\",\n  \"id\": \"1842\",\n  \"service\": \"wa\",\n  \"service_name\": \"WhatsApp\",\n  \"country\": 73,\n  \"country_name\": \"Brasil\",\n  \"phone\": \"5511999999999\",\n  \"state\": \"code_received\",\n  \"state_label\": \"Código recebido\",\n  \"code\": \"123456\",\n  \"codes\": [\n    {\n      \"code\": \"123456\",\n      \"received_at\": \"2026-10-19T14:02:11.000Z\"\n    }\n  ],\n  \"price_brl\": 8.4,\n  \"seconds_left\": 1103,\n  \"cancel_in_seconds\": 0\n}",
      "fields": [
        [
          "id",
          "string",
          "ID da ativação. Vai em /number/status, /number/cancel e /number/finish."
        ],
        [
          "service",
          "string",
          "Código do serviço."
        ],
        [
          "service_name",
          "string",
          "Nome do serviço. Vem do provedor, não é traduzido."
        ],
        [
          "country",
          "integer",
          "ID do país."
        ],
        [
          "country_name",
          "string",
          "Nome do país."
        ],
        [
          "phone",
          "string",
          "Número com DDI, só dígitos (5511999999999)."
        ],
        [
          "state",
          "string",
          "Estado da ativação (veja Estados). Valor fixo em inglês; use este na lógica."
        ],
        [
          "state_label",
          "string",
          "Estado em texto, no idioma da conta. Só para exibir."
        ],
        [
          "code",
          "string | null",
          "Último código recebido. null enquanto não chegou."
        ],
        [
          "codes",
          "array",
          "Todos os códigos, [{ code, received_at }], do mais antigo ao mais novo."
        ],
        [
          "price_brl",
          "number",
          "Valor cobrado, em reais."
        ],
        [
          "seconds_left",
          "integer",
          "Segundos até a ativação expirar."
        ],
        [
          "cancel_in_seconds",
          "integer",
          "Segundos até liberar o cancelamento. 0 = já pode."
        ]
      ],
      "errorCodes": [
        "id_invalido",
        "ativacao_nao_encontrada"
      ],
      "samples": {
        "curl": "curl \"https://kdefy-sms.com/api/v1/number/status?id=1842\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\"",
        "js": "const res = await fetch(\"https://kdefy-sms.com/api/v1/number/status?id=1842\", {\n  headers: { \"X-API-Key\": process.env.KDEFY_API_KEY },\n});\nconst data = await res.json();\nif (data.status !== \"success\") throw new Error(data.error);\nconsole.log(data);",
        "python": "import os, requests\n\nr = requests.get(\n    \"https://kdefy-sms.com/api/v1/number/status\",\n    params={\"id\": \"1842\"},\n    headers={\"X-API-Key\": os.environ[\"KDEFY_API_KEY\"]},\n    timeout=20,\n)\ndata = r.json()\nif data[\"status\"] != \"success\":\n    raise RuntimeError(data[\"error\"])\nprint(data)",
        "php": "<?php\n$ch = curl_init(\"https://kdefy-sms.com/api/v1/number/status?id=1842\");\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 20,\n    CURLOPT_HTTPHEADER => [\"X-API-Key: \" . getenv(\"KDEFY_API_KEY\")],\n]);\n$data = json_decode(curl_exec($ch), true);\nif (($data[\"status\"] ?? null) !== \"success\") {\n    throw new RuntimeException($data[\"error\"] ?? \"erro_desconhecido\");\n}\nprint_r($data);",
        "ruby": "require \"net/http\"\nrequire \"json\"\n\nuri = URI(\"https://kdefy-sms.com/api/v1/number/status?id=1842\")\nreq = Net::HTTP::Get.new(uri, \"X-API-Key\" => ENV.fetch(\"KDEFY_API_KEY\"))\nres = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", read_timeout: 20) { |http| http.request(req) }\ndata = JSON.parse(res.body)\nabort(data[\"error\"]) unless data[\"status\"] == \"success\"\nputs JSON.pretty_generate(data)",
        "shell": "#!/usr/bin/env bash\ncurl -sS --max-time 20 -G \"https://kdefy-sms.com/api/v1/number/status\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\" \\\n  --data-urlencode \"id=1842\" | jq ."
      }
    },
    {
      "id": "cancel",
      "group": "Números",
      "title": "Cancelar",
      "methods": [
        "GET",
        "POST"
      ],
      "path": "/number/cancel",
      "summary": "Cancela e devolve o valor. Só sem código e 2 min após a compra; antes disso responde cancelamento_muito_cedo com retry_in_seconds. Na API não há multa por cancelamentos seguidos.",
      "danger": "Cancelar esta ativação?",
      "params": [
        {
          "name": "id",
          "required": true,
          "description": "ID da ativação."
        }
      ],
      "response": "{\n  \"status\": \"success\",\n  \"id\": \"1842\",\n  \"state\": \"cancelled\",\n  \"refunded_brl\": 8.4,\n  \"balance_brl\": 25.4\n}",
      "fields": [
        [
          "id",
          "string",
          "ID da ativação."
        ],
        [
          "state",
          "string",
          "Sempre \"cancelled\"."
        ],
        [
          "refunded_brl",
          "number",
          "Valor devolvido."
        ],
        [
          "balance_brl",
          "number",
          "Saldo depois do reembolso."
        ]
      ],
      "errorCodes": [
        "id_invalido",
        "ativacao_nao_encontrada",
        "ativacao_nao_cancelavel",
        "cancelamento_muito_cedo",
        "falha_temporaria"
      ],
      "samples": {
        "curl": "curl \"https://kdefy-sms.com/api/v1/number/cancel?id=1842\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\"",
        "js": "const res = await fetch(\"https://kdefy-sms.com/api/v1/number/cancel?id=1842\", {\n  headers: { \"X-API-Key\": process.env.KDEFY_API_KEY },\n});\nconst data = await res.json();\nif (data.status !== \"success\") throw new Error(data.error);\nconsole.log(data);",
        "python": "import os, requests\n\nr = requests.get(\n    \"https://kdefy-sms.com/api/v1/number/cancel\",\n    params={\"id\": \"1842\"},\n    headers={\"X-API-Key\": os.environ[\"KDEFY_API_KEY\"]},\n    timeout=20,\n)\ndata = r.json()\nif data[\"status\"] != \"success\":\n    raise RuntimeError(data[\"error\"])\nprint(data)",
        "php": "<?php\n$ch = curl_init(\"https://kdefy-sms.com/api/v1/number/cancel?id=1842\");\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 20,\n    CURLOPT_HTTPHEADER => [\"X-API-Key: \" . getenv(\"KDEFY_API_KEY\")],\n]);\n$data = json_decode(curl_exec($ch), true);\nif (($data[\"status\"] ?? null) !== \"success\") {\n    throw new RuntimeException($data[\"error\"] ?? \"erro_desconhecido\");\n}\nprint_r($data);",
        "ruby": "require \"net/http\"\nrequire \"json\"\n\nuri = URI(\"https://kdefy-sms.com/api/v1/number/cancel?id=1842\")\nreq = Net::HTTP::Get.new(uri, \"X-API-Key\" => ENV.fetch(\"KDEFY_API_KEY\"))\nres = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", read_timeout: 20) { |http| http.request(req) }\ndata = JSON.parse(res.body)\nabort(data[\"error\"]) unless data[\"status\"] == \"success\"\nputs JSON.pretty_generate(data)",
        "shell": "#!/usr/bin/env bash\ncurl -sS --max-time 20 -G \"https://kdefy-sms.com/api/v1/number/cancel\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\" \\\n  --data-urlencode \"id=1842\" | jq ."
      }
    },
    {
      "id": "finish",
      "group": "Números",
      "title": "Finalizar",
      "methods": [
        "GET",
        "POST"
      ],
      "path": "/number/finish",
      "summary": "Encerra a ativação depois do código. Para de esperar códigos extras. Sem reembolso.",
      "danger": "Finalizar esta ativação?",
      "params": [
        {
          "name": "id",
          "required": true,
          "description": "ID da ativação."
        }
      ],
      "response": "{\n  \"status\": \"success\",\n  \"id\": \"1842\",\n  \"state\": \"completed\"\n}",
      "fields": [
        [
          "id",
          "string",
          "ID da ativação."
        ],
        [
          "state",
          "string",
          "Sempre \"completed\"."
        ]
      ],
      "errorCodes": [
        "id_invalido",
        "ativacao_nao_encontrada",
        "ativacao_nao_finalizavel"
      ],
      "samples": {
        "curl": "curl \"https://kdefy-sms.com/api/v1/number/finish?id=1842\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\"",
        "js": "const res = await fetch(\"https://kdefy-sms.com/api/v1/number/finish?id=1842\", {\n  headers: { \"X-API-Key\": process.env.KDEFY_API_KEY },\n});\nconst data = await res.json();\nif (data.status !== \"success\") throw new Error(data.error);\nconsole.log(data);",
        "python": "import os, requests\n\nr = requests.get(\n    \"https://kdefy-sms.com/api/v1/number/finish\",\n    params={\"id\": \"1842\"},\n    headers={\"X-API-Key\": os.environ[\"KDEFY_API_KEY\"]},\n    timeout=20,\n)\ndata = r.json()\nif data[\"status\"] != \"success\":\n    raise RuntimeError(data[\"error\"])\nprint(data)",
        "php": "<?php\n$ch = curl_init(\"https://kdefy-sms.com/api/v1/number/finish?id=1842\");\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 20,\n    CURLOPT_HTTPHEADER => [\"X-API-Key: \" . getenv(\"KDEFY_API_KEY\")],\n]);\n$data = json_decode(curl_exec($ch), true);\nif (($data[\"status\"] ?? null) !== \"success\") {\n    throw new RuntimeException($data[\"error\"] ?? \"erro_desconhecido\");\n}\nprint_r($data);",
        "ruby": "require \"net/http\"\nrequire \"json\"\n\nuri = URI(\"https://kdefy-sms.com/api/v1/number/finish?id=1842\")\nreq = Net::HTTP::Get.new(uri, \"X-API-Key\" => ENV.fetch(\"KDEFY_API_KEY\"))\nres = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", read_timeout: 20) { |http| http.request(req) }\ndata = JSON.parse(res.body)\nabort(data[\"error\"]) unless data[\"status\"] == \"success\"\nputs JSON.pretty_generate(data)",
        "shell": "#!/usr/bin/env bash\ncurl -sS --max-time 20 -G \"https://kdefy-sms.com/api/v1/number/finish\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\" \\\n  --data-urlencode \"id=1842\" | jq ."
      }
    },
    {
      "id": "activations",
      "group": "Histórico",
      "title": "Listar ativações",
      "methods": [
        "GET"
      ],
      "path": "/activations",
      "summary": "Suas ativações, inclusive as do site, da mais nova para a mais antiga.",
      "params": [
        {
          "name": "state",
          "required": false,
          "description": "\"active\" lista só as em andamento."
        },
        {
          "name": "limit",
          "required": false,
          "description": "Máximo de itens. Padrão 50, máx. 100."
        }
      ],
      "response": "{\n  \"status\": \"success\",\n  \"activations\": [\n    {\n      \"id\": \"1842\",\n      \"service\": \"wa\",\n      \"service_name\": \"WhatsApp\",\n      \"country\": 73,\n      \"country_name\": \"Brasil\",\n      \"phone\": \"5511999999999\",\n      \"state\": \"waiting\",\n      \"state_label\": \"Aguardando código\",\n      \"code\": null,\n      \"codes\": [],\n      \"price_brl\": 8.4,\n      \"seconds_left\": 1200,\n      \"cancel_in_seconds\": 120\n    }\n  ]\n}",
      "fields": [
        [
          "activations[]",
          "array",
          "Ativações, com os mesmos campos de /number/status."
        ]
      ],
      "errorCodes": [],
      "samples": {
        "curl": "curl \"https://kdefy-sms.com/api/v1/activations?state=active\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\"",
        "js": "const res = await fetch(\"https://kdefy-sms.com/api/v1/activations?state=active\", {\n  headers: { \"X-API-Key\": process.env.KDEFY_API_KEY },\n});\nconst data = await res.json();\nif (data.status !== \"success\") throw new Error(data.error);\nconsole.log(data);",
        "python": "import os, requests\n\nr = requests.get(\n    \"https://kdefy-sms.com/api/v1/activations\",\n    params={\"state\": \"active\"},\n    headers={\"X-API-Key\": os.environ[\"KDEFY_API_KEY\"]},\n    timeout=20,\n)\ndata = r.json()\nif data[\"status\"] != \"success\":\n    raise RuntimeError(data[\"error\"])\nprint(data)",
        "php": "<?php\n$ch = curl_init(\"https://kdefy-sms.com/api/v1/activations?state=active\");\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 20,\n    CURLOPT_HTTPHEADER => [\"X-API-Key: \" . getenv(\"KDEFY_API_KEY\")],\n]);\n$data = json_decode(curl_exec($ch), true);\nif (($data[\"status\"] ?? null) !== \"success\") {\n    throw new RuntimeException($data[\"error\"] ?? \"erro_desconhecido\");\n}\nprint_r($data);",
        "ruby": "require \"net/http\"\nrequire \"json\"\n\nuri = URI(\"https://kdefy-sms.com/api/v1/activations?state=active\")\nreq = Net::HTTP::Get.new(uri, \"X-API-Key\" => ENV.fetch(\"KDEFY_API_KEY\"))\nres = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", read_timeout: 20) { |http| http.request(req) }\ndata = JSON.parse(res.body)\nabort(data[\"error\"]) unless data[\"status\"] == \"success\"\nputs JSON.pretty_generate(data)",
        "shell": "#!/usr/bin/env bash\ncurl -sS --max-time 20 -G \"https://kdefy-sms.com/api/v1/activations\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\" \\\n  --data-urlencode \"state=active\" | jq ."
      }
    },
    {
      "id": "transactions",
      "group": "Histórico",
      "title": "Extrato",
      "methods": [
        "GET"
      ],
      "path": "/transactions",
      "summary": "Recargas, compras e reembolsos.",
      "params": [
        {
          "name": "limit",
          "required": false,
          "description": "Máximo de itens. Padrão 50, máx. 200."
        }
      ],
      "response": "{\n  \"status\": \"success\",\n  \"transactions\": [\n    {\n      \"type\": \"PURCHASE\",\n      \"amount_brl\": 8.4,\n      \"balance_after_brl\": 17,\n      \"description\": \"Compra WhatsApp · Brasil (API)\",\n      \"created_at\": \"2026-10-19T14:00:00.000Z\"\n    }\n  ]\n}",
      "fields": [
        [
          "transactions[].type",
          "string",
          "Tipo (PURCHASE, REFUND...)."
        ],
        [
          "transactions[].amount_brl",
          "number",
          "Valor."
        ],
        [
          "transactions[].balance_after_brl",
          "number",
          "Saldo depois."
        ],
        [
          "transactions[].description",
          "string",
          "Descrição."
        ],
        [
          "transactions[].created_at",
          "string",
          "Data (ISO 8601)."
        ]
      ],
      "errorCodes": [],
      "samples": {
        "curl": "curl \"https://kdefy-sms.com/api/v1/transactions?limit=20\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\"",
        "js": "const res = await fetch(\"https://kdefy-sms.com/api/v1/transactions?limit=20\", {\n  headers: { \"X-API-Key\": process.env.KDEFY_API_KEY },\n});\nconst data = await res.json();\nif (data.status !== \"success\") throw new Error(data.error);\nconsole.log(data);",
        "python": "import os, requests\n\nr = requests.get(\n    \"https://kdefy-sms.com/api/v1/transactions\",\n    params={\"limit\": 20},\n    headers={\"X-API-Key\": os.environ[\"KDEFY_API_KEY\"]},\n    timeout=20,\n)\ndata = r.json()\nif data[\"status\"] != \"success\":\n    raise RuntimeError(data[\"error\"])\nprint(data)",
        "php": "<?php\n$ch = curl_init(\"https://kdefy-sms.com/api/v1/transactions?limit=20\");\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 20,\n    CURLOPT_HTTPHEADER => [\"X-API-Key: \" . getenv(\"KDEFY_API_KEY\")],\n]);\n$data = json_decode(curl_exec($ch), true);\nif (($data[\"status\"] ?? null) !== \"success\") {\n    throw new RuntimeException($data[\"error\"] ?? \"erro_desconhecido\");\n}\nprint_r($data);",
        "ruby": "require \"net/http\"\nrequire \"json\"\n\nuri = URI(\"https://kdefy-sms.com/api/v1/transactions?limit=20\")\nreq = Net::HTTP::Get.new(uri, \"X-API-Key\" => ENV.fetch(\"KDEFY_API_KEY\"))\nres = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", read_timeout: 20) { |http| http.request(req) }\ndata = JSON.parse(res.body)\nabort(data[\"error\"]) unless data[\"status\"] == \"success\"\nputs JSON.pretty_generate(data)",
        "shell": "#!/usr/bin/env bash\ncurl -sS --max-time 20 -G \"https://kdefy-sms.com/api/v1/transactions\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\" \\\n  --data-urlencode \"limit=20\" | jq ."
      }
    }
  ],
  "rules": [
    "Cada ativação espera o código por até 20 min. Sem código, expira e o valor volta ao saldo.",
    "Depois do primeiro código não há reembolso.",
    "Você só enxerga e opera as suas ativações.",
    "Recarga de saldo é pelo site (PIX).",
    "Toda requisição fica registrada na conta. O admin consulta o uso do token.",
    "Abuso ou fraude pode bloquear a conta."
  ],
  "guides": [
    {
      "id": "polling",
      "title": "Esperando o código",
      "text": "Consulte /number/status a cada 5 s e pare quando code vier preenchido ou state virar cancelled, expired ou completed.",
      "bullets": [
        "Defina um prazo máximo no seu código (20 min).",
        "Com vários números, use /activations?state=active. Uma chamada traz todas as ativas.",
        "Uma ativação pode receber mais de um código. Leia codes[]."
      ]
    },
    {
      "id": "lote",
      "title": "Compra em lote",
      "text": "quantity=N compra N números numa chamada, até 50. Compra parcial é normal: se o estoque ou o saldo acabar no meio, o resto não é cobrado.",
      "bullets": [
        "A resposta traz numbers[], requested, purchased e failed.",
        "errors lista o motivo das falhas, quando houver.",
        "Limite de 50 números por minuto por conta."
      ],
      "code": {
        "lang": "bash",
        "body": "curl \"https://kdefy-sms.com/api/v1/number/get?country=73&service=wa&quantity=3&request_id=pedido-0001\" \\\n  -H \"X-API-Key: $KDEFY_API_KEY\""
      }
    },
    {
      "id": "idempotencia",
      "title": "Idempotência (request_id)",
      "text": "Mande request_id em toda compra. Se der timeout e você não sabe se a compra saiu, repita a mesma chamada com a mesma request_id. A API devolve a resposta original, com o header Idempotent-Replay: true, e não cobra de novo.",
      "bullets": [
        "Compra nova, request_id nova.",
        "8 a 80 caracteres (letras, números, _ . : -). O header Idempotency-Key também vale.",
        "A chave dura 24 h e só é salva quando a compra deu certo. Compra que falhou pode ser repetida.",
        "Mesma chave com parâmetros diferentes: request_id_reutilizada."
      ]
    }
  ],
  "quickstart": {
    "intro": "Compra, espera o código e finaliza. Se o código não chega, cancela. A compra usa request_id, então repetir a chamada não duplica.",
    "js": "// Node 18+. Rode com: KDEFY_API_KEY=seu_token node kdefy.mjs\nimport { randomUUID } from \"node:crypto\";\n\nconst BASE = \"https://kdefy-sms.com/api/v1\";\nconst KEY = process.env.KDEFY_API_KEY;\nconst sleep = (ms) => new Promise((r) => setTimeout(r, ms));\n\nasync function api(path, params = {}) {\n  const qs = new URLSearchParams(params).toString();\n  const res = await fetch(`${BASE}${path}${qs ? \"?\" + qs : \"\"}`, {\n    headers: { \"X-API-Key\": KEY },\n    signal: AbortSignal.timeout(20000),\n  });\n  const body = await res.json();\n  if (body.status !== \"success\") {\n    const err = new Error(body.message);\n    err.code = body.error;\n    err.http = res.status;\n    err.retryIn = body.retry_in_seconds;\n    throw err;\n  }\n  return body;\n}\n\nasync function buy(country, service) {\n  const requestId = randomUUID(); // uma por compra\n  for (let attempt = 1; attempt <= 4; attempt++) {\n    try {\n      return await api(\"/number/get\", { country, service, request_id: requestId });\n    } catch (e) {\n      const transient = e.name === \"TimeoutError\" || e.name === \"TypeError\" || e.http === 502 || e.code === \"requisicao_em_andamento\";\n      if (!transient || attempt === 4) throw e;\n      await sleep(2000 * attempt); // repete com a mesma request_id\n    }\n  }\n}\n\nasync function waitForCode(id, maxMinutes = 20) {\n  const deadline = Date.now() + maxMinutes * 60_000;\n  while (Date.now() < deadline) {\n    const s = await api(\"/number/status\", { id });\n    if (s.code || [\"cancelled\", \"expired\", \"completed\"].includes(s.state)) return s;\n    await sleep(5000);\n  }\n  return null;\n}\n\nconst number = await buy(73, \"wa\");\nconsole.log(\"Número:\", number.phone);\n\nconst result = await waitForCode(number.id);\nif (result?.code) {\n  console.log(\"Código:\", result.code);\n  await api(\"/number/finish\", { id: number.id });\n} else {\n  try {\n    await api(\"/number/cancel\", { id: number.id });\n  } catch (e) {\n    if (e.code !== \"cancelamento_muito_cedo\") throw e;\n    await sleep((e.retryIn + 1) * 1000);\n    await api(\"/number/cancel\", { id: number.id });\n  }\n  console.log(\"Cancelado e reembolsado.\");\n}",
    "python": "# Python 3.9+. pip install requests\n# Rode com: KDEFY_API_KEY=seu_token python kdefy.py\nimport os, time, uuid, requests\n\nBASE = \"https://kdefy-sms.com/api/v1\"\nHEADERS = {\"X-API-Key\": os.environ[\"KDEFY_API_KEY\"]}\n\nclass ApiError(Exception):\n    def __init__(self, body, http):\n        super().__init__(body.get(\"message\"))\n        self.code = body.get(\"error\")\n        self.http = http\n        self.retry_in = body.get(\"retry_in_seconds\")\n\ndef api(path, **params):\n    r = requests.get(BASE + path, params=params, headers=HEADERS, timeout=20)\n    body = r.json()\n    if body.get(\"status\") != \"success\":\n        raise ApiError(body, r.status_code)\n    return body\n\ndef buy(country, service):\n    request_id = str(uuid.uuid4())  # uma por compra\n    for attempt in range(1, 5):\n        try:\n            return api(\"/number/get\", country=country, service=service, request_id=request_id)\n        except requests.RequestException:\n            pass\n        except ApiError as e:\n            if not (e.http == 502 or e.code == \"requisicao_em_andamento\"):\n                raise\n        if attempt == 4:\n            raise RuntimeError(\"não foi possível comprar agora\")\n        time.sleep(2 * attempt)  # repete com a mesma request_id\n\ndef wait_for_code(activation_id, max_minutes=20):\n    deadline = time.time() + max_minutes * 60\n    while time.time() < deadline:\n        s = api(\"/number/status\", id=activation_id)\n        if s[\"code\"] or s[\"state\"] in (\"cancelled\", \"expired\", \"completed\"):\n            return s\n        time.sleep(5)\n    return None\n\nnumber = buy(73, \"wa\")\nprint(\"Número:\", number[\"phone\"])\n\nresult = wait_for_code(number[\"id\"])\nif result and result[\"code\"]:\n    print(\"Código:\", result[\"code\"])\n    api(\"/number/finish\", id=number[\"id\"])\nelse:\n    try:\n        api(\"/number/cancel\", id=number[\"id\"])\n    except ApiError as e:\n        if e.code != \"cancelamento_muito_cedo\":\n            raise\n        time.sleep((e.retry_in or 5) + 1)\n        api(\"/number/cancel\", id=number[\"id\"])\n    print(\"Cancelado e reembolsado.\")",
    "php": "<?php\n// PHP 8+ com ext-curl. Rode com: KDEFY_API_KEY=seu_token php kdefy.php\nconst BASE = \"https://kdefy-sms.com/api/v1\";\n\nfunction api(string $path, array $params = []): array {\n    $ch = curl_init(BASE . $path . ($params ? \"?\" . http_build_query($params) : \"\"));\n    curl_setopt_array($ch, [\n        CURLOPT_RETURNTRANSFER => true,\n        CURLOPT_TIMEOUT => 20,\n        CURLOPT_HTTPHEADER => [\"X-API-Key: \" . getenv(\"KDEFY_API_KEY\")],\n    ]);\n    $raw = curl_exec($ch);\n    if ($raw === false) throw new RuntimeException(\"rede: \" . curl_error($ch));\n    $body = json_decode($raw, true);\n    if (($body[\"status\"] ?? null) !== \"success\") {\n        $e = new RuntimeException($body[\"error\"] ?? \"erro_desconhecido\");\n        $e->retryIn = $body[\"retry_in_seconds\"] ?? 0;\n        throw $e;\n    }\n    return $body;\n}\n\n$requestId = bin2hex(random_bytes(16)); // uma por compra\n$n = api(\"/number/get\", [\"country\" => 73, \"service\" => \"wa\", \"request_id\" => $requestId]);\necho \"Número: {$n['phone']}\\n\";\n\n$deadline = time() + 20 * 60;\nwhile (time() < $deadline) {\n    $s = api(\"/number/status\", [\"id\" => $n[\"id\"]]);\n    if (!empty($s[\"code\"]) || in_array($s[\"state\"], [\"cancelled\", \"expired\", \"completed\"], true)) break;\n    sleep(5);\n}\n\nif (!empty($s[\"code\"])) {\n    echo \"Código: {$s['code']}\\n\";\n    api(\"/number/finish\", [\"id\" => $n[\"id\"]]);\n} else {\n    api(\"/number/cancel\", [\"id\" => $n[\"id\"]]);\n    echo \"Cancelado e reembolsado.\\n\";\n}",
    "ruby": "# Ruby 3+. Rode com: KDEFY_API_KEY=seu_token ruby kdefy.rb\nrequire \"net/http\"\nrequire \"json\"\nrequire \"securerandom\"\n\nBASE = \"https://kdefy-sms.com/api/v1\"\n\ndef api(path, params = {})\n  uri = URI(BASE + path)\n  uri.query = URI.encode_www_form(params) unless params.empty?\n  req = Net::HTTP::Get.new(uri, \"X-API-Key\" => ENV.fetch(\"KDEFY_API_KEY\"))\n  res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", read_timeout: 20) { |h| h.request(req) }\n  body = JSON.parse(res.body)\n  raise \"#{body['error']}: #{body['message']}\" unless body[\"status\"] == \"success\"\n  body\nend\n\nn = api(\"/number/get\", country: 73, service: \"wa\", request_id: SecureRandom.uuid) # uma por compra\nputs \"Número: #{n['phone']}\"\n\ndeadline = Time.now + 20 * 60\ns = nil\nwhile Time.now < deadline\n  s = api(\"/number/status\", id: n[\"id\"])\n  break if s[\"code\"] || %w[cancelled expired completed].include?(s[\"state\"])\n  sleep 5\nend\n\nif s && s[\"code\"]\n  puts \"Código: #{s['code']}\"\n  api(\"/number/finish\", id: n[\"id\"])\nelse\n  api(\"/number/cancel\", id: n[\"id\"])\n  puts \"Cancelado e reembolsado.\"\nend",
    "shell": "#!/usr/bin/env bash\n# Requer curl e jq. Rode com: KDEFY_API_KEY=seu_token bash kdefy.sh\nset -euo pipefail\nBASE=\"https://kdefy-sms.com/api/v1\"\n\napi() { # api /rota chave=valor ...\n  local path=\"$1\"; shift\n  local args=(); for kv in \"$@\"; do args+=(--data-urlencode \"$kv\"); done\n  curl -sS --max-time 20 -G \"$BASE$path\" -H \"X-API-Key: $KDEFY_API_KEY\" \"${args[@]}\"\n}\n\nnum=$(api /number/get country=73 service=wa \"request_id=$(uuidgen)\") # uma por compra\n[ \"$(jq -r .status <<<\"$num\")\" = success ] || { jq . <<<\"$num\"; exit 1; }\nid=$(jq -r .id <<<\"$num\")\necho \"Número: $(jq -r .phone <<<\"$num\")\"\n\nfor _ in $(seq 240); do # 240 x 5 s = 20 min\n  st=$(api /number/status \"id=$id\")\n  code=$(jq -r '.code // empty' <<<\"$st\")\n  state=$(jq -r .state <<<\"$st\")\n  [ -n \"$code\" ] && break\n  case \"$state\" in cancelled|expired|completed) break ;; esac\n  sleep 5\ndone\n\nif [ -n \"${code:-}\" ]; then\n  echo \"Código: $code\"\n  api /number/finish \"id=$id\" >/dev/null\nelse\n  api /number/cancel \"id=$id\" >/dev/null\n  echo \"Cancelado e reembolsado.\"\nfi"
  },
  "practices": [
    "Token em variável de ambiente. Nunca no código, no front-end ou em repositório.",
    "request_id em toda compra. Repetiu a chamada? Mesma request_id.",
    "Polling de 5 s, com prazo máximo.",
    "Vários números: /activations?state=active em vez de um /number/status por ativação.",
    "Trate error, não message.",
    "429: espere retry_in_seconds. 502: tente de novo com espera crescente (2 s, 4 s, 6 s).",
    "Use max_price_brl como teto. Preço e estoque mudam, então consulte /offers antes de lotes grandes.",
    "Confira /balance antes de lotes grandes.",
    "Guarde request_id e id da ativação nos logs.",
    "Cancele só o que não vai usar. Depois do primeiro código não há reembolso."
  ],
  "faq": [
    [
      "Quanto o código demora?",
      "Em geral segundos, mas depende do serviço. A ativação espera até 20 min. Sem código nesse prazo, expira e o valor volta ao saldo."
    ],
    [
      "Dei timeout na compra. E agora?",
      "Repita a mesma chamada com a mesma request_id: a resposta original volta e nada é cobrado de novo. Se você não usou request_id, liste /activations?state=active antes de comprar outra vez."
    ],
    [
      "Posso cancelar logo depois de comprar?",
      "Não. O cancelamento libera 2 min após a compra. cancel_in_seconds mostra quanto falta."
    ],
    [
      "sem_numeros_disponiveis com saldo. Por quê?",
      "A faixa de preço está sem estoque agora. Suba max_price_brl, troque o país ou veja onde há estoque em /offers."
    ],
    [
      "O preço muda entre a consulta e a compra?",
      "Pode mudar. Vale o price_brl da resposta da compra, e max_price_brl impede que passe do seu teto."
    ],
    [
      "Preciso de request_id?",
      "Não é obrigatória, mas use em qualquer automação. Sem ela, repetir a chamada por engano compra duas vezes."
    ],
    [
      "Dá para comprar mais de 50 números?",
      "Não numa requisição, e o teto é 50 por minuto por conta. Acima disso a API responde limite_de_numeros."
    ],
    [
      "Por que a resposta vem em português?",
      "message e state_label seguem o idioma da conta. Force com ?lang=pt ou ?lang=en. Chaves do JSON, state e códigos de erro são sempre os mesmos."
    ],
    [
      "Como recarrego o saldo?",
      "Pelo site, com PIX. O saldo da API é o mesmo do site."
    ],
    [
      "O token vazou.",
      "Gere outro em API, no site. O antigo para de funcionar na hora. Se viu uso estranho, fale com o suporte."
    ]
  ]
}