Pular para o conteúdo principal

Autenticação e escopos

A platform API autentica usando uma chave da sua organização. Ela existe em duas formas — mesma organização, mesmos scopes, runtime diferente:

FormCredentialSent as
Server (backend)kld_sk_live_…Authorization: Bearer … ou X-Api-Key
Publishable (browser)kld_pk_live_…trocada pelo SDK por uma sessão de curta duração; nunca enviada como raw bearer
Authorization: Bearer kld_sk_live_…
# or
X-Api-Key: kld_sk_live_…

Server keys são o bearer enviado pelo seu backend em cada solicitação. Publishable keys são para o navegador: o SDK troca uma por um session token de curta duração vinculado à origin em runtime, portanto a publishable key nunca permanece como uma credential permanente no código-fonte da página. Uma publishable key enviada diretamente como bearer é rejeitada — use-a pelo SDK. Uma server key não recebe nenhuma permissão CORS, portanto uma página nunca pode ler uma resposta feita com ela — mas CORS não consegue impedir a solicitação de sair do navegador, de modo que uma server key inserida no código fonte já vazou quando a API a recusa. É exatamente por isso que o SDK rejeita chaves kld_sk_… no mount: mantenha as server keys sempre no server-side.

As chaves legadas kld_live_… existentes continuam funcionando como direct bearer nos dois lugares.

Escopos

Uma chave contém capability scopes; cada família de rotas exige um:

ScopeRoute familyUsed by
ai/inference-api/b2b/v1/chat/*, /retrieval/*chat embed
design/inference-api/b2b/v1/design/*editor embed
mapsdesigned basemapsthe tile embed
vendor(modifier, not a route)the chat embed, on your own data

Por padrão, uma chave Pro/Enterprise é criada com ai, design e maps. Chaves do plano Free são restritas a maps + o produto tile; consulte Obter uma chave de API para a tabela canônica plan × scope × produto.

Admissão efetiva = chave armazenada × tier atual. O session-exchange em runtime recalcula a admissão em cada chamada, então uma chave Pro-tier rebaixada para Free passa a criar somente sessões tile daquele momento em diante — mesmo se a chave armazenada ainda contiver ai e design nas suas restrições. O subcode de rejeição diferencia os dois casos:

  • insufficient_scope — a chave nunca teve a capacidade.
  • tier_capability_not_allowed — a chave tinha, mas o tier atual não permite mais. Faça upgrade do plano para restaurar.

Um allowed_products vazio na chave armazenada significa todos os produtos (não nenhum). O tier continua restringindo esse conjunto ao que o plano permite.

vendor — solicite deliberadamente e apenas onde realmente precisar

vendor não é um gate de rota. Ele não admite nem rejeita uma solicitação; decide se o chat de AI pode consultar os dados vendor enviados pela sua organização ao responder. Todos os demais scopes respondem "esta chave pode chamar este endpoint?"; vendor responde "esta chave pode falar em nome dos nossos dados internos?".

Por isso ele nunca é incluído por padrão — solicite explicitamente no mint:

{ "name": "our-site-widget", "scopes": ["ai", "vendor"] }

Qual chave carrega vendor é toda a decisão de segurança. O chat embed é um widget de navegador: ele troca uma publishable key por uma sessão de curta duração, então uma chave habilitada para vendor necessariamente fica em uma página pública. Isso é aceitável para dados que você não se importa em mostrar a todo visitante do site — lista de propriedades, horários, tarifas públicas. Não é aceitável para qualquer coisa que você não publicaria. A origin allowlist que protege a troca é uma convenção do navegador, não uma fronteira de confidencialidade: um cliente que define seu próprio header Origin não é impedido por ela.

Portanto:

  • Uma chave por superfície. Um site de marketing, uma página de demonstração e seu aplicativo para clientes não devem compartilhar uma chave. Apenas a superfície que precisa responder com seus dados recebe vendor.
  • Nunca coloque uma tabela de preços, base de custos ou qualquer coisa não publicada atrás de uma publishable key. Se a resposta causaria constrangimento em uma página pública, os dados não pertencem a um embed com scope vendor.
  • As sessões são restringidas ao produto, portanto uma sessão tile ou viewer nunca carrega vendor, mesmo se a parent key possuir esse scope.

Para removê-lo, edite os scopes da chave ou revogue-a — ambos entram em vigor na próxima solicitação, inclusive em sessões já em andamento. A remoção não é adiada até a expiração da sessão.

401 vs 403

Eles são distintos de propósito:

  • 401 Unauthorized — chave ausente / inválida / revogada / expirada. Verifique novamente o valor da chave e se ela não foi revogada. Também publishable_requires_session quando uma publishable key é enviada como raw bearer.
  • 403 Forbidden — a chave existe, mas não é permitida aqui. Subcodes comuns: insufficient_scope (nunca teve), tier_capability_not_allowed (tinha, mas o tier não permite mais), session_origin_mismatch, ip_not_allowed (server keys), server_key_in_browser, product_not_allowed.

Ambos falham de forma fechada: uma chave sem scopes é negada em todos os lugares.

Session tokens

A sessão pela qual o SDK troca uma publishable key tem este formato:

kld_sess_{env}_{jwt}

por exemplo kld_sess_live_eyJhbGciOi…. Apresentada em chamadas de runtime como Authorization: Bearer ou X-Api-Key. TTL padrão de 900 segundos (15 minutos); máximo do servidor de 30 minutos. As sessões são restringidas ao produto para o qual foram criadas, e cada solicitação relê a live policy da parent key.

A remoção é imediata; uma nova concessão não. As duas direções são propositalmente assimétricas e fail closed:

Change to the parent keyEffect on a session already in flight
Revoked or deletedRejeitada na próxima solicitação
A scope removedRemovido na próxima solicitação
Plan downgradedCapabilities limitadas pelo tier recusam na próxima solicitação
Rate limit or monthly cap set to a new valueAplica-se na próxima solicitação
A scope added, plan upgraded, or a limit lifted entirelyNão visível — crie uma nova sessão

Uma sessão só pode restringir seu parent, nunca ampliar além dos scopes com os quais foi criada, portanto uma nova concessão precisa de uma nova troca. As sessões são curtas (15 minutos por padrão) exatamente para manter essa lacuna pequena.

Consulte Errors para a tabela completa de status.