{"openapi":"3.0.0","info":{"title":"IDeia Design — API","version":"0.1.0","description":"Produtos, cores e logos do Global Style Guide da IDeia. O painel administrativo escreve; os projetos leem. Requisitos na Fase 10 do TODO.md do repositório ideia-design-system."},"servers":[{"url":"/api","description":"Este servidor"}],"tags":[{"name":"Health","description":"Monitoração"},{"name":"Auth","description":"Login do painel (só Sysadmin, D34)"},{"name":"Products (painel)","description":"Cadastro de produtos, cores e arquivo (só Sysadmin)"},{"name":"Logos (painel)","description":"O envio dos logos para o CDN, com as versões guardadas (só Sysadmin, D32, D35)"},{"name":"Products (projetos)","description":"Leitura pública dos produtos pelos projetos (D38): sem login, CORS aberto, cache na borda"},{"name":"Footer (painel)","description":"Os textos da empresa no rodapé de todos os sistemas (só Sysadmin, D40)"},{"name":"Footer (projetos)","description":"O rodapé dos sistemas (2.21; D38, D40 a D43): sem login, CORS aberto, cache na borda"}],"paths":{"/v1/health":{"get":{"tags":["Health"],"summary":"Estado da API e do banco","security":[],"responses":{"200":{"description":"API no ar","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}},"503":{"description":"API no ar, banco fora","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/v1/auth/login":{"post":{"tags":["Auth"],"summary":"Entra no painel com e-mail e senha (só Sysadmin)","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"Autenticado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"401":{"description":"E-mail ou senha inválidos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Dados inválidos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/auth/github":{"post":{"tags":["Auth"],"summary":"Entra pelo GitHub (D34): o BFF repassa o código da volta do OAuth","description":"A API troca o código pelo token do GitHub, confere se a pessoa é membro ativo da organização e se o usuário do GitHub está cadastrado como sysadmin, e devolve o JWT. O id do GitHub fica gravado no primeiro login.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GithubLoginRequest"}}}},"responses":{"200":{"description":"Autenticado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"400":{"description":"Endereço de retorno não autorizado (GITHUB_REDIRECT_INVALID)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"O GitHub não aceitou o código (GITHUB_CODE_INVALID)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Fora da organização, não cadastrado, conta trocada ou desativado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Dados inválidos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Login pelo GitHub não configurado, ou o GitHub fora do ar","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/auth/logout":{"post":{"tags":["Auth"],"summary":"Sai do painel (o cookie é apagado pelo BFF; a API não guarda sessão)","responses":{"200":{"description":"Saiu","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OkResponse"}}}},"401":{"description":"Sem sessão","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/auth/me":{"get":{"tags":["Auth"],"summary":"Quem está autenticado","responses":{"200":{"description":"Usuário atual","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeResponse"}}}},"401":{"description":"Sem sessão","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/products":{"get":{"tags":["Products (projetos)"],"summary":"Os produtos que não estão arquivados, com cores, logos e o \"Aparece no rodapé\". Público, com cache","security":[],"responses":{"200":{"description":"Produtos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicProductListResponse"}}}}}}},"/v1/products/{slug}":{"get":{"tags":["Products (projetos)"],"summary":"Um produto. Público, com cache","security":[],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","example":"ideia-chat"}}],"responses":{"200":{"description":"Produto","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicProductResponse"}}}},"404":{"description":"Produto não encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/products/{slug}/globals.css":{"get":{"tags":["Products (projetos)"],"summary":"O globals.css do produto (shadcn/ui + Tailwind 4), o mesmo do get_tokens do MCP","security":[],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","example":"ideia-chat"}}],"responses":{"200":{"description":"CSS","content":{"text/css":{"schema":{"type":"string"}}}},"404":{"description":"Produto não encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/footer":{"get":{"tags":["Footer (projetos)"],"summary":"Os textos da empresa e os cards do rodapé, em ordem alfabética, sem o do sistema que pede. Público, com cache","security":[],"parameters":[{"name":"product","in":"query","required":false,"schema":{"type":"string","example":"ideia-chat"},"description":"O slug do sistema que pede: o card dele fica de fora (D42)"}],"responses":{"200":{"description":"Rodapé","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicFooterResponse"}}}},"422":{"description":"Slug em formato inválido","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/guide/products":{"get":{"tags":["Products (projetos)"],"summary":"Os produtos no formato do guia (packages/tokens): o que o build do site e o MCP leem (10.5). Público, com cache","description":"Os nomes de hoje do guia, em português, com o card do rodapé e as medidas de cada PNG. Sem os arquivados. O build pede com um parâmetro que muda a cada vez (?build=<hora>), para não receber a resposta guardada na borda.","security":[],"responses":{"200":{"description":"Produtos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuideProductListResponse"}}}}}}},"/v1/admin/products":{"get":{"tags":["Products (painel)"],"summary":"Lista os produtos, com busca e filtros","parameters":[{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Parte do nome ou do slug"},{"name":"family","in":"query","required":false,"schema":{"type":"string","enum":["ideia","propria"]},"description":"Família da marca"},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["ativos","arquivados","todos"],"default":"ativos"},"description":"Arquivados ou não"},{"name":"footer","in":"query","required":false,"schema":{"type":"string","enum":["true","false"]},"description":"Aparece no rodapé"},{"name":"review","in":"query","required":false,"schema":{"type":"string","enum":["true","false"]},"description":"Em revisão"}],"responses":{"200":{"description":"Produtos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminProductListResponse"}}}},"401":{"description":"Sem sessão ou sessão inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Papel sem permissão","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Products (painel)"],"summary":"Cadastra um produto. O que faltar das cores sai da receita do guia (1.2)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCreateRequest"}}}},"responses":{"201":{"description":"Criado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminProductResponse"}}}},"401":{"description":"Sem sessão ou sessão inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Papel sem permissão","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Slug já usado (SLUG_TAKEN)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Dados inválidos ou cor que reprova no contraste (CONTRAST_FAILED, com os motivos em details)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/admin/products/{slug}":{"get":{"tags":["Products (painel)"],"summary":"Um produto, com cores e logos","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","example":"ideia-chat"}}],"responses":{"200":{"description":"Produto","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminProductResponse"}}}},"401":{"description":"Sem sessão ou sessão inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Papel sem permissão","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Produto não encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"tags":["Products (painel)"],"summary":"Edita um produto: só os campos enviados mudam, e nada é recalculado por baixo","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","example":"ideia-chat"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductUpdateRequest"}}}},"responses":{"200":{"description":"Atualizado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminProductResponse"}}}},"401":{"description":"Sem sessão ou sessão inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Papel sem permissão","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Produto não encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Dados inválidos ou cor que reprova no contraste (CONTRAST_FAILED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/admin/products/{slug}/archive":{"post":{"tags":["Products (painel)"],"summary":"Arquiva um produto (sai das listas e do rodapé; os logos continuam no ar, D37)","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","example":"ideia-chat"}}],"responses":{"200":{"description":"Arquivado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminProductResponse"}}}},"401":{"description":"Sem sessão ou sessão inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Papel sem permissão","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Produto não encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/admin/products/{slug}/restore":{"post":{"tags":["Products (painel)"],"summary":"Tira um produto do arquivo","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","example":"ideia-chat"}}],"responses":{"200":{"description":"Restaurado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminProductResponse"}}}},"401":{"description":"Sem sessão ou sessão inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Papel sem permissão","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Produto não encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/admin/colors/derive":{"post":{"tags":["Products (painel)"],"summary":"Sugere a primária clara, a escura e o texto sobre elas a partir de uma cor, com o contraste do resultado","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeriveColorsRequest"}}}},"responses":{"200":{"description":"Sugestão","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeriveColorsResponse"}}}},"401":{"description":"Sem sessão ou sessão inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Papel sem permissão","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Cor inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/admin/products/{slug}/logos/{variant}/uploads":{"post":{"tags":["Logos (painel)"],"summary":"Assina o PUT para o navegador subir o PNG direto no R2 (vale 5 minutos)","description":"O navegador manda o arquivo com o método e os cabeçalhos devolvidos, e depois chama a publicação com a `key`. O arquivo só vale como logo depois de conferido pela publicação.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","example":"ideia-chat"}},{"name":"variant","in":"path","required":true,"schema":{"type":"string","enum":["clara","escura","icone-clara","icone-escura"]},"description":"A versão do logo (1.1)"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoUploadRequest"}}}},"responses":{"201":{"description":"Envio assinado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoUploadResponse"}}}},"401":{"description":"Sem sessão ou sessão inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Papel sem permissão","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Produto não encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Não é PNG, passa de 2 MB ou variante inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"R2 não configurado (STORAGE_NOT_CONFIGURED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/admin/products/{slug}/logos/{variant}":{"post":{"tags":["Logos (painel)"],"summary":"Confere o arquivo enviado e publica como versão nova (D32, D35)","description":"Confere pelo conteúdo: PNG, até 2 MB e 4096 px de lado, fundo transparente (um canto, pelo menos), ícone quadrado com 128 px ou mais, logo completo com 64 px de altura ou mais. Guarda a versão, copia para a URL fixa e limpa o cache da Cloudflare. O mesmo arquivo de uma versão guardada volta para ela, sem versão nova.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","example":"ideia-chat"}},{"name":"variant","in":"path","required":true,"schema":{"type":"string","enum":["clara","escura","icone-clara","icone-escura"]},"description":"A versão do logo (1.1)"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoPublishRequest"}}}},"responses":{"200":{"description":"Publicado (ou já era o atual: changed false)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoChangeResponse"}}}},"401":{"description":"Sem sessão ou sessão inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Papel sem permissão","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Produto não encontrado, ou o envio não chegou ao bucket ou venceu (UPLOAD_NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"O arquivo não serve (LOGO_INVALID, com os motivos em details) ou chave inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"R2 não configurado, ou a cópia para a URL fixa falhou (STORAGE_UNAVAILABLE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/admin/products/{slug}/logos/{variant}/versions/{number}/restore":{"post":{"tags":["Logos (painel)"],"summary":"Volta o logo para uma versão guardada: copia para a URL fixa e limpa o cache","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","example":"ideia-chat"}},{"name":"variant","in":"path","required":true,"schema":{"type":"string","enum":["clara","escura","icone-clara","icone-escura"]},"description":"A versão do logo (1.1)"},{"name":"number","in":"path","required":true,"schema":{"type":"integer","minimum":1,"example":1}}],"responses":{"200":{"description":"Voltou (ou já era a atual: changed false)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoChangeResponse"}}}},"401":{"description":"Sem sessão ou sessão inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Papel sem permissão","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Produto ou versão não encontrados (LOGO_VERSION_NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"O arquivo da versão não está no bucket (LOGO_FILE_MISSING)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"R2 não configurado, ou a cópia para a URL fixa falhou","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/admin/footer":{"get":{"tags":["Footer (painel)"],"summary":"Os textos da empresa, todos os cards (para a prévia) e quem mudou por último","responses":{"200":{"description":"Rodapé","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminFooterResponse"}}}},"401":{"description":"Sem sessão ou sessão inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Papel sem permissão","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"tags":["Footer (painel)"],"summary":"Edita os textos da empresa (só os campos enviados)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FooterUpdateRequest"}}}},"responses":{"200":{"description":"Atualizado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminFooterResponse"}}}},"401":{"description":"Sem sessão ou sessão inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Papel sem permissão","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Dados inválidos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"ErrorResponse":{"type":"object","required":["success","error","metadata"],"properties":{"success":{"type":"boolean","example":false},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","example":"VALIDATION_ERROR"},"message":{"type":"string","example":"Dados inválidos"},"details":{"nullable":true}}},"metadata":{"type":"object","required":["timestamp","version"],"properties":{"timestamp":{"type":"string","format":"date-time"},"version":{"type":"string","example":"v1"}}}}},"HealthResponse":{"type":"object","required":["success","data","metadata"],"properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","required":["status","uptime","database","timestamp"],"properties":{"status":{"type":"string","enum":["ok","degraded"]},"uptime":{"type":"integer","example":42},"database":{"type":"string","enum":["up","down"]},"timestamp":{"type":"string","format":"date-time"}}},"metadata":{"type":"object","required":["timestamp","version"],"properties":{"timestamp":{"type":"string","format":"date-time"},"version":{"type":"string","example":"v1"}}}}},"OkResponse":{"type":"object","required":["success","data","metadata"],"properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean","example":true}}},"metadata":{"type":"object","required":["timestamp","version"],"properties":{"timestamp":{"type":"string","format":"date-time"},"version":{"type":"string","example":"v1"}}}}},"LoginRequest":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email","example":"pessoa@ideiabusiness.com.br"},"password":{"type":"string","format":"password"}}},"GithubLoginRequest":{"type":"object","required":["code","redirectUri"],"properties":{"code":{"type":"string","description":"O código da volta do GitHub"},"redirectUri":{"type":"string","format":"uri","example":"https://design-admin.ideiabusiness.com.br/api/auth/github/callback"}}},"AuthenticatedUser":{"type":"object","required":["id","email","name","role","githubLogin"],"properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"},"name":{"type":"string"},"role":{"type":"string","enum":["sysadmin"]},"githubLogin":{"type":"string","nullable":true}}},"LoginResponse":{"type":"object","required":["success","data","metadata"],"properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","required":["token","expiresIn","user"],"properties":{"token":{"type":"string","description":"O JWT: o BFF guarda num cookie httpOnly"},"expiresIn":{"type":"string","example":"8h"},"user":{"$ref":"#/components/schemas/AuthenticatedUser"}}},"metadata":{"type":"object","required":["timestamp","version"],"properties":{"timestamp":{"type":"string","format":"date-time"},"version":{"type":"string","example":"v1"}}}}},"MeResponse":{"type":"object","required":["success","data","metadata"],"properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/AuthenticatedUser"},"metadata":{"type":"object","required":["timestamp","version"],"properties":{"timestamp":{"type":"string","format":"date-time"},"version":{"type":"string","example":"v1"}}}}},"PrimaryColors":{"type":"object","required":["light","lightForeground","dark","darkForeground"],"properties":{"light":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261"},"lightForeground":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261"},"dark":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261"},"darkForeground":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261"}}},"LinkColors":{"type":"object","required":["light","dark"],"description":"O texto de link de cada tema (D18), calculado","properties":{"light":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261"},"dark":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261"}}},"ProductColors":{"type":"object","required":["primary","brand","link"],"properties":{"primary":{"$ref":"#/components/schemas/PrimaryColors"},"brand":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261"},"link":{"$ref":"#/components/schemas/LinkColors"}}},"AdminProductColors":{"type":"object","required":["primary","brand","link","logo","support","supportShare"],"properties":{"primary":{"$ref":"#/components/schemas/PrimaryColors"},"brand":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261"},"link":{"$ref":"#/components/schemas/LinkColors"},"logo":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261"},"support":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261","nullable":true},"supportShare":{"type":"number","minimum":0,"maximum":1,"nullable":true}}},"ProductLogo":{"type":"object","required":["variant","format","url","width","height","bytes","sha256","version","uploadedAt"],"properties":{"variant":{"type":"string","enum":["clara","escura","icone-clara","icone-escura"]},"format":{"type":"string","enum":["png","svg"]},"url":{"type":"string","format":"uri","description":"A URL fixa no CDN, com ?v= da versão atual (D32)"},"width":{"type":"integer"},"height":{"type":"integer"},"bytes":{"type":"integer"},"sha256":{"type":"string"},"version":{"type":"integer"},"uploadedAt":{"type":"string","format":"date-time"}}},"LogoVersion":{"type":"object","required":["number","url","width","height","bytes","sha256","uploadedAt","uploadedBy","current"],"properties":{"number":{"type":"integer"},"url":{"type":"string","format":"uri","description":"A URL da versão no CDN: nunca muda de conteúdo"},"width":{"type":"integer"},"height":{"type":"integer"},"bytes":{"type":"integer"},"sha256":{"type":"string"},"uploadedAt":{"type":"string","format":"date-time"},"uploadedBy":{"type":"string","description":"Quem enviou; vazio na carga inicial","nullable":true},"current":{"type":"boolean","description":"É a que está na URL fixa"}}},"AdminProductLogo":{"type":"object","required":["variant","format","url","width","height","bytes","sha256","version","uploadedAt","versions"],"properties":{"variant":{"type":"string","enum":["clara","escura","icone-clara","icone-escura"]},"format":{"type":"string","enum":["png","svg"]},"url":{"type":"string","format":"uri","description":"A URL fixa no CDN, com ?v= da versão atual (D32)"},"width":{"type":"integer"},"height":{"type":"integer"},"bytes":{"type":"integer"},"sha256":{"type":"string"},"version":{"type":"integer"},"uploadedAt":{"type":"string","format":"date-time"},"versions":{"type":"array","items":{"$ref":"#/components/schemas/LogoVersion"},"description":"Da mais nova para a mais antiga"}}},"LogoUploadRequest":{"type":"object","required":["contentType","bytes"],"additionalProperties":false,"properties":{"contentType":{"type":"string","enum":["image/png"]},"bytes":{"type":"integer","minimum":1,"maximum":2097152,"description":"O tamanho do arquivo: vai assinado"}}},"LogoUpload":{"type":"object","required":["key","url","method","headers","expiresAt"],"properties":{"key":{"type":"string","example":"logos/_envios/0b8e6f5c-2a43-4c1e-9d55-7f1f7d5d2c11","description":"O que vai na publicação"},"url":{"type":"string","format":"uri","description":"O PUT assinado no R2"},"method":{"type":"string","enum":["PUT"]},"headers":{"type":"object","additionalProperties":{"type":"string"},"example":{"Content-Type":"image/png"}},"expiresAt":{"type":"string","format":"date-time"}}},"LogoUploadResponse":{"type":"object","required":["success","data","metadata"],"properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/LogoUpload"},"metadata":{"type":"object","required":["timestamp","version"],"properties":{"timestamp":{"type":"string","format":"date-time"},"version":{"type":"string","example":"v1"}}}}},"LogoPublishRequest":{"type":"object","required":["key"],"additionalProperties":false,"properties":{"key":{"type":"string","example":"logos/_envios/0b8e6f5c-2a43-4c1e-9d55-7f1f7d5d2c11"}}},"LogoChange":{"type":"object","required":["product","changed","cachePurged"],"properties":{"product":{"$ref":"#/components/schemas/AdminProduct"},"changed":{"type":"boolean","description":"false: o arquivo já era o atual"},"cachePurged":{"type":"boolean","description":"Se o cache da Cloudflare da URL fixa foi limpo"}}},"LogoChangeResponse":{"type":"object","required":["success","data","metadata"],"properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/LogoChange"},"metadata":{"type":"object","required":["timestamp","version"],"properties":{"timestamp":{"type":"string","format":"date-time"},"version":{"type":"string","example":"v1"}}}}},"AdminProduct":{"type":"object","required":["id","slug","name","family","pitch","description","footerDescription","siteUrl","colors","inReview","note","showInFooter","archivedAt","logos","createdAt","updatedAt","updatedBy"],"properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string","example":"ideia-chat"},"name":{"type":"string","maxLength":120,"example":"IDeia Chat"},"family":{"type":"string","enum":["ideia","propria"]},"pitch":{"type":"string","maxLength":160,"description":"O título do painel do login (2.20)"},"description":{"type":"string","maxLength":300,"description":"O texto do painel do login (2.20)"},"footerDescription":{"type":"string","maxLength":300,"description":"O texto do card do rodapé (2.21)"},"siteUrl":{"type":"string","format":"uri","example":"https://chat.ideiabusiness.com.br","nullable":true},"inReview":{"type":"boolean","description":"Em revisão"},"showInFooter":{"type":"boolean","description":"Aparece no rodapé"},"note":{"type":"string","maxLength":500,"description":"Observação interna","nullable":true},"colors":{"$ref":"#/components/schemas/AdminProductColors"},"archivedAt":{"type":"string","format":"date-time","nullable":true},"logos":{"type":"array","items":{"$ref":"#/components/schemas/AdminProductLogo"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"}},"nullable":true}}},"AdminProductResponse":{"type":"object","required":["success","data","metadata"],"properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/AdminProduct"},"metadata":{"type":"object","required":["timestamp","version"],"properties":{"timestamp":{"type":"string","format":"date-time"},"version":{"type":"string","example":"v1"}}}}},"AdminProductListResponse":{"type":"object","required":["success","data","metadata"],"properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminProduct"}},"metadata":{"type":"object","required":["timestamp","version"],"properties":{"timestamp":{"type":"string","format":"date-time"},"version":{"type":"string","example":"v1"}}}}},"ProductColorsInput":{"type":"object","required":["primary"],"additionalProperties":false,"properties":{"primary":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261","description":"A primária do tema claro; a marca a acompanha"},"primaryDark":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261","description":"A primária do tema escuro (na criação, calculada se faltar)"},"lightForeground":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261","description":"O texto sobre a primária clara (na criação, calculado se faltar)"},"darkForeground":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261","description":"O texto sobre a primária escura (na criação, calculado se faltar)"},"logo":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261","description":"A cor principal do logo (na criação, a primária se faltar)"},"support":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261","description":"A secundária: a cor de apoio do logo","nullable":true}}},"ProductCreateRequest":{"type":"object","required":["slug","name","family","colors"],"additionalProperties":false,"properties":{"slug":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$","minLength":2,"maxLength":64,"description":"Não muda depois de criado"},"name":{"type":"string","maxLength":120,"example":"IDeia Chat"},"family":{"type":"string","enum":["ideia","propria"]},"pitch":{"type":"string","maxLength":160,"description":"O título do painel do login (2.20)"},"description":{"type":"string","maxLength":300,"description":"O texto do painel do login (2.20)"},"footerDescription":{"type":"string","maxLength":300,"description":"O texto do card do rodapé (2.21)"},"siteUrl":{"type":"string","format":"uri","example":"https://chat.ideiabusiness.com.br","nullable":true},"inReview":{"type":"boolean","description":"Em revisão"},"showInFooter":{"type":"boolean","description":"Aparece no rodapé"},"note":{"type":"string","maxLength":500,"description":"Observação interna","nullable":true},"colors":{"$ref":"#/components/schemas/ProductColorsInput"}}},"ProductUpdateRequest":{"type":"object","additionalProperties":false,"description":"Só os campos enviados mudam. O slug não muda.","properties":{"name":{"type":"string","maxLength":120,"example":"IDeia Chat"},"family":{"type":"string","enum":["ideia","propria"]},"pitch":{"type":"string","maxLength":160,"description":"O título do painel do login (2.20)"},"description":{"type":"string","maxLength":300,"description":"O texto do painel do login (2.20)"},"footerDescription":{"type":"string","maxLength":300,"description":"O texto do card do rodapé (2.21)"},"siteUrl":{"type":"string","format":"uri","example":"https://chat.ideiabusiness.com.br","nullable":true},"inReview":{"type":"boolean","description":"Em revisão"},"showInFooter":{"type":"boolean","description":"Aparece no rodapé"},"note":{"type":"string","maxLength":500,"description":"Observação interna","nullable":true},"colors":{"type":"object","additionalProperties":false,"properties":{"primary":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261","description":"A primária do tema claro; a marca a acompanha"},"primaryDark":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261","description":"A primária do tema escuro (na criação, calculada se faltar)"},"lightForeground":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261","description":"O texto sobre a primária clara (na criação, calculado se faltar)"},"darkForeground":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261","description":"O texto sobre a primária escura (na criação, calculado se faltar)"},"logo":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261","description":"A cor principal do logo (na criação, a primária se faltar)"},"support":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261","description":"A secundária: a cor de apoio do logo","nullable":true}}}}},"GuideProduct":{"type":"object","required":["slug","nome","familia","logo","primary","brand","logos","arquivos","revisar","pitch","descricao","rodape","medidas"],"description":"O Product do packages/tokens, como o guia sempre leu","properties":{"slug":{"type":"string","example":"ideia-chat"},"nome":{"type":"string","example":"IDeia Chat"},"familia":{"type":"string","enum":["ideia","propria"]},"logo":{"type":"object","required":["cor","apoio","parcela"],"properties":{"cor":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261"},"apoio":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261","nullable":true},"parcela":{"type":"number","minimum":0,"maximum":1,"nullable":true}}},"primary":{"$ref":"#/components/schemas/PrimaryColors"},"brand":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261"},"logos":{"type":"object","additionalProperties":{"type":"string","format":"uri"},"description":"A URL fixa de cada variante, sem ?v="},"arquivos":{"type":"object","additionalProperties":{"type":"boolean"},"description":"Quais variantes têm arquivo"},"revisar":{"type":"boolean","description":"Em revisão"},"pitch":{"type":"string"},"descricao":{"type":"string"},"rodape":{"type":"object","required":["descricao","url","aparece"],"properties":{"descricao":{"type":"string"},"url":{"type":"string","format":"uri","nullable":true},"aparece":{"type":"boolean"}}},"medidas":{"type":"object","additionalProperties":{"type":"object","required":["largura","altura","bytes"],"properties":{"largura":{"type":"integer"},"altura":{"type":"integer"},"bytes":{"type":"integer"}}}}}},"GuideProductListResponse":{"type":"object","required":["success","data","metadata"],"properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/GuideProduct"}},"metadata":{"type":"object","required":["timestamp","version"],"properties":{"timestamp":{"type":"string","format":"date-time"},"version":{"type":"string","example":"v1"}}}}},"PublicProduct":{"type":"object","required":["slug","name","family","pitch","description","footerDescription","siteUrl","showInFooter","colors","logos"],"properties":{"slug":{"type":"string","example":"ideia-chat"},"name":{"type":"string","maxLength":120,"example":"IDeia Chat"},"family":{"type":"string","enum":["ideia","propria"]},"pitch":{"type":"string","maxLength":160,"description":"O título do painel do login (2.20)"},"description":{"type":"string","maxLength":300,"description":"O texto do painel do login (2.20)"},"footerDescription":{"type":"string","maxLength":300,"description":"O texto do card do rodapé (2.21)"},"siteUrl":{"type":"string","format":"uri","example":"https://chat.ideiabusiness.com.br","nullable":true},"showInFooter":{"type":"boolean","description":"Aparece no rodapé"},"colors":{"$ref":"#/components/schemas/ProductColors"},"logos":{"type":"object","description":"As URLs dos PNGs que existem, por variante, com ?v=","properties":{"clara":{"type":"string","format":"uri"},"escura":{"type":"string","format":"uri"},"icone-clara":{"type":"string","format":"uri"},"icone-escura":{"type":"string","format":"uri"}}}}},"PublicProductResponse":{"type":"object","required":["success","data","metadata"],"properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/PublicProduct"},"metadata":{"type":"object","required":["timestamp","version"],"properties":{"timestamp":{"type":"string","format":"date-time"},"version":{"type":"string","example":"v1"}}}}},"PublicProductListResponse":{"type":"object","required":["success","data","metadata"],"properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicProduct"}},"metadata":{"type":"object","required":["timestamp","version"],"properties":{"timestamp":{"type":"string","format":"date-time"},"version":{"type":"string","example":"v1"}}}}},"FooterCompany":{"type":"object","required":["brandName","legalName","cnpj","email","title","tagline","aboutUrl"],"properties":{"brandName":{"type":"string","maxLength":60,"example":"Ideia Business","description":"\"Um produto da Ideia Business\" e o nome da seta"},"legalName":{"type":"string","maxLength":120,"example":"Ideia Business Soluções Tecnológicas LTDA"},"cnpj":{"type":"string","example":"30.082.914/0001-00","description":"Com ou sem a máscara; sai com ela. Vale o alfanumérico da Receita"},"email":{"type":"string","format":"email","maxLength":254,"example":"contato@ideiabusiness.com.br"},"title":{"type":"string","maxLength":80,"example":"Os negócios por trás deste produto"},"tagline":{"type":"string","maxLength":240,"description":"A frase da empresa, embaixo do título"},"aboutUrl":{"type":"string","format":"uri","example":"https://ideiabusiness.com.br","description":"Para onde o \"Quem está por trás\" leva (https)"}}},"FooterProduct":{"type":"object","required":["slug","name","description","url","icon"],"properties":{"slug":{"type":"string","example":"ideia-chat"},"name":{"type":"string","example":"IDeia Chat"},"description":{"type":"string","description":"O texto do rodapé do cadastro; vazio, o texto do login (D43)"},"url":{"type":"string","format":"uri","nullable":true,"description":"Para onde o card leva, em outra aba"},"icon":{"type":"string","format":"uri","nullable":true,"description":"O ícone (32px) com ?v=: o ícone claro ou, sem ele, o logo claro"}}},"PublicFooter":{"type":"object","required":["company","products"],"properties":{"company":{"$ref":"#/components/schemas/FooterCompany"},"products":{"type":"array","items":{"$ref":"#/components/schemas/FooterProduct"},"description":"Em ordem alfabética (D41)"}}},"PublicFooterResponse":{"type":"object","required":["success","data","metadata"],"properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/PublicFooter"},"metadata":{"type":"object","required":["timestamp","version"],"properties":{"timestamp":{"type":"string","format":"date-time"},"version":{"type":"string","example":"v1"}}}}},"AdminFooterResponse":{"type":"object","required":["success","data","metadata"],"properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","required":["company","products","updatedAt","updatedBy"],"properties":{"company":{"$ref":"#/components/schemas/FooterCompany"},"products":{"type":"array","items":{"$ref":"#/components/schemas/FooterProduct"}},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"}},"nullable":true}}},"metadata":{"type":"object","required":["timestamp","version"],"properties":{"timestamp":{"type":"string","format":"date-time"},"version":{"type":"string","example":"v1"}}}}},"FooterUpdateRequest":{"type":"object","additionalProperties":false,"description":"Só os campos enviados mudam.","properties":{"brandName":{"type":"string","maxLength":60,"example":"Ideia Business","description":"\"Um produto da Ideia Business\" e o nome da seta"},"legalName":{"type":"string","maxLength":120,"example":"Ideia Business Soluções Tecnológicas LTDA"},"cnpj":{"type":"string","example":"30.082.914/0001-00","description":"Com ou sem a máscara; sai com ela. Vale o alfanumérico da Receita"},"email":{"type":"string","format":"email","maxLength":254,"example":"contato@ideiabusiness.com.br"},"title":{"type":"string","maxLength":80,"example":"Os negócios por trás deste produto"},"tagline":{"type":"string","maxLength":240,"description":"A frase da empresa, embaixo do título"},"aboutUrl":{"type":"string","format":"uri","example":"https://ideiabusiness.com.br","description":"Para onde o \"Quem está por trás\" leva (https)"}}},"DeriveColorsRequest":{"type":"object","required":["color"],"additionalProperties":false,"properties":{"color":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261","description":"A cor do logo (ou a que o sysadmin quer de primária)"}}},"ContrastFailure":{"type":"object","required":["mode","fg","color","bg","onto","ratio","min","what","message"],"properties":{"mode":{"type":"string","enum":["light","dark"]},"fg":{"type":"string","description":"O token que reprovou","example":"primary"},"color":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261"},"bg":{"type":"string","description":"A superfície","example":"card"},"onto":{"type":"string","pattern":"^#[0-9a-f]{6}$","example":"#153261"},"ratio":{"type":"number","example":2.1},"min":{"type":"number","example":3},"what":{"type":"string","example":"primária, ícone e foco"},"message":{"type":"string"}}},"DeriveColorsResponse":{"type":"object","required":["success","data","metadata"],"properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","required":["primary","link","failures"],"properties":{"primary":{"$ref":"#/components/schemas/PrimaryColors"},"link":{"$ref":"#/components/schemas/LinkColors"},"failures":{"type":"array","items":{"$ref":"#/components/schemas/ContrastFailure"}}}},"metadata":{"type":"object","required":["timestamp","version"],"properties":{"timestamp":{"type":"string","format":"date-time"},"version":{"type":"string","example":"v1"}}}}}}},"security":[{"bearerAuth":[]}]}