{
  "openapi": "3.1.0",
  "info": {
    "title": "WebDone CRM API",
    "description": "API REST do WebDone CRM para integrações POS, ERP, e-commerce e automações.\n\n## Autenticação\nTodos os endpoints (excepto `/v1/health`) requerem uma API key no header:\n```\nAuthorization: Bearer wd_<chave>\n```\nou\n```\nX-Api-Key: wd_<chave>\n```\n\nA chave encontra-se em **Definições → Integrações → Chaves API**.\n\n## Rate Limit\n- Plano Empresa: **2 000 pedidos/hora**\n- Resposta 429 inclui `retryAfterSec`\n\n## Idempotência\nPara POSTs, enviar o header `Idempotency-Key: <uuid>` para evitar duplicados em caso de retry de rede. Cache 24h.\n\n## Paginação\nCursor-based via `?after=<docId>&limit=<N>` (max 100). A resposta inclui `nextCursor`.\n\n## Versionamento\nHeader de resposta: `WebDone-Version: 2026-06-22`",
    "version": "1.5.0",
    "contact": {
      "name": "WebDone Suporte",
      "url": "https://webdone.pt",
      "email": "contacto@webdonecrm.com"
    },
    "license": {
      "name": "Proprietário",
      "url": "https://webdone.pt/termos"
    }
  },
  "servers": [
    {
      "url": "https://europe-west1-{projectId}.cloudfunctions.net/api",
      "description": "Produção",
      "variables": {
        "projectId": {
          "default": "webdone-crm",
          "description": "Firebase Project ID"
        }
      }
    }
  ],
  "security": [
    { "bearerAuth": [] }
  ],
  "tags": [
    { "name": "Health", "description": "Estado da API — sem autenticação" },
    { "name": "Leads", "description": "CRUD completo de leads/contactos" },
    { "name": "Tasks", "description": "Tarefas associadas a leads" },
    { "name": "Comments", "description": "Comentários e notas em leads" },
    { "name": "Webhooks", "description": "Configuração de endpoints webhook" },
    { "name": "Sync", "description": "Sincronização com gestão comercial/POS" },
    { "name": "Team", "description": "Membros da equipa" }
  ],
  "paths": {
    "/v1/health": {
      "get": {
        "tags": ["Health"],
        "summary": "Estado da API",
        "description": "Endpoint público (sem autenticação). Retorna status da API.",
        "security": [],
        "operationId": "getHealth",
        "responses": {
          "200": {
            "description": "API operacional",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": { "type": "boolean", "example": true },
                    "service": { "type": "string", "example": "webdone-crm-api" },
                    "version": { "type": "string", "example": "2026-06-22" },
                    "timestamp": { "type": "string", "format": "date-time" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/leads": {
      "get": {
        "tags": ["Leads"],
        "summary": "Listar leads",
        "description": "Lista leads com paginação cursor-based e filtros. Ideal para sync incremental com POS/ERP usando `updatedSince`.",
        "operationId": "listLeads",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Número de resultados (1–100)",
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 }
          },
          {
            "name": "after",
            "in": "query",
            "description": "Cursor para paginação — docId do último resultado anterior",
            "schema": { "type": "string" }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Filtrar por statusNegocio",
            "schema": {
              "type": "string",
              "enum": ["Por Contactar", "Contactado", "Reunião Marcada", "Proposta Enviada", "Em Negociação", "Ganho", "Perdido", "Em Espera"]
            }
          },
          {
            "name": "pipeline",
            "in": "query",
            "description": "Filtrar por pipeline",
            "schema": { "type": "string", "enum": ["vendas", "pos-venda", "parceiros"] }
          },
          {
            "name": "responsavel",
            "in": "query",
            "description": "Filtrar por nome do responsável",
            "schema": { "type": "string" }
          },
          {
            "name": "updatedSince",
            "in": "query",
            "description": "Apenas leads atualizadas após esta data (ISO-8601). Útil para sync incremental.",
            "schema": { "type": "string", "format": "date-time", "example": "2026-06-01T00:00:00Z" }
          },
          {
            "name": "externalId",
            "in": "query",
            "description": "Filtrar por ID no sistema externo (POS/ERP). Ex: 'POS-12345'",
            "schema": { "type": "string" }
          },
          {
            "name": "externalSource",
            "in": "query",
            "description": "Filtrar por sistema de origem. Ex: 'moloni', 'phc', 'pos_custom'",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Lista de leads",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Lead" }
                    },
                    "nextCursor": {
                      "type": "string",
                      "nullable": true,
                      "description": "Cursor para a próxima página. null se não há mais."
                    },
                    "count": { "type": "integer", "description": "Número de resultados nesta página" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/RateLimit" }
        }
      },
      "post": {
        "tags": ["Leads"],
        "summary": "Criar lead (ou upsert por externalId)",
        "description": "Cria uma nova lead. Se `externalId` + `externalSource` forem fornecidos e já existir uma lead com esses valores, ATUALIZA em vez de criar duplicado (upsert).\n\nUsar header `Idempotency-Key` para evitar duplicados em retries de rede.",
        "operationId": "createLead",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "UUID para idempotência (24h cache). Pedidos repetidos com a mesma chave devolvem o resultado original.",
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/LeadCreate" },
              "examples": {
                "basic": {
                  "summary": "Lead simples",
                  "value": {
                    "nome": "Restaurante Silva",
                    "telefone": "+351911234567",
                    "emailDireto": "silva@restaurante.pt"
                  }
                },
                "pos_upsert": {
                  "summary": "Upsert por externalId (POS)",
                  "value": {
                    "nome": "Restaurante Silva",
                    "externalId": "POS-12345",
                    "externalSource": "pos_restaurante",
                    "nif": "123456789",
                    "telefone": "+351911234567",
                    "valor": 1250.00
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Lead criada",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "$ref": "#/components/schemas/Lead" },
                    "upserted": {
                      "type": "boolean",
                      "description": "true se foi update de lead existente, false se criação"
                    }
                  }
                }
              }
            }
          },
          "200": {
            "description": "Lead atualizada (upsert por externalId)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "$ref": "#/components/schemas/Lead" },
                    "upserted": { "type": "boolean", "example": true }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimit" }
        }
      }
    },
    "/v1/leads/bulk": {
      "post": {
        "tags": ["Leads"],
        "summary": "Criar/upsert leads em lote",
        "description": "Importação em lote de até 100 leads. Suporta upsert por externalId. Ideal para sync diário completo do POS.\n\nO processamento é parcial: se um lead falhar, os restantes continuam. Verificar `errors[]` na resposta.",
        "operationId": "bulkLeads",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["leads"],
                "properties": {
                  "leads": {
                    "type": "array",
                    "maxItems": 100,
                    "items": { "$ref": "#/components/schemas/LeadCreate" }
                  },
                  "source": {
                    "type": "string",
                    "description": "Tag do sistema de origem (ex: 'moloni', 'pos_csv')",
                    "example": "moloni"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resultado do processamento em lote",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "created": { "type": "integer" },
                        "updated": { "type": "integer" },
                        "failed": { "type": "integer" },
                        "errors": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "index": { "type": "integer" },
                              "error": { "type": "string" }
                            }
                          }
                        }
                      }
                    },
                    "status": { "type": "string", "enum": ["ok", "partial"] }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimit" }
        }
      }
    },
    "/v1/leads/{id}": {
      "get": {
        "tags": ["Leads"],
        "summary": "Obter lead por ID",
        "operationId": "getLead",
        "parameters": [
          { "$ref": "#/components/parameters/leadId" }
        ],
        "responses": {
          "200": {
            "description": "Lead encontrada",
            "content": {
              "application/json": {
                "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Lead" } } }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      },
      "patch": {
        "tags": ["Leads"],
        "summary": "Atualizar lead (parcial)",
        "description": "Atualização parcial — apenas os campos enviados são alterados. Campos omitidos ficam inalterados.",
        "operationId": "patchLead",
        "parameters": [
          { "$ref": "#/components/parameters/leadId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/LeadPatch" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Lead atualizada",
            "content": {
              "application/json": {
                "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Lead" } } }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      },
      "delete": {
        "tags": ["Leads"],
        "summary": "Apagar lead (soft delete)",
        "description": "Move a lead para o lixo. Pode ser restaurada na UI. Dispara o evento webhook `lead.deleted`.",
        "operationId": "deleteLead",
        "parameters": [
          { "$ref": "#/components/parameters/leadId" }
        ],
        "responses": {
          "204": { "description": "Lead movida para lixo" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/leads/{id}/tasks": {
      "get": {
        "tags": ["Tasks"],
        "summary": "Listar tarefas da lead",
        "operationId": "listTasks",
        "parameters": [{ "$ref": "#/components/parameters/leadId" }],
        "responses": {
          "200": {
            "description": "Lista de tarefas",
            "content": {
              "application/json": {
                "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Task" } } } }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["Tasks"],
        "summary": "Criar tarefa na lead",
        "description": "Cria tarefa de follow-up. Caso de uso POS: após venda, criar tarefa 'Ligar ao cliente em 7 dias'.",
        "operationId": "createTask",
        "parameters": [{ "$ref": "#/components/parameters/leadId" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/TaskCreate" },
              "example": {
                "titulo": "Follow-up pós-venda",
                "descricao": "Verificar satisfação com produto X",
                "dataLimite": "2026-07-01T10:00:00Z",
                "responsavelId": "uid_vendedor"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Tarefa criada",
            "content": {
              "application/json": {
                "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Task" } } }
              }
            }
          }
        }
      }
    },
    "/v1/leads/{id}/comments": {
      "get": {
        "tags": ["Comments"],
        "summary": "Listar comentários da lead",
        "operationId": "listComments",
        "parameters": [{ "$ref": "#/components/parameters/leadId" }],
        "responses": {
          "200": {
            "description": "Lista de comentários",
            "content": {
              "application/json": {
                "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Comment" } } } }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["Comments"],
        "summary": "Criar comentário na lead",
        "description": "Adiciona nota ao historial. Caso de uso POS: 'Venda POS #12345 — Mesa 4 — 47,50€'.",
        "operationId": "createComment",
        "parameters": [{ "$ref": "#/components/parameters/leadId" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["texto"],
                "properties": {
                  "texto": { "type": "string", "maxLength": 5000, "example": "Venda POS #12345 — Mesa 4 — 47,50€ — 2026-06-22 13:45" }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Comentário criado",
            "content": {
              "application/json": {
                "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Comment" } } }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks": {
      "get": {
        "tags": ["Webhooks"],
        "summary": "Listar endpoints webhook",
        "operationId": "listWebhooks",
        "responses": {
          "200": {
            "description": "Endpoints configurados (máx. 3)",
            "content": {
              "application/json": {
                "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/WebhookEndpoint" } } } }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["Webhooks"],
        "summary": "Adicionar endpoint webhook",
        "operationId": "createWebhook",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["url"],
                "properties": {
                  "url": { "type": "string", "format": "uri", "description": "URL HTTPS do endpoint receptor" },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": ["lead.created", "lead.updated", "lead.won", "lead.deleted", "proposal.sent", "proposal.signed", "workflow.executed", "sync.conflict"]
                    },
                    "description": "Eventos a receber. Array vazio = todos os eventos."
                  },
                  "secret": { "type": "string", "description": "Secret HMAC (gerado automaticamente se omitido)" }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Endpoint adicionado",
            "content": {
              "application/json": {
                "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/WebhookEndpoint" } } }
              }
            }
          },
          "403": { "description": "Máximo de 3 webhooks atingido" }
        }
      }
    },
    "/v1/sync/clients": {
      "post": {
        "tags": ["Sync"],
        "summary": "Sincronizar clientes de gestão comercial",
        "description": "Upsert em lote de clientes vindos de Moloni, PHC, ou outro sistema. Deduplica por NIF, email, telefone e externalId.",
        "operationId": "syncClients",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["clientes"],
                "properties": {
                  "clientes": { "type": "array", "maxItems": 100, "items": { "$ref": "#/components/schemas/ClienteSync" } },
                  "source": { "type": "string", "description": "Sistema de origem", "example": "moloni" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resultado da sincronização",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "stats": { "type": "object", "properties": { "created": { "type": "integer" }, "updated": { "type": "integer" }, "skipped": { "type": "integer" } } },
                        "errors": { "type": "array", "items": { "type": "object" } }
                      }
                    },
                    "status": { "type": "string", "enum": ["ok", "partial"] }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/team": {
      "get": {
        "tags": ["Team"],
        "summary": "Listar membros da equipa",
        "description": "Útil para mapear vendedor POS → responsável CRM (via responsavelId).",
        "operationId": "listTeam",
        "responses": {
          "200": {
            "description": "Membros da equipa",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": { "type": "string" },
                          "nome": { "type": "string" },
                          "email": { "type": "string" },
                          "role": { "type": "string", "enum": ["admin", "gestor", "membro"] },
                          "ativo": { "type": "boolean" }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "wd_<hex>",
        "description": "API key no formato `wd_<36 chars hex>`. Obtida em Definições → Integrações."
      }
    },
    "parameters": {
      "leadId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "ID Firestore da lead",
        "schema": { "type": "string" }
      }
    },
    "schemas": {
      "Lead": {
        "type": "object",
        "description": "Lead/oportunidade de negócio no CRM",
        "properties": {
          "id": { "type": "string", "description": "ID Firestore" },
          "nome": { "type": "string", "description": "Nome da empresa ou cliente" },
          "tipo": { "type": "string", "description": "Tipo de lead (B2B, B2C, etc.)" },
          "pipeline": { "type": "string", "enum": ["vendas", "pos-venda", "parceiros"] },
          "statusNegocio": {
            "type": "string",
            "enum": ["Por Contactar", "Contactado", "Reunião Marcada", "Proposta Enviada", "Em Negociação", "Ganho", "Perdido", "Em Espera"]
          },
          "responsavel": { "type": "string" },
          "responsavelId": { "type": "string", "nullable": true },
          "interesse": { "type": "string", "enum": ["Baixo", "Médio", "Alto", "Urgente"] },
          "valor": { "type": "number", "description": "Valor estimado do negócio (€)" },
          "contactoNome": { "type": "string" },
          "telefone": { "type": "string" },
          "emailDireto": { "type": "string", "format": "email" },
          "nif": { "type": "string", "description": "NIF fiscal" },
          "morada": { "type": "string" },
          "instagram": { "type": "string" },
          "linkedin": { "type": "string" },
          "notas": { "type": "string" },
          "tags": { "type": "array", "items": { "type": "string" } },
          "valoresCustomizados": { "type": "object", "description": "Campos personalizados do CRM" },
          "externalId": {
            "type": "string",
            "nullable": true,
            "description": "ID no sistema externo (ex: 'POS-12345'). Chave para upsert."
          },
          "externalSource": {
            "type": "string",
            "nullable": true,
            "description": "Sistema de origem (ex: 'moloni', 'phc', 'pos_custom')"
          },
          "lastSyncedAt": { "type": "string", "format": "date-time", "nullable": true },
          "syncStatus": { "type": "string", "enum": ["ok", "pending", "error"], "nullable": true },
          "origem": { "type": "string", "description": "Como chegou ao CRM (api, widget, manual, sync_gestao_comercial)" },
          "dataRegisto": { "type": "string", "format": "date-time" },
          "dataUltimaInteracao": { "type": "string", "format": "date-time" }
        }
      },
      "LeadCreate": {
        "type": "object",
        "required": ["nome"],
        "properties": {
          "nome": { "type": "string", "minLength": 1, "maxLength": 200 },
          "tipo": { "type": "string" },
          "statusNegocio": { "type": "string" },
          "pipeline": { "type": "string" },
          "responsavel": { "type": "string" },
          "responsavelId": { "type": "string" },
          "interesse": { "type": "string" },
          "valor": { "type": "number", "minimum": 0 },
          "contactoNome": { "type": "string" },
          "telefone": { "type": "string" },
          "emailDireto": { "type": "string", "format": "email" },
          "nif": { "type": "string" },
          "morada": { "type": "string" },
          "notas": { "type": "string" },
          "tags": { "type": "array", "items": { "type": "string" } },
          "valoresCustomizados": { "type": "object" },
          "externalId": { "type": "string" },
          "externalSource": { "type": "string" }
        }
      },
      "LeadPatch": {
        "type": "object",
        "description": "Campos a atualizar (todos opcionais — merge parcial)",
        "properties": {
          "nome": { "type": "string" },
          "statusNegocio": { "type": "string" },
          "pipeline": { "type": "string" },
          "responsavel": { "type": "string" },
          "responsavelId": { "type": "string" },
          "valor": { "type": "number" },
          "telefone": { "type": "string" },
          "emailDireto": { "type": "string" },
          "nif": { "type": "string" },
          "notas": { "type": "string" },
          "tags": { "type": "array", "items": { "type": "string" } },
          "valoresCustomizados": { "type": "object" },
          "externalId": { "type": "string" },
          "externalSource": { "type": "string" },
          "syncStatus": { "type": "string", "enum": ["ok", "pending", "error"] }
        }
      },
      "Task": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "titulo": { "type": "string" },
          "descricao": { "type": "string" },
          "dataLimite": { "type": "string", "format": "date-time", "nullable": true },
          "concluida": { "type": "boolean" },
          "responsavelId": { "type": "string", "nullable": true },
          "criadoEm": { "type": "string", "format": "date-time" }
        }
      },
      "TaskCreate": {
        "type": "object",
        "required": ["titulo"],
        "properties": {
          "titulo": { "type": "string", "maxLength": 200 },
          "descricao": { "type": "string", "maxLength": 2000 },
          "dataLimite": { "type": "string", "format": "date-time" },
          "responsavelId": { "type": "string" }
        }
      },
      "Comment": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "texto": { "type": "string" },
          "autorNome": { "type": "string" },
          "autorId": { "type": "string", "nullable": true },
          "criadoEm": { "type": "string", "format": "date-time" }
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "id": { "type": "integer", "description": "Índice no array de endpoints" },
          "url": { "type": "string", "format": "uri" },
          "secretMasked": { "type": "string", "description": "Secret mascarado (últimos 4 chars)", "example": "whsec_...a3f9" },
          "events": { "type": "array", "items": { "type": "string" } },
          "active": { "type": "boolean" },
          "createdAt": { "type": "string", "format": "date-time" }
        }
      },
      "ClienteSync": {
        "type": "object",
        "required": ["nome"],
        "properties": {
          "externalId": { "type": "string" },
          "nome": { "type": "string" },
          "nif": { "type": "string" },
          "telefone": { "type": "string" },
          "email": { "type": "string" },
          "morada": { "type": "string" }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "API key inválida ou em falta",
        "content": {
          "application/json": {
            "schema": { "type": "object", "properties": { "error": { "type": "string" }, "hint": { "type": "string" } } },
            "example": { "error": "API key inválida ou em falta.", "hint": "Enviar header 'Authorization: Bearer wd_...'" }
          }
        }
      },
      "Forbidden": {
        "description": "Plano não suporta esta funcionalidade",
        "content": {
          "application/json": {
            "schema": { "type": "object", "properties": { "error": { "type": "string" } } }
          }
        }
      },
      "NotFound": {
        "description": "Recurso não encontrado",
        "content": {
          "application/json": {
            "schema": { "type": "object", "properties": { "error": { "type": "string" } } }
          }
        }
      },
      "BadRequest": {
        "description": "Dados inválidos",
        "content": {
          "application/json": {
            "schema": { "type": "object", "properties": { "error": { "type": "string" } } }
          }
        }
      },
      "RateLimit": {
        "description": "Rate limit excedido",
        "headers": {
          "Retry-After": { "schema": { "type": "integer" }, "description": "Segundos até poder tentar novamente" }
        },
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": { "type": "string" },
                "retryAfterSec": { "type": "integer" },
                "limit": { "type": "integer" },
                "windowHours": { "type": "integer" }
              }
            },
            "example": { "error": "Limite de pedidos API excedido.", "retryAfterSec": 3600, "limit": 2000, "windowHours": 1 }
          }
        }
      }
    }
  },
  "x-webhooks": {
    "lead.created": {
      "post": {
        "summary": "Lead criada",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/WebhookPayload" }
            }
          }
        }
      }
    }
  },
  "x-webhook-events": [
    "lead.created", "lead.updated", "lead.won", "lead.deleted",
    "lead.task.created", "proposal.sent", "proposal.signed",
    "workflow.executed", "sync.conflict", "webhook.test"
  ],
  "x-webhook-payload-example": {
    "event": "lead.won",
    "eventId": "lead.won:abc123",
    "empresaId": "empresa_xyz",
    "timestamp": "2026-06-22T10:00:00.000Z",
    "apiVersion": "2026-06-22",
    "data": {
      "lead": {
        "id": "abc123",
        "nome": "Restaurante Silva",
        "statusNegocio": "Ganho",
        "valor": 1500
      },
      "previous": {
        "statusNegocio": "Em Negociação"
      }
    }
  },
  "x-webhook-signature-verification": {
    "description": "Verificar assinatura HMAC-SHA256 no receptor",
    "nodeJs": "const sig = crypto.createHmac('sha256', process.env.WEBHOOK_SECRET).update(rawBody).digest('hex'); if (sig !== req.headers['x-webdone-signature']) throw new Error('Invalid signature');",
    "php": "$sig = hash_hmac('sha256', $rawBody, $_ENV['WEBHOOK_SECRET']); if ($sig !== $_SERVER['HTTP_X_WEBDONE_SIGNATURE']) { http_response_code(401); exit; }",
    "python": "import hmac, hashlib; sig = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest(); assert sig == request.headers['X-WebDone-Signature']"
  }
}
