본문으로 건너뛰기

인증 및 scope

platform API는 조직의 key로 인증합니다. 두 가지 형태가 있습니다 — 같은 org, 같은 scopes, 다른 runtime:

FormCredentialSent as
Server (backend)kld_sk_live_…Authorization: Bearer … 또는 X-Api-Key
Publishable (browser)kld_pk_live_…SDK가 단기 session으로 교환하며 raw bearer로 직접 전송하지 않음
Authorization: Bearer kld_sk_live_…
# or
X-Api-Key: kld_sk_live_…

Server key는 backend에서 각 request마다 보내는 bearer입니다. Publishable key는 browser용입니다. SDK가 runtime에 단기 origin-bound session token으로 교환하므로 publishable key 자체가 page source에 지속적인 credential로 남지 않습니다. publishable key를 직접 bearer로 보내면 거부됩니다 — SDK를 통해 사용하세요. server key에는 CORS grant가 없으므로 page가 server key로 만든 response를 읽을 수 없습니다 — 하지만 CORS는 request가 browser 밖으로 나가는 것 자체를 막지 못하므로 page source에 server key를 넣는 순간 이미 유출된 것입니다. API가 거부할 때는 이미 늦습니다. SDK가 mount 시 kld_sk_… key를 거부하는 이유도 정확히 이것입니다. server key는 항상 server-side에 보관하세요.

기존 legacy kld_live_… key는 두 위치 모두에서 direct bearer로 계속 작동합니다.

Scopes

key에는 capability scope가 있으며 각 route family는 하나를 요구합니다:

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

기본적으로 Pro/Enterprise key는 ai, design, maps를 포함해 mint됩니다. Free-plan key는 maps + tile product로 제한됩니다. 공식 plan × scope × product 표는 API 키 받기를 참조하세요.

Effective admission = stored key × current tier. Runtime session-exchange는 매 call마다 admission을 다시 계산하므로 Pro-tier key가 Free로 downgrade되면 그 시점부터 tile session만 mint합니다 — stored key가 여전히 aidesign 제한을 가지고 있어도 같습니다. rejection subcode는 두 경우를 구분합니다:

  • insufficient_scope — key가 해당 capability를 한 번도 가진 적이 없음.
  • tier_capability_not_allowed — key가 가지고 있었지만 current tier가 더 이상 허용하지 않음. 복원하려면 plan을 upgrade하세요.

stored key의 빈 allowed_products모든 product를 의미합니다 (none이 아님). tier는 이 superset을 plan이 허용하는 범위로 다시 좁힙니다.

vendor — 의도적으로 요청하고 필요한 곳에서만 사용

vendorroute gate가 아닙니다. request의 허용/거부를 결정하는 대신, AI chat이 답변할 때 조직이 upload한 vendor data를 참조할 수 있는지 결정합니다. 다른 모든 scope가 "이 key가 이 endpoint를 호출할 수 있는가"를 답한다면 vendor는 "이 key가 내부 데이터를 바탕으로 말할 수 있는가"를 답합니다.

따라서 기본적으로 포함되지 않습니다 — mint 시 명시적으로 요청하세요:

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

어떤 key가 vendor를 가지는지가 전체 보안 결정입니다. chat embed는 browser widget입니다. publishable key를 단기 session으로 교환하므로 vendor-enabled key는 필연적으로 public page 안에 있게 됩니다. property list, 운영 시간, 공개 rate처럼 모든 방문자에게 보여도 되는 data라면 괜찮습니다. 공개하고 싶지 않은 정보에는 적합하지 않습니다. exchange를 보호하는 origin allowlist는 browser convention이지 confidentiality boundary가 아닙니다. 자체 Origin header를 설정할 수 있는 caller는 이것으로 막을 수 없습니다.

따라서:

  • surface당 하나의 key. Marketing site, demo page, customer-facing app이 같은 key를 공유해서는 안 됩니다. 자체 data로 답해야 하는 surface에만 vendor를 부여하세요.
  • rate sheet, cost basis 또는 미공개 정보를 publishable key 뒤에 두지 마세요. public page에 나오면 곤란한 답이라면 해당 data는 vendor-scoped embed에 속하지 않습니다.
  • Session은 product에 맞게 좁혀집니다. 따라서 parent key가 vendor를 갖더라도 tile 또는 viewer session은 vendor를 가지지 않습니다.

철회하려면 key scopes를 편집하거나 revoke하세요 — 두 변경 모두 다음 request부터 적용되며 이미 실행 중인 session에도 적용됩니다. session expiry까지 지연되지 않습니다.

401 vs 403

의도적으로 구분됩니다:

  • 401 Unauthorized — key 없음 / invalid / revoked / expired. key 값을 다시 확인하고 revoke되지 않았는지 확인하세요. publishable key가 raw bearer로 전송된 경우의 publishable_requires_session도 포함됩니다.
  • 403 Forbidden — key는 존재하지만 여기서는 허용되지 않음. 일반적인 subcode: insufficient_scope(처음부터 없음), tier_capability_not_allowed (있었지만 tier가 더 이상 허용하지 않음), session_origin_mismatch, ip_not_allowed(server key), server_key_in_browser, product_not_allowed.

둘 다 fail closed입니다. scope가 없는 key는 모든 곳에서 거부됩니다.

Session token

SDK가 publishable key와 교환하는 session은 다음과 같습니다:

kld_sess_{env}_{jwt}

예: kld_sess_live_eyJhbGciOi…. runtime call에서는 Authorization: Bearer 또는 X-Api-Key로 전달합니다. Default TTL 900초(15 분); server max는 30분입니다. session은 mint된 product로 좁혀지고, 모든 request는 parent key의 live policy를 다시 읽습니다.

철회는 즉시 적용되지만 새로운 grant는 그렇지 않습니다. 두 방향은 의도적으로 비대칭이며 fail closed입니다:

Change to the parent keyEffect on a session already in flight
Revoked or deleted다음 request에서 거부
A scope removed다음 request에서 제거
Plan downgradedtier-gated capability는 다음 request에서 거부
Rate limit or monthly cap set to a new value다음 request에서 적용
A scope added, plan upgraded, or a limit lifted entirely보이지 않음 — 새 session mint 필요

session은 parent보다 권한을 좁힐 수만 있고 mint된 scope를 넘어 넓힐 수 없습니다. 따라서 새 grant에는 새 exchange가 필요합니다. session이 짧은 것(default 15분)은 그 간격을 작게 유지하기 위해서입니다.

전체 status table은 Errors를 참조하세요.