跳到正文
Indiana
控制台
章节目录 / 服务端 API Key 鉴权

服务端 API Key 鉴权

创建服务端 Key,配置权限并处理撤销与轮换。

API v1更新于

先分清三种身份

服务端 API 调用使用 gg_sk_ API Key。Key 固定到一个 App 和环境,Indiana 按权限验证每次请求。服务端读取或写入客户数据时,不需要先签发 Customer Token。

谁持有什么凭据
调用方凭据用途
企业后端INDIANA_SECRET_KEY / gg_sk_在 App/环境及权限内调用 Indiana
企业客户端customerToken / gg_ct_直接调用允许的客户接口;由企业后端签发
Indiana 控制台成员Supabase Auth登录控制台、创建 App 和 Key

谁持有什么凭据

  • 企业后端

    凭据
    INDIANA_SECRET_KEY / gg_sk_
    用途
    在 App/环境及权限内调用 Indiana
  • 企业客户端

    凭据
    customerToken / gg_ct_
    用途
    直接调用允许的客户接口;由企业后端签发
  • Indiana 控制台成员

    凭据
    Supabase Auth
    用途
    登录控制台、创建 App 和 Key

Customer 是 Indiana 的业务记录,通过 App、环境和 externalUserId 关联你产品里的用户;它不共用控制台的账号密码。企业自己的登录系统负责确认用户身份,Indiana Key 不证明用户已登录。本文先完成只用 Key 的服务端接入,再说明客户端会话。

1. 创建 Sandbox 服务端 Key

  • 使用对当前 App 有 apiKeys:write 权限的 Owner、Admin 或 Developer 登录,在 SDK 接入页选择 App 和 Sandbox。
  • 创建 Key,仅勾选本次首次请求需要的 customers:write。Key 原文只显示一次,立即保存到自己的后端配置。
  • 同页复制 base URL 和 App ID。base URL 填站点根地址,不追加 /api/v1。纯服务端请求无需配置浏览器来源。

Key 固定到一个 App/环境,有效期为 1–365 天。修改权限或到期时间时创建替代 Key;每个 App/环境最多保留 20 把有效 Key。

2. 配置并验证 Key

使用 Node.js 22+ 和原生 fetch 验证 Key,无需安装 SDK。此处只验证服务端身份;快速开始继续讲解 Customer 会话和 Web 读取。

在应用根目录保存被 Git 忽略的 .env.local,把下面的站点根地址、App ID 和 Key 换成 SDK 接入页里的值。INDIANA_SECRET_KEY 只在后端读取,不加 NEXT_PUBLIC_ 前缀。

在同一目录新建 indiana-server-check.mjs,完整复制下面代码。脚本会创建并保留一个 Sandbox 测试客户;不会签发客户 Token;符合资格的免费默认 Product 可以初始化,付费 Product/trial 必须走独立 billing。

首次请求完成

成功输出包含 requestId、Sandbox scope 和 customerId。重复运行返回同一 customerId,created 变为 false,表示找到了已有 Customer。

实际业务中,把固定测试 externalUserId 换成后端已经验证或业务任务已经授权的稳定用户 ID。不要把浏览器提交的 customerId 或用户 ID 直接当作授权。

.env.localShell
# 从 SDK 接入页复制 Sandbox 配置;此文件须被 Git 忽略。INDIANA_BASE_URL=https://your-indiana-host.exampleINDIANA_PROJECT_ID=your-app-idINDIANA_SECRET_KEY=your-sandbox-server-key
indiana-server-check.mjsJavaScript
class ConfigurationError extends Error {}class RequestError extends Error {  constructor(code, status, requestId) { super(code); this.code = code; this.status = status; this.requestId = requestId; }}function required(name) {  const value = process.env[name];  if (!value) throw new ConfigurationError("Missing environment variable: " + name);  return value;}try {  const baseUrl = required("INDIANA_BASE_URL");  const key = required("INDIANA_SECRET_KEY");  const projectId = required("INDIANA_PROJECT_ID");  const response = await fetch(new URL("/api/v1/customers", baseUrl).href, {    method: "POST", redirect: "error", signal: AbortSignal.timeout(10000),    headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json",      "X-Indiana-Project": projectId, "X-Indiana-Environment": "sandbox" },    body: JSON.stringify({ externalUserId: "docs-server-auth-check" }),  });  const body = await response.json();  if (!response.ok) throw new RequestError(body.error?.code ?? "HTTP_ERROR", response.status, body.requestId);  const { requestId, scope, customer, created } = body;  console.log(JSON.stringify({ requestId, scope, customerId: customer.id, created }, null, 2));} catch (error) {  if (error instanceof RequestError) console.error(JSON.stringify({ code: error.code, status: error.status, requestId: error.requestId }));  else if (error instanceof ConfigurationError) console.error(error.message);  else console.error("请求未完成。检查 Node 版本、环境文件和网络配置。");  process.exitCode = 1;}
TerminalShell
node --env-file=.env.local indiana-server-check.mjs
成功输出示意JSON
{  "requestId": "<request-id>",  "scope": {    "projectId": "<your-app-id>",    "environment": "sandbox"  },  "customerId": "<customer-id>",  "created": true}

用 curl 验证同一个请求

HTTP 与 SDK 使用相同 Key 和权限。先在终端环境中设置 INDIANA_BASE_URL、INDIANA_PROJECT_ID、INDIANA_SECRET_KEY,再执行以下命令;Node 的 --env-file 不会替终端导入变量。

成功返回 HTTP 200,JSON 包含 requestId、scope、created 和 customer。使用相同 App、环境和 externalUserId 重试返回原客户。

TerminalShell
curl -X POST "$INDIANA_BASE_URL/api/v1/customers" \  -H "Authorization: Bearer $INDIANA_SECRET_KEY" \  -H "X-Indiana-Project: $INDIANA_PROJECT_ID" \  -H "X-Indiana-Environment: sandbox" \  -H "Content-Type: application/json" \  -d '{"externalUserId":"docs-server-auth-check"}'

3. 按需要增加权限

常用 SDK 方法与权限
SDK 方法HTTP凭据权限
getOrCreateCustomerPOST /api/v1/customers服务端 Keycustomers:write
getCustomerPOST /api/v1/customers/get服务端 Keycustomers:read
readHoldingsPOST /api/v1/customer-holdings服务端 Key / 客户 Tokenentitlements:read
mutateBalancePOST /api/v1/balances/execute服务端 Keycredits:consume
createCustomerSessionPOST /api/v1/customer-sessions服务端 Keysessions:create

常用 SDK 方法与权限

  • getOrCreateCustomer

    HTTP
    POST /api/v1/customers
    凭据
    服务端 Key
    权限
    customers:write
  • getCustomer

    HTTP
    POST /api/v1/customers/get
    凭据
    服务端 Key
    权限
    customers:read
  • readHoldings

    HTTP
    POST /api/v1/customer-holdings
    凭据
    服务端 Key / 客户 Token
    权限
    entitlements:read
  • mutateBalance

    HTTP
    POST /api/v1/balances/execute
    凭据
    服务端 Key
    权限
    credits:consume
  • createCustomerSession

    HTTP
    POST /api/v1/customer-sessions
    凭据
    服务端 Key
    权限
    sessions:create

SDK 的 scope 应与 Key 所属 App/环境相同。X-Indiana-Project 和 X-Indiana-Environment 只核对预期范围,不能通过修改它们切换到未授权 App。直接 HTTP 可以省略;传入时必须成对。

退款除 credits:consume 外还需 credits:manage;Funnel start 需要 sessions:create 和 funnels:run。其余附加条件查对应接口。

相关:完整接口与权限

可选:让客户端直接访问 Indiana

如果业务只在后端调用 Indiana,上面的流程已经足够。浏览器需要读取当前客户数据或展示 Funnel 时,才增加客户会话流程。

  • 企业后端先用自己的登录系统确认用户,再映射该用户的 Customer。
  • 为 Key 增加 sessions:create,以及客户端实际需要的权限,例如 entitlements:read。
  • 后端调用 POST /api/v1/customer-sessions,body 为 { customerId };只向已验证用户返回 customerToken / expiresAt,并设置 no-store。
  • 客户端使用 Authorization: Bearer <customerToken> 请求 Indiana;浏览器来源须配置在当前 App/环境允许列表中。

Customer Token 默认 10 分钟有效,且不超过签发 Key 到期时间。只下放 entitlements:read、funnels:run、billing:read、billing:write、offers:resolve、credits:read;每次请求重新检查签发 Key 和权限。游客会话只获得 funnels:run。

当前没有 Public SDK Key 加任意用户 ID 来建立客户身份的入口。普通 SDK 接口拒绝带 Origin 的服务端 Key 请求,不能把 Secret Key 放进浏览器;Funnel 的独立网关来源规则见 Funnel 文档。

相关:完整后端会话接口与浏览器接入文件

相关:Funnel 运行凭据与流程

首次请求失败时怎么排查

常见失败与下一步
HTTP / code优先检查
401 / UNAUTHENTICATEDKey 是否完整复制、被部署正确读取,是否撤销或到期。服务端 Key 失败不能靠刷新 Customer Token 解决。
403 / FORBIDDEN首次请求需要 customers:write;普通服务端请求不要携带浏览器 Origin。
403 / SCOPE_MISMATCHApp ID、sandbox/live 是否与 Key 所属范围一致。
400 / INVALID_REQUEST输入是否符合 DTO;范围 Header 是否成对;body 是否为 JSON。
500 / INTERNAL_ERROR; 503 / SERVICE_UNAVAILABLE记录 code、status、requestId 排查 Indiana 依赖或网络问题;写入重试保留原幂等身份与输入。
501 / NOT_CONFIGUREDIndiana 必需服务未配置,由服务端维护者修复。

常见失败与下一步

  • 401 / UNAUTHENTICATED

    优先检查
    Key 是否完整复制、被部署正确读取,是否撤销或到期。服务端 Key 失败不能靠刷新 Customer Token 解决。
  • 403 / FORBIDDEN

    优先检查
    首次请求需要 customers:write;普通服务端请求不要携带浏览器 Origin。
  • 403 / SCOPE_MISMATCH

    优先检查
    App ID、sandbox/live 是否与 Key 所属范围一致。
  • 400 / INVALID_REQUEST

    优先检查
    输入是否符合 DTO;范围 Header 是否成对;body 是否为 JSON。
  • 500 / INTERNAL_ERROR; 503 / SERVICE_UNAVAILABLE

    优先检查
    记录 code、status、requestId 排查 Indiana 依赖或网络问题;写入重试保留原幂等身份与输入。
  • 501 / NOT_CONFIGURED

    优先检查
    Indiana 必需服务未配置,由服务端维护者修复。

记录错误的 code、status 和 requestId,按 retryable/recovery 处理。超时或响应丢失时先恢复原业务操作。完整字段与版本要求见错误说明;日志中不要记录 Key 或 Token。

相关:完整错误说明

撤销和轮换 Key

  • 纯服务端轮换:创建同 App/环境的新 Key 并授予所需权限 → 更新后端配置,确认请求成功 → 撤销旧 Key。
  • 已接入客户端时,撤销旧 Key 前还要用新 Key 创建客户会话并更新客户端。renewCustomerSession 只接受原签发者,不能跨 Key 续签。
  • 撤销或到期后,Key 及其签发的客户 Token 在后续鉴权失败;不会自动回滚已提交操作。
  • SDK 不自动续签。客户 Token 到期时由宿主重新验证登录后签发或续签;grow.reset 只清内存,撤销会话须由后端显式调用 revokeCustomerSessions。

相关:客户会话生命周期与退出登录