凭据是服务器管理的密钥,可让您的代理访问第三方服务,而无需将密钥输入到代理的环境中。您只需存储一次凭据,然后通过 ID 引用该凭据,出站代理会在请求时解析并注入该凭据。
Secret 值是只写值。存储后,任何端点都不会返回这些令牌,因此遭到入侵的代理无法读取其正在使用的令牌。
您使用凭据的主要位置是 environment.network 上的网络许可名单。先存储 Secret:
Python
from google import genai
client = genai.Client()
credential = client.credentials.create(
id="github-production",
type="bearer_token",
token="ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
)
print(f"Credential ID: {credential.id}, Status: {credential.status}")
JavaScript
import { GoogleGenAI } from "@google/genai";
const client = new GoogleGenAI({});
const credential = await client.credentials.create({
id: "github-production",
type: "bearer_token",
token: "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
});
console.log(`Credential ID: ${credential.id}, Status: ${credential.status}`);
REST
curl -X POST "https://generativelanguage.googleapis.com/v1beta/credentials" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"id": "github-production",
"type": "bearer_token",
"token": "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}'
然后将其附加到它所验证的网域:
Python
interaction = client.interactions.create(
agent="antigravity-preview-09-2026",
input="Triage the open issues in my-org/my-repo.",
environment={
"type": "remote",
"network": {
"allowlist": [
{"domain": "api.github.com", "credential": "github-production"},
{"domain": "*"},
]
},
},
)
JavaScript
const interaction = await client.interactions.create({
agent: "antigravity-preview-09-2026",
input: "Triage the open issues in my-org/my-repo.",
environment: {
type: "remote",
network: {
allowlist: [
{ domain: "api.github.com", credential: "github-production" },
{ domain: "*" },
],
},
},
});
REST
curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"agent": "antigravity-preview-09-2026",
"input": "Triage the open issues in my-org/my-repo.",
"environment": {
"type": "remote",
"network": {
"allowlist": [
{ "domain": "api.github.com", "credential": "github-production" },
{ "domain": "*" }
]
}
}
}'
现在,代理会向 api.github.com 发出经过身份验证的请求,并且令牌永远不会存在于沙盒中。
凭据类型
每项凭据都有一个 type,用于确定凭据接受哪些字段以及代理如何应用凭据。
| 类型 | 使用场景 | 行为 |
|---|---|---|
bearer_token |
个人访问令牌、机器人令牌、静态 API 密钥 | 代理将令牌作为请求标头注入。无刷新逻辑。 |
oauth2 |
OAuth 应用和用户委托流程 | 代理会将刷新令牌交换为访问令牌,并在访问令牌过期时刷新它们。 |
environment_variable |
从进程环境中读取 Secret 的客户端 SDK | 代理的环境会收到一个占位符。代理会替换出站请求中的真实密钥。 |
使用网络许可名单中的凭据
将 credential 添加到许可名单规则中,代理会验证向该网域发出的每个出站请求。这是向代理授予对私有 API、私有代码库或私有存储桶的访问权限的推荐方法。
您可以在同一许可名单中混用经过身份验证的规则和未经身份验证的规则:
Python
interaction = client.interactions.create(
agent="antigravity-preview-09-2026",
input="Sync the open Jira issues into the tracking sheet in my repo.",
environment={
"type": "remote",
"sources": [
{
"type": "repository",
"source": "https://github.com/your-org/backend",
"target": "/backend-app",
}
],
"network": {
"allowlist": [
{"domain": "github.com", "credential": "github-production"},
{"domain": "api.atlassian.com", "credential": "jira-oauth"},
{"domain": "*.googleapis.com"},
]
},
},
)
JavaScript
const interaction = await client.interactions.create({
agent: "antigravity-preview-09-2026",
input: "Sync the open Jira issues into the tracking sheet in my repo.",
environment: {
type: "remote",
sources: [
{
type: "repository",
source: "https://github.com/your-org/backend",
target: "/backend-app",
},
],
network: {
allowlist: [
{ domain: "github.com", credential: "github-production" },
{ domain: "api.atlassian.com", credential: "jira-oauth" },
{ domain: "*.googleapis.com" },
],
},
},
});
REST
curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"agent": "antigravity-preview-09-2026",
"input": "Sync the open Jira issues into the tracking sheet in my repo.",
"environment": {
"type": "remote",
"sources": [
{
"type": "repository",
"source": "https://github.com/your-org/backend",
"target": "/backend-app"
}
],
"network": {
"allowlist": [
{ "domain": "github.com", "credential": "github-production" },
{ "domain": "api.atlassian.com", "credential": "jira-oauth" },
{ "domain": "*.googleapis.com" }
]
}
}
}'
由于代理会针对每个请求解析凭据,因此 oauth2 凭据会以透明方式刷新其访问令牌。当访问令牌过期时,长时间运行的互动不会中断。
合并 credential 和 transform
许可名单规则还接受内嵌的 transform 对象,该对象可直接在规则中设置标头。这两种机制均由出口代理在网络上应用,因此在两种情况下,标头值都不会存在于沙盒内。这两个字段可以出现在同一条规则中。
| 规则配置 | 行为 |
|---|---|
仅 credential |
代理会解析凭据,并在向网域发出的每个请求中注入其标头。 |
仅 transform |
静态标头注入。您撰写的标头会按原样发送。 |
| 两者都有 | 系统会先应用凭据,然后将 transform 合并到顶部。如果两者设置了相同的键,则显式 transform 标头胜出。 |
| 都不是 | 允许该网域,并且不注入任何标头。 |
如果您希望存储一次 Secret,然后从项目中的每个环境、代理和触发器引用该 Secret,并且希望系统为您处理访问令牌刷新和轮换,那么凭据就非常有用。如果值属于单个调用,例如在创建互动之前自行生成的令牌,则适合使用内嵌 transform。
将两者结合起来是很常见的做法。凭据包含身份验证标头,并添加上游服务在同一请求中所需的任何其他内容:transform
{
"domain": "api.atlassian.com",
"credential": "jira-oauth",
"transform": {
"X-Atlassian-Workspace": "my-workspace-id"
}
}
如需将密钥从内嵌 transform 移至凭据,请使用 POST /credentials 存储该密钥,将 transform 中的身份验证标头替换为 "credential": "<id>",然后将 transform 对象的其余部分保持不变。
将凭据与 MCP 服务器搭配使用
远程 MCP 服务器采用相同的 credential 字段。在 mcp_server 工具上设置该标头,然后代理会将身份验证标头注入到发送给该服务器的每个请求中:
Python
interaction = client.interactions.create(
agent="antigravity-preview-09-2026",
input="Create a new issue in my-org/my-repo",
environment="remote",
tools=[{
"type": "mcp_server",
"name": "github",
"url": "https://api.githubcopilot.com/mcp",
"credential": "github-production",
}],
)
JavaScript
const interaction = await client.interactions.create({
agent: "antigravity-preview-09-2026",
input: "Create a new issue in my-org/my-repo",
environment: "remote",
tools: [{
type: "mcp_server",
name: "github",
url: "https://api.githubcopilot.com/mcp",
credential: "github-production",
}],
});
REST
curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"agent": "antigravity-preview-09-2026",
"input": "Create a new issue in my-org/my-repo",
"environment": "remote",
"tools": [
{
"type": "mcp_server",
"name": "github",
"url": "https://api.githubcopilot.com/mcp",
"credential": "github-production"
}
]
}'
credential 和 headers 遵循与许可名单相同的优先规则。凭据先应用,然后 headers 合并到顶部,因此如果两者都设置了相同的键,则显式标头胜出:
{
"type": "mcp_server",
"name": "jira",
"url": "https://jira.atlassian.com/mcp",
"credential": "jira-oauth",
"headers": {
"X-Atlassian-Workspace": "my-workspace-id"
}
}
如需将密钥从内嵌 headers 移至凭据,请使用 POST /credentials 存储该密钥,并将 headers 中的身份验证条目替换为 credential。将其他标题保留在原位。
将凭据用作环境变量
某些客户端库从进程环境中读取密钥,而不是将其作为请求标头接受。套接字模式和长轮询客户端是常见情况。
将 environment_variable 凭据绑定到 environment.env 下的变量名称:
Python
interaction = client.interactions.create(
agent="antigravity-preview-09-2026",
input="Run the sync script and check notifications.",
environment={
"type": "remote",
"env": {
"NODE_ENV": "production",
"SLACK_BOT_TOKEN": {"credential": "slack-bot-token"},
},
},
)
JavaScript
const interaction = await client.interactions.create({
agent: "antigravity-preview-09-2026",
input: "Run the sync script and check notifications.",
environment: {
type: "remote",
env: {
NODE_ENV: "production",
SLACK_BOT_TOKEN: { credential: "slack-bot-token" },
},
},
});
REST
curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"agent": "antigravity-preview-09-2026",
"input": "Run the sync script and check notifications.",
"environment": {
"type": "remote",
"env": {
"NODE_ENV": "production",
"SLACK_BOT_TOKEN": { "credential": "slack-bot-token" }
}
}
}'
env 接受字面值字符串和凭据引用。一个字面量字符串作为普通明文变量注入到容器中。
凭据引用不是。该变量接收占位符 __GEMINI_CRED_<credential-id>__,并且代理仅针对发送到凭据 trusted_domains 中网域的出站请求换入真实 Secret。对任何其他网域的请求都会被拒绝,因此密钥永远不会离开安全边界,也不会发送占位符来代替密钥。
为每个 environment_variable 凭据设置 trusted_domains。它是一种控制机制,用于限定密文的使用范围。
创建凭据
每个创建请求都需要一个 type,以及相应类型所需的任何字段。
直接调用 REST 时,所有字段名称都使用 snake_case。发送 camelCase 字段会返回 400。
不记名令牌
不记名令牌凭据只需 token:
Python
credential = client.credentials.create(
id="github-production",
type="bearer_token",
token="ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
)
JavaScript
const credential = await client.credentials.create({
id: "github-production",
type: "bearer_token",
token: "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
});
REST
curl -X POST "https://generativelanguage.googleapis.com/v1beta/credentials" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"id": "github-production",
"type": "bearer_token",
"token": "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}'
响应仅返回元数据,绝不会返回令牌:
{
"id": "github-production",
"type": "bearer_token",
"status": "active",
"create_time": "2026-07-15T10:00:00.000000000Z",
"update_time": "2026-07-15T10:00:00.000000000Z"
}
默认情况下,代理会发送 Authorization: Bearer <token>。替换 header_name 和 prefix 以定位到需要其他内容的相应服务:
Python
credential = client.credentials.create(
id="my-api-key",
type="bearer_token",
token="key_xxxxxxxxxxxx",
header_name="x-goog-api-key",
prefix="",
)
JavaScript
const credential = await client.credentials.create({
id: "my-api-key",
type: "bearer_token",
token: "key_xxxxxxxxxxxx",
header_name: "x-goog-api-key",
prefix: "",
});
REST
curl -X POST "https://generativelanguage.googleapis.com/v1beta/credentials" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"id": "my-api-key",
"type": "bearer_token",
"token": "key_xxxxxxxxxxxx",
"header_name": "x-goog-api-key",
"prefix": ""
}'
此配置会生成标头 x-goog-api-key: key_xxxxxxxxxxxx。
下表显示了 header_name 和 prefix 的组合方式:
| 配置 | 注入的标头 |
|---|---|
{"token": "ghp_xxx"} |
Authorization: Bearer ghp_xxx |
{"token": "sk_live_xxx"} |
Authorization: Bearer sk_live_xxx |
{"token": "key_xxx", "header_name": "x-goog-api-key", "prefix": ""} |
x-goog-api-key: key_xxx |
{"token": "mytoken", "header_name": "X-API-Token", "prefix": ""} |
X-API-Token: mytoken |
OAuth2
OAuth2 凭据需要 client_id、client_secret、refresh_token 和 token_url。scopes 字段为可选字段:
Python
credential = client.credentials.create(
id="jira-oauth",
type="oauth2",
client_id="my-client-id",
client_secret="my-client-secret",
token_url="https://auth.atlassian.com/oauth/token",
refresh_token="rt_xxxxxxxxxxxxxxxxxxxx",
scopes=["read:jira-work", "write:jira-work"],
)
JavaScript
const credential = await client.credentials.create({
id: "jira-oauth",
type: "oauth2",
client_id: "my-client-id",
client_secret: "my-client-secret",
token_url: "https://auth.atlassian.com/oauth/token",
refresh_token: "rt_xxxxxxxxxxxxxxxxxxxx",
scopes: ["read:jira-work", "write:jira-work"],
});
REST
curl -X POST "https://generativelanguage.googleapis.com/v1beta/credentials" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"id": "jira-oauth",
"type": "oauth2",
"client_id": "my-client-id",
"client_secret": "my-client-secret",
"token_url": "https://auth.atlassian.com/oauth/token",
"refresh_token": "rt_xxxxxxxxxxxxxxxxxxxx",
"scopes": ["read:jira-work", "write:jira-work"]
}'
创建 OAuth2 凭据时,系统会针对 token_url 执行实时令牌交换,以确认配置有效。仅当提供方返回包含 access_token 的成功令牌响应时,系统才会存储凭据。系统接受 JSON 和 form-urlencoded 响应。
这意味着您需要在创建时提供有效且未过期的刷新令牌。如果提供方拒绝交换,系统会向您返回错误:
{
"error": {
"message": "OAuth token validation failed with HTTP 403: {\"error\":\"unauthorized_client\",\"error_description\":\"refresh_token is invalid\"}",
"code": "invalid_request"
}
}
存储后,代理会在访问令牌过期时刷新它们。如果提供方轮替刷新令牌并在刷新期间返回新的刷新令牌,则新令牌会自动替换存储的令牌。
环境变量
environment_variable 凭据需要 value 和 injection_location:
Python
credential = client.credentials.create(
id="slack-bot-token",
type="environment_variable",
value="xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx",
trusted_domains=["*.slack.com", "slack.com"],
injection_location="header",
)
JavaScript
const credential = await client.credentials.create({
id: "slack-bot-token",
type: "environment_variable",
value: "xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx",
trusted_domains: ["*.slack.com", "slack.com"],
injection_location: "header",
});
REST
curl -X POST "https://generativelanguage.googleapis.com/v1beta/credentials" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"id": "slack-bot-token",
"type": "environment_variable",
"value": "xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx",
"trusted_domains": ["*.slack.com", "slack.com"],
"injection_location": "header"
}'
injection_location 字段用于告知代理在出站请求中替换 Secret 的位置。它接受 header、query 或 body,可以作为单个字符串,也可以作为数组(当服务需要多个时):
"injection_location": ["header", "query"]
替换仅在您列出的位置进行。如果请求在其他任何位置携带占位符,则会被拒绝,而不是继续发送。
如需将凭据绑定到变量名称,请参阅将凭据用作环境变量。
生成的 ID
id 字段为可选字段。如果省略此参数,服务会生成一个 UUID:
{
"id": "9e545973-4330-49bb-9a44-930cea9fbe3c",
"type": "bearer_token",
"status": "active",
"create_time": "2026-07-15T10:00:00.000000000Z",
"update_time": "2026-07-15T10:00:00.000000000Z"
}
如果您希望使用稳定且易读的引用来处理各种互动,请提供您自己的 ID。由于 ID 会显示在资源路径中,因此最好使用带连字符或下划线的小写字母数字字符。
列出凭据
列出属于项目的凭据。使用分页参数控制响应批次大小。
Python
response = client.credentials.list(page_size=10)
for credential in response.credentials:
print(f"Credential ID: {credential.id}, Type: {credential.type}")
JavaScript
const response = await client.credentials.list({ page_size: 10 });
for (const credential of response.credentials) {
console.log(`Credential ID: ${credential.id}, Type: ${credential.type}`);
}
REST
curl -X GET "https://generativelanguage.googleapis.com/v1beta/credentials?page_size=10" \
-H "x-goog-api-key: $GEMINI_API_KEY"
响应仅包含元数据:
{
"credentials": [
{
"id": "github-production",
"type": "bearer_token",
"status": "active",
"create_time": "2026-07-15T10:00:00.000000000Z",
"update_time": "2026-07-15T10:00:00.000000000Z"
},
{
"id": "jira-oauth",
"type": "oauth2",
"status": "active",
"create_time": "2026-07-15T10:05:00.000000000Z",
"update_time": "2026-07-15T10:05:00.000000000Z"
}
],
"next_page_token": "Cj...5aE="
}
将 next_page_token 作为 page_token 传回,以获取下一页。如果没有更多结果,则省略此字段。
| 参数 | 类型 | 说明 |
|---|---|---|
page_size |
integer | 每个页面的凭据数量上限。 |
page_token |
字符串 | 上一个响应的 next_page_token 中的令牌。 |
获取凭据
按 ID 检索特定凭据的元数据。
Python
credential = client.credentials.get(id="github-production")
print(f"Credential ID: {credential.id}, Status: {credential.status}")
JavaScript
const credential = await client.credentials.get("github-production");
console.log(`Credential ID: ${credential.id}, Status: ${credential.status}`);
REST
curl -X GET "https://generativelanguage.googleapis.com/v1beta/credentials/github-production" \
-H "x-goog-api-key: $GEMINI_API_KEY"
响应类似于以下内容:
{
"id": "github-production",
"type": "bearer_token",
"status": "active",
"create_time": "2026-07-15T10:00:00.000000000Z",
"update_time": "2026-08-01T14:30:00.000000000Z"
}
请求不存在的凭据会返回 404:
{
"error": {
"message": "Result not found.; GetCredential call failed",
"code": "not_found"
}
}
轮替凭据
替换某个 Secret,而不影响引用该 Secret 的任何许可名单规则、工具定义或环境变量。旋转会在下个代理分辨率生效。
请求必须包含 type 以及您要更改的字段。您省略的字段将保留其当前值。
轮替不记名令牌:
Python
credential = client.credentials.update(
id="github-production",
type="bearer_token",
token="ghp_new_xxxxxxxxxxxxxxxxxxxx",
)
JavaScript
const credential = await client.credentials.update("github-production", {
type: "bearer_token",
token: "ghp_new_xxxxxxxxxxxxxxxxxxxx",
});
REST
curl -X PATCH "https://generativelanguage.googleapis.com/v1beta/credentials/github-production" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"type": "bearer_token",
"token": "ghp_new_xxxxxxxxxxxxxxxxxxxx"
}'
轮换 OAuth2 刷新令牌:
Python
credential = client.credentials.update(
id="jira-oauth",
type="oauth2",
refresh_token="rt_new_xxxxxxxxxxxxxxxxxxxx",
)
JavaScript
const credential = await client.credentials.update("jira-oauth", {
type: "oauth2",
refresh_token: "rt_new_xxxxxxxxxxxxxxxxxxxx",
});
REST
curl -X PATCH "https://generativelanguage.googleapis.com/v1beta/credentials/jira-oauth" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"type": "oauth2",
"refresh_token": "rt_new_xxxxxxxxxxxxxxxxxxxx"
}'
响应反映了新的 update_time:
{
"id": "jira-oauth",
"type": "oauth2",
"status": "active",
"create_time": "2026-07-15T10:05:00.000000000Z",
"update_time": "2026-08-01T14:30:00.000000000Z"
}
凭据的 type 在创建时即已确定。如需更改,请删除相应凭据并创建新凭据。
删除凭据
在不再需要凭据及其存储的密钥时,将其删除。
Python
client.credentials.delete(id="github-production")
JavaScript
await client.credentials.delete("github-production");
REST
curl -X DELETE "https://generativelanguage.googleapis.com/v1beta/credentials/github-production" \
-H "x-goog-api-key: $GEMINI_API_KEY"
成功删除后会返回一个空对象:
{}
任何仍引用该 ID 的许可名单规则、工具或环境变量都将无法解析,因此请先更新这些内容。
字段参考
每种凭据通用的字段:
| 字段 | 类型 | 是否必需 | 说明 |
|---|---|---|---|
id |
string | 否 | 唯一标识符。如果省略,则以 UUID 形式生成。 |
type |
字符串 | 是 | bearer_token、oauth2、environment_variable 之一。 |
status |
字符串 | 只读 | 凭据的当前状态。 |
create_time |
字符串 | 只读 | RFC 3339 创建时间戳。 |
update_time |
字符串 | 只读 | 上次更新的 RFC 3339 时间戳。 |
bearer_token 的字段:
| 字段 | 类型 | 是否必需 | 说明 |
|---|---|---|---|
token |
字符串 | 是 | 只写。令牌值。 |
header_name |
字符串 | 否 | 要注入的标头。默认为 Authorization。 |
prefix |
字符串 | 否 | 值前缀。默认为 Bearer。如果设为 "",则表示没有。 |
oauth2 的字段:
| 字段 | 类型 | 是否必需 | 说明 |
|---|---|---|---|
client_id |
字符串 | 是 | OAuth2 客户端 ID。 |
client_secret |
字符串 | 是 | 只写。OAuth2 客户端密钥。 |
refresh_token |
字符串 | 是 | 只写。用于获取访问令牌的刷新令牌。 |
token_url |
字符串 | 是 | 提供方令牌端点。 |
scopes |
数组 | 否 | 要请求的 OAuth 范围。 |
environment_variable 的字段:
| 字段 | 类型 | 是否必需 | 说明 |
|---|---|---|---|
value |
字符串 | 是 | 只写。相应 Secret 的值。 |
injection_location |
字符串或数组 | 是 | 要替换 Secret 的位置。header、query、body 中的一个或多个。 |
trusted_domains |
数组 | 否 | 授权用于替换的网域模式。 |
错误
如果出现错误,系统会返回一个包含 message 和 code 的 JSON 对象:
{
"error": {
"message": "Credential 'github-production' already exists.; CreateCredential call failed",
"code": "aborted"
}
}
| HTTP 状态 | code |
原因 |
|---|---|---|
| 400 | invalid_request |
缺少必填字段、存在未知字段、type 不受支持或 OAuth2 验证失败。 |
| 404 | not_found |
没有具有该 ID 的凭据。 |
| 409 | aborted |
已存在使用该 ID 的凭据。 |
系统会拒绝未知字段,而不是忽略它们,并且错误会指明相应字段:
{
"error": {
"message": "Unknown parameter 'headerName'. Did you mean 'header_name'?",
"code": "invalid_request"
}
}