{
  "openapi": "3.1.0",
  "info": {
    "title": "Sparkle Tracker API",
    "version": "1.0.0",
    "summary": "API de conversão do Sparkle Tracker: envie leads e eventos de venda e consulte as operações da conta.",
    "description": "API REST do Sparkle Tracker, plataforma de rastreamento e inteligência de vendas para grupo e comunidade de WhatsApp, venda direta e comercial 1x1.\n\nUse para mandar ao Sparkle Tracker os leads e eventos que nascem fora dele (quiz, formulário, CRM, checkout próprio). Cada lead entra no painel com a origem, vai para a ficha e, quando há pixel configurado, o evento segue para o Meta.\n\nAutenticação: chave de API da conta no cabeçalho `Authorization: Bearer spk_live_...`. A chave é gerada no painel, em Integrações. A API está disponível nos planos com acesso à API (Expert e Growth).\n\nPara agentes de IA que precisam ler métricas e configurar a conta, o Sparkle Tracker também tem um servidor MCP em https://ap.sparkletracker.com/mcp, com OAuth 2.1.",
    "contact": {
      "name": "Suporte Sparkle Tracker",
      "email": "suporte@sparkletracker.com",
      "url": "https://sparkletracker.com/central-de-ajuda"
    },
    "termsOfService": "https://sparkletracker.com/termos-de-uso"
  },
  "externalDocs": {
    "description": "Documentação completa da API",
    "url": "https://docs.sparkletracker.com"
  },
  "servers": [
    { "url": "https://ap.sparkletracker.com", "description": "Produção" }
  ],
  "security": [ { "chaveApi": [] } ],
  "tags": [
    { "name": "Leads", "description": "Envio de leads e eventos de conversão." },
    { "name": "Conta", "description": "Consulta da conta e das operações da chave de API." }
  ],
  "paths": {
    "/api/v1/leads": {
      "post": {
        "operationId": "enviarLead",
        "tags": ["Leads"],
        "summary": "Enviar um lead ou evento de conversão",
        "description": "Cria ou atualiza o lead pelo telefone e registra o evento. O telefone é o único campo obrigatório. Envie `id_event` para que um reenvio do mesmo evento não seja contado duas vezes (o repetido devolve 409). Use `operation_id` (de `GET /api/v1/operations`) para colocar o lead numa operação específica.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/Lead" },
              "examples": {
                "simples": {
                  "summary": "Lead com origem de anúncio",
                  "value": {
                    "phone": "+5511999998888",
                    "name": "Maria Souza",
                    "email": "maria@exemplo.com",
                    "event_name": "Lead",
                    "utm_source": "facebook",
                    "utm_campaign": "lancamento-novembro",
                    "id_event": "form-8f2c1a"
                  }
                },
                "venda": {
                  "summary": "Venda com valor",
                  "value": {
                    "phone": "11999998888",
                    "event_name": "Purchase",
                    "value": 497.0,
                    "currency": "BRL",
                    "product_name": "Curso Completo",
                    "id_event": "pedido-10293"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Lead processado.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LeadProcessado" } } }
          },
          "400": { "$ref": "#/components/responses/DadosInvalidos" },
          "401": { "$ref": "#/components/responses/SemChave" },
          "403": { "$ref": "#/components/responses/Proibido" },
          "404": { "$ref": "#/components/responses/NaoEncontrado" },
          "409": {
            "description": "O `id_event` já foi processado antes. Nada foi gravado de novo.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventoDuplicado" } } }
          },
          "500": { "$ref": "#/components/responses/ErroInterno" }
        }
      }
    },
    "/api/v1/status": {
      "get": {
        "operationId": "consultarStatus",
        "tags": ["Conta"],
        "summary": "Conferir a chave de API e a situação da conta",
        "description": "Use para testar se a chave está certa antes de enviar eventos. Devolve o nome da conta, a situação e os limites.",
        "responses": {
          "200": {
            "description": "Chave válida.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusConta" } } }
          },
          "403": { "$ref": "#/components/responses/Proibido" },
          "404": { "$ref": "#/components/responses/NaoEncontrado" }
        }
      }
    },
    "/api/v1/operations": {
      "get": {
        "operationId": "listarOperacoes",
        "tags": ["Conta"],
        "summary": "Listar as operações da conta",
        "description": "Lista as operações da conta dona da chave (id, nome e tipo). O `id` é o valor de `operation_id` em `POST /api/v1/leads`. Só leitura.",
        "responses": {
          "200": {
            "description": "Operações da conta, da mais antiga para a mais nova.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ListaOperacoes" } } }
          },
          "401": { "$ref": "#/components/responses/SemChave" },
          "403": { "$ref": "#/components/responses/Proibido" },
          "404": { "$ref": "#/components/responses/NaoEncontrado" },
          "500": { "$ref": "#/components/responses/ErroInterno" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "chaveApi": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "spk_live_...",
        "description": "Chave de API da conta, gerada no painel do Sparkle Tracker em Integrações. Envie em `Authorization: Bearer spk_live_...`."
      }
    },
    "schemas": {
      "Lead": {
        "type": "object",
        "required": ["phone"],
        "properties": {
          "phone": {
            "description": "Telefone do lead. De preferência em E.164 (`+5511999998888`). Número sem DDI com 10 ou 11 dígitos é lido como brasileiro, a menos que `country` diga outro país. Também aceita o telefone em partes.",
            "oneOf": [
              { "type": "string", "minLength": 8 },
              { "$ref": "#/components/schemas/TelefoneEmPartes" }
            ]
          },
          "ddi": { "type": ["string", "number"], "maxLength": 8, "description": "DDI em campo próprio (`1`, `+34`, `0034`). Tem prioridade sobre `country`." },
          "ddd": { "type": ["string", "number"], "maxLength": 8, "description": "DDD em campo próprio." },
          "country": { "type": "string", "minLength": 2, "maxLength": 2, "description": "País do TELEFONE (ISO de 2 letras). Só completa número enviado sem DDI." },
          "id_event": { "type": "string", "maxLength": 128, "description": "Identificador único do evento no seu sistema. Evita contar o mesmo evento duas vezes." },
          "name": { "type": "string", "description": "Nome do lead. Acima de 200 caracteres é cortado, não recusado." },
          "email": { "type": "string", "format": "email", "maxLength": 320 },
          "value": { "type": ["string", "number"], "description": "Valor do evento, por exemplo o valor da venda." },
          "currency": { "type": "string", "maxLength": 5, "examples": ["BRL"] },
          "event_name": { "type": "string", "maxLength": 100, "description": "Nome do evento, por exemplo `Lead` ou `Purchase`." },
          "product_name": { "type": ["string", "number"], "maxLength": 120, "description": "Produto da venda ou do evento." },
          "operation_id": { "type": "string", "maxLength": 64, "description": "ID de uma operação da conta (veja `GET /api/v1/operations`). ID de outra conta é ignorado." },
          "utm_source": { "type": "string", "maxLength": 200 },
          "utm_medium": { "type": "string", "maxLength": 200 },
          "utm_campaign": { "type": "string", "maxLength": 200 },
          "utm_content": { "type": "string", "maxLength": 200 },
          "utm_term": { "type": "string", "maxLength": 200 },
          "fbclid": { "type": "string", "maxLength": 500 },
          "fbc": { "type": "string", "maxLength": 500, "description": "Cookie `_fbc` do lead." },
          "fbp": { "type": "string", "maxLength": 500, "description": "Cookie `_fbp` do lead." },
          "gclid": { "type": "string", "maxLength": 500 },
          "wbraid": { "type": "string", "maxLength": 500 },
          "gbraid": { "type": "string", "maxLength": 500 },
          "ttclid": { "type": "string", "maxLength": 500 },
          "city": { "type": "string", "maxLength": 120, "description": "Cidade do lead." },
          "region": { "type": "string", "maxLength": 120, "description": "Estado ou região do lead." },
          "zip": { "type": "string", "maxLength": 20, "description": "CEP do lead." },
          "lead_country": { "type": "string", "minLength": 2, "maxLength": 2, "description": "País do LEAD (ISO de 2 letras)." },
          "ip": { "type": "string", "maxLength": 64, "description": "IP do lead (não do seu servidor). Melhora a correspondência do evento no Meta." },
          "user_agent": { "type": "string", "maxLength": 500, "description": "Navegador do lead (não do seu servidor)." },
          "event_time": { "type": ["string", "number"], "description": "Quando o evento aconteceu: ISO 8601 ou Unix em segundos ou milissegundos." },
          "custom_fields": {
            "type": "object",
            "description": "Dados livres do lead, mostrados na ficha. Até 50 chaves, chave com até 64 caracteres e texto com até 500.",
            "additionalProperties": { "type": ["string", "number", "boolean", "null"] }
          }
        }
      },
      "TelefoneEmPartes": {
        "type": "object",
        "description": "Telefone dividido em campos, como alguns checkouts e CRMs enviam.",
        "properties": {
          "ddi": { "type": ["string", "number"] },
          "ddd": { "type": ["string", "number"] },
          "number": { "type": ["string", "number"] },
          "phone": { "type": ["string", "number"] },
          "country": { "type": "string", "minLength": 2, "maxLength": 2 }
        }
      },
      "LeadProcessado": {
        "type": "object",
        "required": ["success", "message", "lead_id"],
        "properties": {
          "success": { "type": "boolean", "const": true },
          "message": { "type": "string", "examples": ["Lead processado com sucesso."] },
          "lead_id": { "type": "string" },
          "phone": { "type": "string", "description": "Telefone como foi gravado." },
          "phone_country": { "type": ["string", "null"] },
          "phone_warning": { "type": "string" },
          "event_name": { "type": ["string", "null"] },
          "product_name": { "type": ["string", "null"] },
          "operation_id": { "type": ["string", "null"] },
          "operation_name": { "type": ["string", "null"] },
          "meta_event_name": { "type": ["string", "null"], "description": "Nome do evento enviado ao Meta." },
          "attribution_source": { "type": ["string", "null"], "description": "De onde veio a origem atribuída ao lead." },
          "pixel_sent": { "type": "boolean", "description": "Se o evento foi enviado a algum pixel." },
          "event_time": { "type": "string", "format": "date-time" },
          "custom_fields_saved": {},
          "warnings": { "type": "array", "items": {} },
          "id_event": { "type": "string" }
        }
      },
      "EventoDuplicado": {
        "type": "object",
        "properties": {
          "error": { "type": "string", "examples": ["id_event duplicado."] },
          "message": { "type": "string" },
          "id_event": { "type": "string" },
          "lead_id": { "type": ["string", "null"] }
        }
      },
      "StatusConta": {
        "type": "object",
        "properties": {
          "user": { "type": "string", "description": "Nome da conta." },
          "status": { "type": "string", "description": "Situação da conta." },
          "limits": { "type": ["object", "null"], "description": "Limites do plano da conta." }
        }
      },
      "ListaOperacoes": {
        "type": "object",
        "required": ["operations"],
        "properties": {
          "operations": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": { "type": "string", "description": "Valor para `operation_id`." },
                "name": { "type": ["string", "null"] },
                "type": { "type": ["string", "null"], "enum": ["group", "dm_1x1", "dm_1x1_ab", "direct_checkout", null] },
                "type_label": { "type": ["string", "null"], "examples": ["Grupo/Comunidade", "WhatsApp Comercial", "Venda Direta"] }
              }
            }
          }
        }
      },
      "Erro": {
        "type": "object",
        "description": "Erro em JSON. Na maior parte dos casos `error` é o texto da mensagem; erros de validação e de endereço inexistente trazem `error` como objeto com `code`, `message` e, quando ajuda, `details` ou `hint`.",
        "required": ["error"],
        "properties": {
          "error": {
            "oneOf": [
              { "type": "string" },
              {
                "type": "object",
                "required": ["code", "message"],
                "properties": {
                  "code": { "type": "string", "examples": ["VALIDATION_ERROR", "NOT_FOUND"] },
                  "message": { "type": "string" },
                  "hint": { "type": "string" },
                  "details": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": { "field": { "type": "string" }, "message": { "type": "string" } }
                    }
                  }
                }
              }
            ]
          },
          "message": { "type": "string" }
        }
      }
    },
    "responses": {
      "DadosInvalidos": {
        "description": "Corpo inválido, por exemplo sem `phone`. `error.details` lista cada campo com problema.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Erro" },
            "example": { "error": { "code": "VALIDATION_ERROR", "message": "O campo \"phone\" é obrigatório e deve ter pelo menos 8 dígitos", "details": [ { "field": "phone", "message": "O campo \"phone\" é obrigatório e deve ter pelo menos 8 dígitos" } ] } }
          }
        }
      },
      "SemChave": {
        "description": "Faltou o cabeçalho `Authorization: Bearer spk_live_...`.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Erro" }, "example": { "error": "Token de autenticação não fornecido." } } }
      },
      "Proibido": {
        "description": "Chave inválida, conta suspensa, teste encerrado, limite de leads atingido ou plano sem acesso à API.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Erro" }, "example": { "error": "Seu plano atual não permite acesso à API." } } }
      },
      "NaoEncontrado": {
        "description": "Endereço inexistente na API.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Erro" }, "example": { "error": { "code": "NOT_FOUND", "message": "Endereço não encontrado na API do Sparkle Tracker.", "hint": "Confira o método e o caminho em https://sparkletracker.com/openapi.json" } } } }
      },
      "ErroInterno": {
        "description": "Erro inesperado no servidor. Pode tentar de novo; com `id_event`, o reenvio não duplica o evento.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Erro" }, "example": { "error": "Erro interno ao processar API." } } }
      }
    }
  }
}
