AI Agent 如何不开浏览器拿到区块链 API key:SIWE 钱包登录

开发者与 AI Agent 不开浏览器,用 3 次 BlockVectra HTTP 请求加本地钱包签名,完成 4 个步骤取得区块链 API key:SIWE challenge、本地签名、login、POST /keys。注册即送 30,000,000 CU,无需先付款。

AI Agent 不开浏览器拿到 BlockVectra API key 需完成 4 个步骤:3 次 BlockVectra HTTP 请求加本地钱包签名,依次为获取 SIWE challenge、用以太坊钱包在本地签名、login、POST /keys。钱包首次登录即自动开户并赠送 30,000,000 CU,无需先付款。先发出第一条调用(核对于 ):

BASE=https://console-api.blockvectra.com/v1
curl -s "$BASE/auth/siwe/challenge" -H 'Content-Type: application/json' \
  -d "{\"address\":\"$ADDR\",\"purpose\":\"login\"}"

同一流程适用于在无浏览器环境中运行的自主 AI Agent、CI 流水线与脚本,基于以太坊钱包签名(EIP-4361 / SIWE 消息,用 personal_sign(EIP-191)签名)。

密钥安全

私钥、会话令牌和 API key 不要贴进与 AI 的对话、不要作为 MCP 工具参数。

在开户前,可先使用免 key 公开端点 https://api.blockvectra.com/v1/robinhood_mainnet/public 进行测试(Robinhood Chain 上该端点仅允许钱包类 JSON-RPC 方法;各链允许的方法不同,以 list_chains 或 GET /v1/chains 里的 public.methods 为准;Data API 需要 key),额度不够再开户。

流程概览

程序化开户与创建 API key 包含四个步骤:

  1. 获取 challenge:向 POST /auth/siwe/challenge 发送请求,获取服务端生成的签名消息。
  2. 对消息签名:使用以太坊 EOA 钱包,通过 personal_sign(EIP-191)对 EIP-4361 消息原文进行签名。
  3. 登录 / 自动开户:将消息原文与签名提交至 POST /auth/siwe/login。钱包首次登录会自动开户(account_created: true)。新账户注册即得 30,000,000 CU,无需信用卡。
  4. 创建 API key:携带返回的会话令牌调用 POST /keys,创建 API key。

最短 curl 序列

3 次 BlockVectra HTTP 请求加本地钱包签名(4 个步骤),随后用独立的带 key HTTP 请求调用 eth_blockNumber 验证 key。ADDR 为钱包地址(0x...),PK 为其私钥,从密钥管理系统读入;需要 curl、jq 与 Foundry cast。新 key 生效需要几秒,遇到 401 invalid_api_key 请稍等后重试(下方完整示例会自动重试)。

BASE=https://console-api.blockvectra.com/v1

# 1. challenge:服务端返回待签的消息原文
MSG=$(curl -s "$BASE/auth/siwe/challenge" -H 'Content-Type: application/json' \
  -d "{\"address\":\"$ADDR\",\"purpose\":\"login\"}" | jq -r .message)

# 2. 用 EIP-191 personal_sign 签名
SIG=$(cast wallet sign --private-key "$PK" "$MSG")

# 3. login:钱包首次登录自动开户
TOKEN=$(jq -n --arg m "$MSG" --arg s "$SIG" '{message: $m, signature: $s}' |
  curl -s "$BASE/auth/siwe/login" -H 'Content-Type: application/json' -d @- | jq -r .session.token)

# 4. 创建 API key(secret 仅返回一次)
KEY=$(curl -s "$BASE/keys" -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"label":"agent-key"}' | jq -r .api_key)

# 5. 用 x-api-key 请求头携带 key 调用 JSON-RPC
RESP=$(curl -s "https://api.blockvectra.com/v1/robinhood_mainnet" -H "x-api-key: $KEY" -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}' \
  -w '\n%{http_code}') &&
  printf '%s' "${RESP%$'\n'*}" | jq -es --arg status "${RESP##*$'\n'}" '
    if length == 1 and (.[0] | type) == "object" and ($status | test("^2[0-9][0-9]\\z")) and
      (.[0] | .jsonrpc == "2.0" and (.id | type) == "number" and .id == 1 and has("error") == false and
        (.result | type) == "string" and (.result | test("^0x(0|[1-9a-fA-F][0-9a-fA-F]*)\\z")))
    then .[0] else empty end'

完整复制即跑示例

从这里开始:使用本地以太坊 EOA 签名工具,创建 key,并通过 eth_blockNumber 验证。 对于 Bash 示例,需要安装 curl、jq 与 Foundry cast。请在本地签名环境中妥善保管钱包凭据。

完整模板仓库:blockvectra/agent-quickstart

以下代码从环境变量读取钱包凭据,完成 challenge、签名、登录、建 key 全流程,将生成的 key 导出或打印提示 export BLOCKVECTRA_API_KEY=...,并通过请求头调用一次 eth_blockNumber:

新 key 生效需要几秒,示例会自动重试。

BASE=https://console-api.blockvectra.com/v1
# $ADDR: 以太坊钱包地址 (0x...)
# $PK: 钱包私钥,从密钥管理系统读入(严禁写进脚本或命令历史)

# 1. 获取服务端签发的 SIWE 消息(不带 Origin 请求头)
curl -s "$BASE/auth/siwe/challenge" -H 'Content-Type: application/json' \
  -d "{\"address\":\"$ADDR\",\"purpose\":\"login\"}" > challenge.json
jq -r .message challenge.json > msg.txt

# 2. 用钱包对消息原文做 EIP-191 personal_sign 签名
SIG=$(cast wallet sign --private-key "$PK" "$(cat msg.txt)")

# 3. 原样提交消息原文与签名(同样不带 Origin 请求头)完成登录(ref 字段可选)
jq -n --rawfile m msg.txt --arg s "$SIG" '{message: ($m | rtrimstr("\n")), signature: $s, ref: "docs-signup"}' |
  curl -s "$BASE/auth/siwe/login" -H 'Content-Type: application/json' -d @- > session.json
TOKEN=$(jq -r .session.token session.json)

# 4. 创建 API key(secret 仅返回一次)
KEY_RESP=$(curl -s "$BASE/keys" \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"label":"agent-key"}')
export BLOCKVECTRA_API_KEY=$(echo "$KEY_RESP" | jq -r .api_key)

# 5. 用请求头携带 key 发送 JSON-RPC 请求
RPC_DEADLINE=$((SECONDS + 10))
while true; do
  RPC_TIMEOUT=$((RPC_DEADLINE - SECONDS))
  if ((RPC_TIMEOUT <= 0)); then
    printf '%s' "${RPC_BODY:-}"
    echo "RPC verification failed: retry budget exhausted" >&2
    exit 1
  fi
  RPC_RESP=$(curl -s --max-time "$RPC_TIMEOUT" -w '\n%{http_code}' "https://api.blockvectra.com/v1/robinhood_mainnet" \
    -H "x-api-key: $BLOCKVECTRA_API_KEY" \
    -H 'Content-Type: application/json' \
    -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}') || { rc=$?; echo "request failed (curl exit $rc)" >&2; exit $rc; }
  RPC_STATUS=${RPC_RESP##*$'\n'}
  RPC_BODY=${RPC_RESP%$'\n'*}
  if ((SECONDS + 2 < RPC_DEADLINE)) &&
    printf '%s' "$RPC_BODY" | jq -es --arg status "$RPC_STATUS" '
      length == 1 and (.[0] | type) == "object" and (
        ($status == "401" and .[0].error.data.reason == "invalid_api_key") or
        ($status == "503" and .[0].error.code == -32021))
    ' >/dev/null 2>&1; then
    sleep 2
    continue
  fi
  printf '%s' "$RPC_BODY"
  printf '%s' "$RPC_BODY" | jq -es --arg status "$RPC_STATUS" '
    length == 1 and (.[0] | type) == "object" and ($status | test("^2[0-9][0-9]\\z")) and
    (.[0] | .jsonrpc == "2.0" and (.id | type) == "number" and .id == 1 and has("error") == false and
      (.result | type) == "string" and (.result | test("^0x(0|[1-9a-fA-F][0-9a-fA-F]*)\\z")))
  ' >/dev/null 2>&1 || { echo "RPC verification failed (HTTP $RPC_STATUS)" >&2; exit 1; }
  break
done

Base 地址与程序化模式

所有认证与 key 管理接口均使用正式 Base 地址:

https://console-api.blockvectra.com/v1

不带 Origin 请求头

程序化请求工作在程序化模式:

  • challenge(POST /auth/siwe/challenge)和 login(POST /auth/siwe/login)两个请求都不带 Origin 请求头(curl 与常用 HTTP 客户端默认不附带该头;请勿手动添加)。
  • 若请求中带有 Origin 请求头,但取值不是配置的网页控制台域名(包括空值或 null),challenge 请求直接返回 HTTP 400 invalid_request。
  • 若登录时提交的模式与 challenge 时的模式不一致(例如以程序化模式获取 challenge 后,提交 login 时附带了 Origin 请求头,反之亦然),login 请求返回 HTTP 400 siwe_invalid,原因码为 domain_mismatch。

消息原文与钱包要求

  • 原样签名、原样提交:客户端必须对 challenge 返回的 message 原文进行签名并原样提交,严禁修改任何空格、换行、域名或链 ID。篡改任何内容均会返回 HTTP 400 siwe_invalid,原因码为 signature。
  • 支持的钱包:以太坊主网(Chain ID 1)的 EOA(Externally Owned Account)钱包。签名必须为 65 字节 ECDSA 签名(personal_sign)。不支持合约钱包(EIP-1271)与智能账户(smart accounts)。
  • challenge 有效期:每个 challenge 的 nonce 仅限使用一次,5 分钟内有效。

请求体参数与注册归因(可选)

POST /auth/siwe/login 的请求体包含必选的认证字段与可选的来源归因字段:

  • 必选字段:
    • message:从 challenge 端点获取的完整 SIWE 消息字符串。
    • signature:以太坊钱包用 personal_sign(EIP-191)对 EIP-4361 message 签名得到的 65 字节十六进制签名(0x 开头)。
  • 可选归因字段(仅在首次登录新开户时随账户保存一次,后续登录不再改写):
    • ref:小写渠道标记,格式为 ^[a-z0-9._-]{1,64}$(仅允许小写字母、数字、.、_、-,1–64 个字符)。例如自主 Agent 可在此传入自身框架或运行环境名(如 my-agent.v1)。不合规的值(包含大写字母、空串、超长或非法字符)一律返回 HTTP 400 invalid_request(不做大小写折叠,亦不开户);不传或为 null 表示没有渠道标记。
    • referrer:来源 URL 或主机名字符串。省略或传入 null 均按未提供处理;空字符串或无法提取有效主机名的字符串会被丢弃。仅非 null 且非字符串的值返回 HTTP 400 invalid_request。

请求体中若附带 signup_method 等未定义字段会返回 HTTP 400 invalid_request。

会话令牌与 API key

会话令牌生命周期

  • 格式:rgs_ + 64 位小写十六进制字符串。
  • 有效期:绝对有效 7 天;连续空闲 24 小时后失效。
  • 无 refresh token:会话失效后重新执行 challenge 与 login 流程获取新令牌。
  • 传递方式:在请求头中传入 Authorization: Bearer rgs_...。

创建 API key

  • 携带会话令牌调用 POST /keys,创建 API key(rgw_ + 64 位十六进制字符串)。
  • 默认策略下,每账户最多持有 20 个未吊销且未过期的 key(active + disabled)。只有未吊销且 expires_at 为 null或严格晚于当前时刻的 key 才计入,已过期的 key 不计入;超出实际配置上限时返回 HTTP 409 key_limit_reached,附 error.reason: active_keys,error.limit 为实际配置上限,请先吊销一个 key。该上限由账户下所有身份、会话与链共享。默认滚动 24 小时创建上限也是 20 次;超出时返回 HTTP 429 rate_limited,附 error.reason: daily_creations 与 Retry-After: 3600。
  • 在创建配额检查中,先检查有效 key 数量上限,再检查滚动 24 小时创建上限;若同时达到两者,创建请求返回有效 key 上限的 HTTP 409,而非每日创建上限的 HTTP 429。轮换要求目标 key 已启用、未吊销且未过期;轮换不增加有效 key 数量,但仍消耗每日创建配额。吊销或过期不会从滚动 24 小时统计中扣除该 key 的创建次数。
  • 可选限额与有效期:支持传入 cu_cap(该 key 终身可用的 CU 上限,为软上限)以及有效期(expires_in_secs 或 expires_at 二选一,最长为 key 策略允许的天数);到期或用满上限后,服务端返回 403(JSON-RPC -32025,reason 为 key_expired 或 key_cap_exhausted)。
  • 响应中的 api_key 只出现这一次,请立即妥善保存至密钥管理系统或环境变量中。
  • 一个 API key 可用于所有支持链的 JSON-RPC 与 Data API。

会话或 key 丢了怎么办

在 BlockVectra 中,Agent 的账户身份与开户所用的以太坊钱包地址绑定。当会话令牌过期,或 API key 丢失、泄露时,只需持有该钱包的私钥即可找回账户控制权并换新 key:

  1. 凭同一个钱包重新签名登录:使用同一钱包地址重新执行获取 challenge、钱包签名并提交登录流程(POST /auth/siwe/login)。服务端验签通过后直接登录已有账户,返回新会话令牌,且 account_created 为 false。
  2. 创建新 API key:拿到新会话令牌后,携带 Authorization: Bearer <token> 请求头调用 POST /keys,在请求体中传入 {"label": "..."}。响应为 HTTP 201,返回包含新 key 信息的 key 对象与只显示一次的 api_key 字符串(密钥)。将其妥善保存在环境变量或密钥管理系统中。
  3. 列出本账户的 key:
    • 接口:GET /keys
    • 请求头:Authorization: Bearer <token>
    • Query 参数:可选 include_revoked=true(为 true 时包含已吊销的 key;默认仅返回未吊销 key)。
    • 响应:HTTP 200,返回 JSON 对象 {"items": [...]}。其中 items 数组的每个元素包含字段:
      • key_id:key 唯一标识(字符串)
      • label:key 标签名(字符串或 null)
      • status:状态("active"、"disabled" 或 "revoked")
      • created_at:创建时间戳
      • revoked_at:吊销时间戳(未吊销时为 null)
  4. 吊销不再使用或泄露的旧 key:
    • 接口:POST /keys/{key_id}/revoke(注意使用 POST 方法,路径参数为待吊销的 key_id,无请求体)
    • 请求头:Authorization: Bearer <token>
    • 行为:操作幂等,active 与 disabled 状态的 key 均可吊销。已吊销时原样返回 HTTP 200。吊销后,用这个 key 的请求会被拒绝。
    • 响应:HTTP 200,返回已吊销 key 的对象(字段同上述单个 key,其中 status 为 "revoked",revoked_at 为吊销时间戳)。

私钥与 key 安全

钱包私钥与 API key 必须妥善保存在环境变量或密钥管理系统中,切勿写入代码仓库、运行日志或与 AI 的对话上下文中。

安全建议

  • 短期 key 用完即吊销:对于自动化或短期任务,建议在创建 key 时设置短期有效期(expires_in_secs),任务执行完毕后及时通过 POST /keys/{key_id}/revoke 吊销。

开户限流(signup_rate_limited)

新开户操作受开户限额约束。每个 IPv4 地址或 IPv6 /64 前缀的令牌桶容量为 100 个账户,每小时补充 100 个账户的开户配额,由 SIWE 与 OAuth 开户共享:

  • 超出限额时,POST /auth/siwe/login 返回 HTTP 429 signup_rate_limited,并带有 Retry-After 响应头(秒数)。
  • 错误体中的 reason 字段区分限额维度:
    • per_ip:同一 IP 前缀的开户预算已耗尽。
    • global:全平台开户上限已耗尽。
  • 开户限流仅在新建账户时生效,已有账户登录不受开户限流影响。

常见问题

拿 key 之前要先付款或充值吗?

不要。钱包首次登录即自动开户并赠送 30,000,000 CU,用登录返回的会话令牌创建 key 即可。首次充值之前,账户每秒最多 25 次调用,详见免费套餐能做什么。

需要浏览器、密码或 gas 吗?

不需要浏览器。HTTP 请求使用普通 HTTPS JSON,不带 Origin 请求头;消息签名在自行具备的钱包环境中本地完成。没有密码,钱包签名就是凭据。签名在链下完成,不花 gas、不发交易。钱包必须是以太坊 EOA,不支持合约钱包(EIP-1271)。

API key 丢了怎么办?

secret 只显示一次,无法再次取回。用同一个钱包重新登录拿到新的会话令牌,创建新 key,再吊销旧 key;详见会话或 key 丢了怎么办。

相关资源

  • 阅读 AI Agent 接入指南,了解免 key 的 MCP 服务与机器可读上下文文件。
  • 查看快速上手获取更多语言的客户端接入代码。
  • 查看错误参考页了解所有错误码、原因码与自动化处置建议。

下一步

最后更新:

本页目录