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

> Original page: https://docs.blockvectra.com/en/guides/programmatic-signup/

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:

```bash
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":[]}'
```

## Related resources

* Read the [AI agents integration guide](https://docs.blockvectra.com/en/guides/ai-agents/) to learn about the keyless MCP server and machine-readable context files.
* Review [Quickstart](https://docs.blockvectra.com/en/quickstart/) for multi-language client examples.
* Inspect the [Error reference](https://docs.blockvectra.com/en/errors/) for full error codes, reasons, and automated recovery actions.

## Next steps

* [Browse the datasets directory](https://blockvectra.com/en/data/) to see every dataset BlockVectra indexes.
* [See the free plan and pricing](https://blockvectra.com/en/pricing/#free) to check what your account includes.
* [Log in to the console](https://console.blockvectra.com/en/login/?next=%2Fen%2Fkeys%2F) to create an API key.
