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:
| Form | Credential | Sent 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:
| Scope | Route family | Used by |
|---|---|---|
ai | /inference-api/b2b/v1/chat/*, /retrieval/* | chat embed |
design | /inference-api/b2b/v1/design/* | editor embed |
maps | designed basemaps | the 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
tileouviewernunca carregavendor, 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_sessionquando 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 key | Effect on a session already in flight |
|---|---|
| Revoked or deleted | Rejeitada na próxima solicitação |
| A scope removed | Removido na próxima solicitação |
| Plan downgraded | Capabilities limitadas pelo tier recusam na próxima solicitação |
| Rate limit or monthly cap set to a new value | Aplica-se na próxima solicitação |
| A scope added, plan upgraded, or a limit lifted entirely | Nã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.