Current offer: every account has 1 reset chance(s) (valid for 30 days) to top its balance back up to 30,000,000 CU in one click. New accounts start with 30,000,000 CU. Learn more →

Programmatic Sign-up: Wallet Sign-in and API Key Creation for Agents and CI

Sign up and create an API key programmatically using an Ethereum wallet signature (EIP-191) without a browser for AI agents, scripts, and CI workflows.

For autonomous AI agents, CI pipelines, and automated scripts running without a browser, BlockVectra provides a programmatic sign-in and account opening workflow based on Ethereum wallet signatures (EIP-4361 / EIP-191).

Key security

Never paste private keys, session tokens, or API keys into conversations with AI or pass them as MCP tool arguments.

Workflow overview

The programmatic registration and key provisioning flow consists of four steps:

  1. Request challenge: Send a request to POST /auth/siwe/challenge to obtain a server-generated sign-in message.
  2. Sign message: Sign the exact message with an Ethereum EOA wallet using EIP-191 (personal_sign).
  3. Log in / open account: Submit the verbatim message and signature to POST /auth/siwe/login. On the first sign-in for a wallet, an account is created automatically (account_created: true). New accounts get 30,000,000 CU on sign-up — no credit card.
  4. Create API key: Use the session token to call POST /keys and create an API key.

Base URL and programmatic mode

All control plane authentication and key management endpoints use the official base URL:

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

Omitting the Origin header

Programmatic requests operate in programmatic mode:

  • Both the challenge (POST /auth/siwe/challenge) and login (POST /auth/siwe/login) requests must not include the Origin header (curl and standard HTTP clients omit this header by default; do not add it manually).
  • In programmatic mode, the server-issued message sets the domain to console-api.blockvectra.com and URI to https://console-api.blockvectra.com.
  • If an Origin header is sent but is not a configured web console domain (including empty string or null), the challenge request returns HTTP 400 invalid_request.
  • If the mode at login does not match the challenge mode (for instance, requesting a programmatic challenge without Origin and then submitting login with an Origin header, or vice versa), the login request returns HTTP 400 siwe_invalid with reason: domain_mismatch.

Message integrity and wallet requirements

  • Verbatim signature and submission: Clients must sign and submit the message text exactly as returned by the challenge endpoint. Do not alter whitespace, domain, chain ID, or any fields. The server performs a byte-by-byte check against the stored challenge before verifying the cryptographic signature. Any modification results in HTTP 400 siwe_invalid with reason: signature.
  • Supported wallets: Ethereum mainnet (Chain ID 1) Externally Owned Accounts (EOA). The signature must be a 65-byte ECDSA signature (personal_sign). Contract wallets (EIP-1271) and smart accounts are not supported.
  • Challenge validity: Each challenge nonce is single-use and expires after 5 minutes.

Session tokens and API keys

Session token lifecycle

  • Format: rgs_ followed by 64 lowercase hexadecimal characters.
  • Validity: Absolute lifetime of 7 days; expires automatically after 24 hours of idle time.
  • No refresh token: When a session token expires, initiate a new challenge and login flow.
  • Header: Pass the session token in the Authorization: Bearer rgs_... request header.

API key creation

  • Call POST /keys with the session token to create an API key (rgw_ followed by 64 hexadecimal characters).
  • The secret api_key is returned only once upon creation. Store it securely immediately in your secrets manager or environment variables.
  • One API key works across all supported chains on JSON-RPC and the Data API.

Sign-up rate limits (signup_rate_limited)

Account creation is subject to sign-up rate limits to protect service resources:

  • When exceeding sign-up limits, POST /auth/siwe/login returns HTTP 429 signup_rate_limited with a Retry-After header indicating the number of seconds to wait.
  • The reason field distinguishes the limit scope:
    • per_ip: the registration budget for the requesting IP prefix has been exhausted.
    • global: the aggregate platform sign-up limit has been exhausted.
  • Sign-up rate limits only evaluate new account registrations. Existing accounts logging in are not blocked by sign-up rate limits.

Complete bash example

The following script reads the wallet address from $ADDR and the wallet private key from $PK (loaded from a secrets manager), completes the challenge and login sequence, creates an API key, exports it to BLOCKVECTRA_API_KEY, and sends a verification eth_blockNumber request:

BASE=https://console-api.blockvectra.com/v1
# $ADDR: Ethereum wallet address (0x...)
# $PK: wallet private key, loaded from a secrets manager (never hardcode in scripts)

# 1. Fetch server-generated SIWE message (omit Origin header)
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. Sign the exact message with EIP-191 personal_sign
SIG=$(cast wallet sign --private-key "$PK" "$(cat msg.txt)")

# 3. Submit verbatim message and signature (omit Origin header) to log in
jq -n --rawfile m msg.txt --arg s "$SIG" '{message: ($m | rtrimstr("\n")), signature: $s}' |
  curl -s "$BASE/auth/siwe/login" -H 'Content-Type: application/json' -d @- > session.json
TOKEN=$(jq -r .session.token session.json)

# 4. Create an API key (the secret is returned only once)
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. Call JSON-RPC with the key in the x-api-key request header
curl -s "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":[]}'

Next steps

Last updated:

On this page