{"openapi":"3.0.3","info":{"title":"MenthalHelp Revenue OS — API interna","version":"1.0.0","description":"\nSistema proprietário da Clínica Menthal Help — CRM + attribution + analytics.\n\n**Autenticação:** endpoints `/api/v1/*` exigem:\n- Header `x-tenant-id` (UUID do tenant)\n- Header `x-api-key` (API key configurada no Railway)\n\nEndpoints de webhook (`/webhooks/*`) usam secret próprio.\nEndpoints de health/ping são públicos.\n\n**Rate limit:** 100 req/min por tenant+IP (configurável via `RATE_LIMIT_MAX`).\n\n**LGPD:** toda criação/atualização de Lead exige consentimento (`consentGiven: true`)\ne registra audit log (Art. 12 Lei 13.709/2018).\n","contact":{"name":"Dev Team","email":"davitrevisan@mechelpbr.com.br"},"license":{"name":"Proprietary — Clínica Menthal Help"}},"servers":[{"url":"https://painel.clinicamenthal.com","description":"Production"},{"url":"http://localhost:3000","description":"Local dev"}],"tags":[{"name":"Health","description":"Liveness, readiness e deep health probes"},{"name":"Leads","description":"CRUD de leads com consent tracking"},{"name":"Webhooks","description":"Ingestão de eventos externos (ManyChat, UTM)"},{"name":"Analytics","description":"KPIs, attribution, cohort, forecast"},{"name":"LGPD","description":"Auditoria e direitos do titular (Art. 12, 18)"}],"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","in":"header","name":"x-api-key","description":"API key secreta — uma por tenant"},"TenantHeader":{"type":"apiKey","in":"header","name":"x-tenant-id","description":"UUID do tenant"}},"schemas":{"Lead":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"tenantId":{"type":"string","format":"uuid"},"name":{"type":"string","example":"Maria Silva"},"phone":{"type":"string","example":"+5511999999999"},"email":{"type":"string","format":"email","nullable":true},"status":{"type":"string","enum":["new","contacted","qualified","scheduled","converted","lost","no_show"]},"sourceId":{"type":"string","format":"uuid","nullable":true},"unitId":{"type":"string","format":"uuid","nullable":true},"specialtyId":{"type":"string","format":"uuid","nullable":true},"insurancePlanId":{"type":"string","format":"uuid","nullable":true},"revenue":{"type":"number","nullable":true,"example":450},"consentGiven":{"type":"boolean"},"consentDate":{"type":"string","format":"date-time","nullable":true},"consentSource":{"type":"string","nullable":true,"example":"manychat"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"convertedAt":{"type":"string","format":"date-time","nullable":true}}},"LeadCreateInput":{"type":"object","required":["name","phone","consentGiven"],"properties":{"name":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string","format":"email"},"sourceName":{"type":"string","example":"Google Ads"},"unitName":{"type":"string","example":"Unidade Centro"},"specialtyName":{"type":"string","example":"Psicologia"},"consentGiven":{"type":"boolean"},"consentSource":{"type":"string","example":"api"},"consentText":{"type":"string"},"utm":{"type":"object","properties":{"source":{"type":"string"},"medium":{"type":"string"},"campaign":{"type":"string"},"term":{"type":"string"},"content":{"type":"string"}}}}},"HealthDeep":{"type":"object","properties":{"status":{"type":"string","enum":["ok","degraded","down"]},"timestamp":{"type":"string","format":"date-time"},"uptimeSec":{"type":"integer"},"memoryMB":{"type":"object","properties":{"rss":{"type":"integer"},"heapUsed":{"type":"integer"},"heapTotal":{"type":"integer"}}},"checks":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","example":"database"},"status":{"type":"string","enum":["ok","error","skipped"]},"latencyMs":{"type":"integer","nullable":true},"message":{"type":"string","nullable":true}}}}}},"AttributionReport":{"type":"object","properties":{"dateRange":{"type":"object","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"}}},"totalConverted":{"type":"integer"},"totalRevenue":{"type":"number"},"models":{"type":"object","properties":{"firstTouch":{"type":"array","items":{"$ref":"#/components/schemas/AttributionSlice"}},"lastTouch":{"type":"array","items":{"$ref":"#/components/schemas/AttributionSlice"}},"linear":{"type":"array","items":{"$ref":"#/components/schemas/AttributionSlice"}}}}}},"AttributionSlice":{"type":"object","properties":{"source":{"type":"string","example":"Google Ads"},"leads":{"type":"number"},"revenue":{"type":"number"}}},"ErrorResponse":{"type":"object","properties":{"error":{"type":"string","example":"VALIDATION_ERROR"},"message":{"type":"string","example":"Campo name é obrigatório"}}}}},"paths":{"/ping":{"get":{"tags":["Health"],"summary":"Liveness probe","description":"Sub-5ms response pra UptimeRobot. Zero dependências.","responses":{"200":{"description":"Alive","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"ts":{"type":"integer","description":"Unix timestamp em ms"}}}}}}}}},"/health":{"get":{"tags":["Health"],"summary":"Basic health status","responses":{"200":{"description":"OK"}}}},"/health/ready":{"get":{"tags":["Health"],"summary":"Readiness probe","description":"Verifica conexão com Postgres. Retorna 503 se DB fora.","responses":{"200":{"description":"Pronto"},"503":{"description":"DB desconectado"}}}},"/health/deep":{"get":{"tags":["Health"],"summary":"Deep healthcheck","description":"Verifica DB + ClinicWeb token + Google Ads creds + Meta token. Latência reportada por serviço.","responses":{"200":{"description":"Status ok ou degraded (falhas parciais)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthDeep"}}}},"503":{"description":"DB indisponível (critical)"}}}},"/api/v1/leads":{"post":{"tags":["Leads"],"summary":"Criar lead","description":"Cria lead novo com consent tracking LGPD obrigatório. Phone-locked pra evitar dedup race-condition.","security":[{"ApiKey":[],"TenantHeader":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadCreateInput"}}}},"responses":{"201":{"description":"Lead criado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lead"}}}},"400":{"description":"Validação falhou (consentGiven=false ou campo ausente)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"API key ausente/inválida"},"429":{"description":"Rate limit excedido"}}}},"/api/v1/leads/{id}":{"get":{"tags":["Leads"],"summary":"Obter lead por ID","security":[{"ApiKey":[],"TenantHeader":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Lead encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lead"}}}},"404":{"description":"Não encontrado"}}}},"/webhooks/manychat":{"post":{"tags":["Webhooks"],"summary":"Webhook ManyChat","description":"\nRecebe External Request do ManyChat via flow. Autenticado via\nheader `x-webhook-secret` (dual-accept: `MANYCHAT_WEBHOOK_SECRET` +\n`MANYCHAT_WEBHOOK_SECRET_PREV` pra rotation).\n\nDedupe automático por telefone (phone-lock evita race em múltiplos triggers).\nPreserva enriquecimento histórico quando lead tem tag `backfill-safe`.\n        ","responses":{"200":{"description":"Processado"},"401":{"description":"Auth inválida"}}}},"/webhooks/utm-visit":{"post":{"tags":["Webhooks"],"summary":"UTM visit tracker","description":"Captura primeiro touchpoint UTM antes do lead existir (pixel/snippet na landing page).","responses":{"200":{"description":"Registrado"}}}},"/dashboard/api/attribution":{"get":{"tags":["Analytics"],"summary":"Multi-touch attribution report","description":"Retorna 3 modelos: first-touch, last-touch, linear. Filtro de data via query params.","parameters":[{"name":"from","in":"query","schema":{"type":"string","format":"date"},"example":"2026-01-01"},{"name":"to","in":"query","schema":{"type":"string","format":"date"},"example":"2026-04-22"}],"responses":{"200":{"description":"Report","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttributionReport"}}}}}}},"/dashboard/api/cohort-analysis":{"get":{"tags":["Analytics"],"summary":"Cohort retention analysis","parameters":[{"name":"months","in":"query","schema":{"type":"integer","minimum":1,"maximum":24},"example":6}],"responses":{"200":{"description":"Cohort matrix com rates 30/60/90d"}}}},"/dashboard/api/forecast":{"get":{"tags":["Analytics"],"summary":"Moving-average forecast","description":"Projeção de leads/converted/revenue pros próximos 30 dias.","responses":{"200":{"description":"Forecast com daily average"}}}},"/dashboard/api/leads/{id}/erase":{"post":{"tags":["LGPD"],"summary":"Right to erasure (Art. 18)","description":"\nExerce o direito de eliminação do titular previsto na LGPD (Art. 18 II).\nApaga PII do lead (name, phone, email, metadata) mantendo métricas agregadas.\nGera audit log imutável.\n        ","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Eliminado"},"404":{"description":"Lead não existe"}}}},"/privacy":{"get":{"tags":["LGPD"],"summary":"Política de Privacidade pública","description":"Página HTML pública com a política completa (cumprimento Art. 9º LGPD).","responses":{"200":{"description":"HTML"}}}}}}