# Programatik Kayıt: Ajanlar ve CI İçin Cüzdanla Oturum Açma ve API Anahtarı Oluşturma

> Source: https://docs.blockvectra.com/tr/guides/programmatic-signup/

Otonom AI Agent'lar, CI ardışık düzenleri ve tarayıcı olmadan çalışan otomatik betikler için BlockVectra, Ethereum cüzdan imzalarına (EIP-4361 / EIP-191) dayalı programatik bir oturum açma ve hesap açma iş akışı sağlar.

> **Anahtar güvenliği**
>
> Özel anahtarları, oturum token'larını veya API anahtarlarını asla yapay zeka ile yapılan konuşmalara yapıştırmayın veya MCP araç parametreleri olarak iletmeyin.


Kaydolmadan önce, anahtarsız genel uç nokta olan `https://api.blockvectra.com/v1/robinhood_mainnet/public` adresini deneyebilirsiniz (yalnızca cüzdan JSON-RPC yöntemleri, Data API bir anahtar gerektirir; yöntemler ve sınırlar `/v1/chains` koşullarına tabidir); kota yetersizse bir hesap açın.

## İş akışına genel bakış

Programatik kayıt ve anahtar sağlama akışı dört adımdan oluşur:

1. **Challenge isteğinde bulunun**: Sunucu tarafından oluşturulan bir oturum açma mesajı almak için `POST /auth/siwe/challenge` uç noktasına bir istek gönderin.
2. **Mesajı imzalayın**: Bu tam mesajı, bir Ethereum EOA cüzdanı ile EIP-191 (`personal_sign`) kullanarak imzalayın.
3. **Oturum açın / hesap oluşturun**: Birebir mesajı ve imzayı `POST /auth/siwe/login` uç noktasına gönderin. Bir cüzdan için yapılan ilk oturum açma işleminde, otomatik olarak bir hesap oluşturulur (`account_created: true`). Yeni hesaplar kaydolduklarında 30,000,000 CU kazanır — kredi kartı gerekmez.
4. **API anahtarı oluşturun**: `POST /keys` uç noktasını çağırmak ve bir API anahtarı oluşturmak için oturum token'ını kullanın.

## Eksiksiz çalıştırılabilir örnekler

Buradan başlayın: yerel bir Ethereum EOA imzalayıcısı kullanın, bir anahtar oluşturun ve bunu eth\_blockNumber ile doğrulayın.
Bash örneği için curl, jq ve Foundry cast gereklidir. Cüzdan kimlik bilgilerini yerel imzalama ortamınızda tutun.

Eksiksiz başlangıç şablonu: [blockvectra/agent-quickstart](https://github.com/blockvectra/agent-quickstart)

Aşağıdaki betikler cüzdan kimlik bilgilerini okur, challenge ve oturum açma dizisini tamamlar, bir API anahtarı sağlar, ortam yapılandırması için `export BLOCKVECTRA_API_KEY=...` dışa aktarır veya yazdırır ve bir doğrulama `eth_blockNumber` isteği gönderir:

Yeni anahtarların aktif hale gelmesi birkaç saniye sürer; bu örnekler otomatik olarak yeniden dener.

**Bash**

```bash
BASE=https://console-api.blockvectra.com/v1
# $ADDR: Ethereum cüzdan adresi (0x...)
# $PK: cüzdan özel anahtarı, bir gizli bilgi yöneticisinden yüklenir (asla betiklerde sabit kodlamayın)

# 1. Sunucu tarafından oluşturulan SIWE mesajını alın (Origin başlığını atlayın)
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. Mesajı EIP-191 personal_sign ile tam olarak imzalayın
SIG=$(cast wallet sign --private-key "$PK" "$(cat msg.txt)")

# 3. Oturum açmak için birebir mesajı ve imzayı gönderin (Origin başlığını atlayın; ref isteğe bağlıdır)
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. Bir API anahtarı oluşturun (gizli anahtar yalnızca bir kez döndürülür)
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. Anahtarı x-api-key istek başlığında ileterek JSON-RPC'yi çağırın
RPC_DEADLINE=$((SECONDS + 10))
while true; do
  RPC_TIMEOUT=$((RPC_DEADLINE - SECONDS))
  if ((RPC_TIMEOUT <= 0)); then
    printf '%s' "${RPC_BODY:-}"
    break
  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 -e --arg status "$RPC_STATUS" '
      ($status == "401" and .error.data.reason == "invalid_api_key") or
      ($status == "503" and .error.code == -32021)
    ' >/dev/null 2>&1; then
    sleep 2
  else
    printf '%s' "$RPC_BODY"
    break
  fi
done
```


  **TypeScript**

```bash
npm i viem
```

```ts
// ESM gerektirir (top-level await; node --input-type=module veya tsx ile çalıştırın)
import { privateKeyToAccount } from "viem/accounts";

const BASE = "https://console-api.blockvectra.com/v1";
const privateKey = process.env.PRIVATE_KEY as `0x${string}`;
const account = privateKeyToAccount(privateKey);

// 1. Sunucu tarafından oluşturulan SIWE mesajını alın (Origin başlığını atlayın)
const challengeRes = await fetch(`${BASE}/auth/siwe/challenge`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ address: account.address, purpose: "login" }),
});
if (!challengeRes.ok) throw new Error(`Challenge failed: ${challengeRes.status}`);
const { message } = (await challengeRes.json()) as { message: string };

// 2. Mesajı EIP-191 personal_sign ile tam olarak imzalayın
const signature = await account.signMessage({ message });

// 3. Oturum açmak için birebir mesajı ve imzayı gönderin (Origin başlığını atlayın; ref isteğe bağlıdır)
const loginRes = await fetch(`${BASE}/auth/siwe/login`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ message, signature, ref: "docs-signup" }),
});
if (!loginRes.ok) throw new Error(`Login failed: ${loginRes.status}`);
const { session } = (await loginRes.json()) as { session: { token: string } };

// 4. Bir API anahtarı oluşturun (gizli anahtar yalnızca bir kez döndürülür)
const keyRes = await fetch(`${BASE}/keys`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${session.token}`,
  },
  body: JSON.stringify({ label: "agent-key" }),
});
if (!keyRes.ok) throw new Error(`Create key failed: ${keyRes.status}`);
const { api_key } = (await keyRes.json()) as { api_key: string };
console.log("Created API key:", api_key);
console.log(`export BLOCKVECTRA_API_KEY=${api_key}`);

// 5. Anahtarı x-api-key istek başlığında ileterek JSON-RPC'yi çağırın
const rpcDeadline = performance.now() + 10_000;
while (true) {
  const rpcRes = await fetch("https://api.blockvectra.com/v1/robinhood_mainnet", {
    method: "POST",
    signal: AbortSignal.timeout(Math.max(1, Math.ceil(rpcDeadline - performance.now()))),
    headers: {
      "Content-Type": "application/json",
      "x-api-key": api_key,
    },
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "eth_blockNumber",
      params: [],
    }),
  });
  const rpcBody = await rpcRes.json();
  const retryable =
    (rpcRes.status === 401 && rpcBody.error?.data?.reason === "invalid_api_key") ||
    (rpcRes.status === 503 && rpcBody.error?.code === -32021);
  if (retryable && performance.now() + 2_000 < rpcDeadline) {
    await new Promise((resolve) => setTimeout(resolve, 2_000));
    continue;
  }
  if (!rpcRes.ok) throw new Error(`RPC call failed: ${rpcRes.status}`);
  console.log("Block number response:", rpcBody);
  break;
}
```


  **Python**

```bash
pip install eth-account requests
```

```python
import os
import time
import requests
from eth_account import Account
from eth_account.messages import encode_defunct

BASE = "https://console-api.blockvectra.com/v1"
private_key = os.environ["PRIVATE_KEY"]
account = Account.from_key(private_key)
address = account.address

# 1. Sunucu tarafından oluşturulan SIWE mesajını alın (Origin başlığını atlayın)
challenge_resp = requests.post(
    f"{BASE}/auth/siwe/challenge",
    json={"address": address, "purpose": "login"},
)
challenge_resp.raise_for_status()
message = challenge_resp.json()["message"]

# 2. Mesajı EIP-191 personal_sign ile tam olarak imzalayın
signable = encode_defunct(text=message)
signed = Account.sign_message(signable, private_key=private_key)
signature = "0x" + bytes(signed.signature).hex()

# 3. Oturum açmak için birebir mesajı ve imzayı gönderin (Origin başlığını atlayın; ref isteğe bağlıdır)
login_resp = requests.post(
    f"{BASE}/auth/siwe/login",
    json={"message": message, "signature": signature, "ref": "docs-signup"},
)
login_resp.raise_for_status()
token = login_resp.json()["session"]["token"]

# 4. Bir API anahtarı oluşturun (gizli anahtar yalnızca bir kez döndürülür)
key_resp = requests.post(
    f"{BASE}/keys",
    headers={"Authorization": f"Bearer {token}"},
    json={"label": "agent-key"},
)
key_resp.raise_for_status()
api_key = key_resp.json()["api_key"]
print("Created API key:", api_key)
print(f"export BLOCKVECTRA_API_KEY={api_key}")

# 5. Anahtarı x-api-key istek başlığında ileterek JSON-RPC'yi çağırın
rpc_deadline = time.monotonic() + 10
while True:
    rpc_resp = requests.post(
        "https://api.blockvectra.com/v1/robinhood_mainnet",
        headers={"x-api-key": api_key, "Content-Type": "application/json"},
        json={"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber", "params": []},
        timeout=max(0.001, rpc_deadline - time.monotonic()),
    )
    rpc_data = rpc_resp.json()
    error = rpc_data.get("error") or {}
    retryable = (
        rpc_resp.status_code == 401
        and (error.get("data") or {}).get("reason") == "invalid_api_key"
    ) or (rpc_resp.status_code == 503 and error.get("code") == -32021)
    if retryable and time.monotonic() + 2 < rpc_deadline:
        time.sleep(2)
        continue
    rpc_resp.raise_for_status()
    print("Block number response:", rpc_data)
    break
```


## Temel URL ve programatik mod

Tüm kimlik doğrulama ve anahtar yönetimi uç noktaları resmi temel URL'yi kullanır:

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

### Origin başlığının atlanması

Programatik istekler **programatik modda** çalışır:

* Hem challenge (`POST /auth/siwe/challenge`) hem de oturum açma (`POST /auth/siwe/login`) istekleri **`Origin` başlığını içermemelidir** (`curl` ve standart HTTP istemcileri varsayılan olarak bu başlığı atlar; manuel olarak eklemeyin).
* Bir `Origin` başlığı gönderilir ancak yapılandırılmış bir web konsolu etki alanı değilse (boş dize veya `null` dahil), challenge isteği HTTP 400 `invalid_request` döndürür.
* Oturum açma anındaki mod challenge moduyla eşleşmezse (örneğin, `Origin` olmadan programatik bir challenge talep edip ardından bir `Origin` başlığı ile oturum açma göndermek veya tam tersi), oturum açma isteği `reason: domain_mismatch` ile HTTP 400 `siwe_invalid` döndürür.

### Mesaj bütünlüğü ve cüzdan gereksinimleri

* **Birebir imzalama ve gönderim**: İstemciler, mesaj metnini challenge uç noktası tarafından döndürüldüğü şekliyle tam olarak imzalamalı ve göndermelidir. Boşlukları, etki alanını, zincir kimliğini veya herhangi bir alanı değiştirmeyin. Herhangi bir değişiklik, `reason: signature` ile HTTP 400 `siwe_invalid` ile sonuçlanır.
* **Desteklenen cüzdanlar**: Ethereum mainnet (Chain ID 1) Harici Olarak Sahip Olunan Hesaplar (EOA). İmza 65 baytlık bir ECDSA imzası (`personal_sign`) olmalıdır. Sözleşme cüzdanları (EIP-1271) ve akıllı hesaplar desteklenmez.
* **Challenge geçerliliği**: Her challenge nonce'ı tek kullanımlıktır ve 5 dakika sonra sona erer.

### İstek gövdesi ve kayıt ilişkilendirmesi (isteğe bağlı)

`POST /auth/siwe/login` istek gövdesi, gerekli kimlik doğrulama parametrelerini ve isteğe bağlı kayıt ilişkilendirme alanlarını kabul eder:

* **Gerekli alanlar**:
  * `message`: Challenge uç noktasından alınan eksiksiz SIWE mesaj dizesi.
  * `signature`: Bir Ethereum cüzdanı ile EIP-191 aracılığıyla `message` imzalanarak üretilen 65 baytlık onaltılık imza (`0x` önekli).
* **İsteğe bağlı ilişkilendirme alanları** (yalnızca yeni bir hesap oluşturulduğunda bir kez kaydedilir; sonraki oturum açmalarda yok sayılır):
  * `ref`: `^[a-z0-9._-]{1,64}$` ile eşleşen küçük harfli bir kanal token'ı (küçük harf ASCII harfler, rakamlar, `.`, `_`, `-`, 1–64 karakter). Örneğin, otonom ajanlar bunu çerçevelerine veya çalışma zamanı tanımlayıcılarına (ör. `my-agent.v1`) ayarlayabilir. Uyumsuz değerler (büyük harfler, boş dizeler, fazla uzunluk veya desteklenmeyen karakterler dahil), harf dönüşümü yapılmaksızın HTTP 400 `invalid_request` döndürür ve hesap oluşturulmasını engeller; geçerli olmadığında atlayın veya `null` iletin.
  * `referrer`: Bir kaynak URL veya ana bilgisayar adı dizesi; yalnızca dize olmayan türler HTTP 400 döndürür.

`signup_method` gibi tanımsız alanların gönderilmesi HTTP 400 `invalid_request` döndürür.

## Oturum token'ları ve API anahtarları

### Oturum token'ı yaşam döngüsü

* **Biçim**: `rgs_` ve ardından gelen 64 küçük harfli onaltılık karakter.
* **Geçerlilik**: Mutlak 7 günlük kullanım ömrü; 24 saatlik boşta kalma süresinden sonra otomatik olarak sona erer.
* **Yenileme token'ı (refresh token) yok**: Bir oturum token'ının süresi dolduğunda, yeni bir challenge ve oturum açma akışı başlatın.
* **Başlık**: Oturum token'ını `Authorization: Bearer rgs_...` istek başlığında iletin.

### API anahtarı oluşturma

* Bir API anahtarı oluşturmak için oturum token'ı ile `POST /keys` uç noktasını çağırın (`rgw_` ve ardından gelen 64 onaltılık karakter).
* Hesap başına, iptal edilmemiş ve süresi dolmamış en fazla 20 (`active` + `disabled`) anahtar; süresi dolmuş anahtarlar sayılmaz. Bunun aşılması, `reason: active_keys` ve `limit: 20` ile HTTP 409 `key_limit_reached` döndürür; önce bir anahtarı iptal edin. Bu sınır, hesabın tüm kimlikleri, oturumları ve zincirleri genelinde geçerlidir. Anahtar oluşturma ve rotasyonu da 24 saatte 20 ile sınırlıdır; bunun aşılması, `Retry-After: 3600` ile HTTP 429 `rate_limited` döndürür.
* İsteğe bağlı sınır ve süre sonu: `cu_cap` (anahtar için ömür boyu CU sınırı, bu esnek bir sınırdır) ve süre sonu (`expires_in_secs` veya `expires_at`, anahtar politikası tarafından izin verilen maksimum güne kadar) belirtebilirsiniz; süresi dolduğunda veya sınır tükendiğinde sunucu 403 döndürür (JSON-RPC `-32025`, neden `key_expired` veya `key_cap_exhausted`).
* Gizli `api_key` **oluşturma sırasında yalnızca bir kez döndürülür**. Bunu hemen gizli bilgi yöneticinizde veya ortam değişkenlerinizde güvenli bir şekilde saklayın.
* Tek bir API anahtarı, JSON-RPC ve Data API üzerinde desteklenen tüm zincirlerde çalışır.

## Oturumunuzu veya API anahtarınızı mı kaybettiniz?

BlockVectra'da, **bir ajanın hesap kimliği kayıt sırasında kullanılan Ethereum cüzdan adresine bağlıdır**. Oturum token'ınızın süresi dolarsa veya bir API anahtarı kaybolur veya sızdırılırsa, yalnızca o cüzdanı kullanarak tam kontrolü geri kazanabilirsiniz:

1. **Aynı cüzdanla yeniden kimlik doğrulaması yapın**: Bir challenge talep edin, aynı cüzdanla imzalayın ve oturum açma isteğini (`POST /auth/siwe/login`) gönderin. Sunucu imzayı doğrular, `account_created: false` ile mevcut hesapta oturum açar ve yeni bir oturum token'ı düzenler.
2. **Yeni bir API anahtarı oluşturun**: Yeni oturum token'ı ile `{"label": "..."}` ve `Authorization: Bearer <token>` başlığıyla `POST /keys` çağrısı yapın. Uç nokta, oluşturulan anahtar ayrıntılarını `key` içinde ve tek seferlik gizli anahtarı `api_key` içinde içeren HTTP 201 döndürür. Bu anahtarı hemen ortam değişkenlerinizde veya gizli bilgi yöneticinizde saklayın.
3. **Hesap için tüm anahtarları listeleyin**:
   * Uç nokta: `GET /keys`
   * Başlık: `Authorization: Bearer <token>`
   * Sorgu parametresi: isteğe bağlı `include_revoked=true` (`true` olduğunda iptal edilen anahtarları içerir; varsayılan olarak yalnızca aktif/devre dışı anahtarları getirir).
   * Yanıt: `{"items": [...]}` JSON ile HTTP 200. `items` dizisindeki her öğe şunları içerir:
     * `key_id`: benzersiz anahtar tanımlayıcısı (dize)
     * `label`: anahtar etiketi (dize veya `null`)
     * `status`: durum (`"active"`, `"disabled"` veya `"revoked"`)
     * `created_at`: oluşturma zaman damgası (ISO 8601 dizesi)
     * `revoked_at`: iptal zaman damgası (dize veya iptal edilmemişse `null`)
4. **Kullanılmayan veya güvenliği ihlal edilmiş anahtarları iptal edin**:
   * Uç nokta: `POST /keys/{key_id}/revoke` (not: yolda hedef `key_id` ile `POST` kullanır; boş istek gövdesi)
   * Başlık: `Authorization: Bearer <token>`
   * Davranış: idempotent; `active` veya `disabled` durumundaki anahtarların her ikisi de iptal edilebilir. Zaten iptal edilmişse, değişmeden HTTP 200 döndürür. İptal işleminden sonra, bu anahtarı kullanan istekler reddedilir.
   * Yanıt: İptal edilen anahtar nesnesini döndüren HTTP 200 (alanlar yukarıdaki anahtar nesnesiyle eşleşir; `status: "revoked"` ve `revoked_at` içinde bir zaman damgası bulunur).

> **Anahtar ve gizli bilgi güvenliği**
>
> Cüzdan özel anahtarlarını ve API anahtarlarını ortam değişkenlerinde veya bir gizli bilgi yöneticisinde saklayın. Bunları asla kod depolarına commit etmeyin, loglara yazmayın veya yapay zeka sohbet konuşmalarına yapıştırmayın.


## Güvenlik önerileri

* **Kısa süreli anahtarlar kullanın ve işiniz bittiğinde iptal edin**: Otomatik veya geçici görevler için `expires_in_secs` ile kısa ömürlü anahtarlar oluşturun ve çalışma tamamlandığında bunları hemen `POST /keys/{key_id}/revoke` aracılığıyla iptal edin.

## Kayıt hız sınırları (signup\_rate\_limited)

Hesap oluşturma kayıt hız sınırlarına tabidir. IP başına token kovası 100 hesap kapasitesine sahiptir ve IPv4 adresi veya IPv6 /64 öneki başına saatte 100 hesap hızında yenilenir; SIWE ve OAuth kaydı tarafından paylaşılır:

* Kayıt sınırları aşıldığında, `POST /auth/siwe/login` beklenecek saniye sayısını belirten bir `Retry-After` başlığı ile HTTP 429 `signup_rate_limited` döndürür.
* `reason` alanı sınır kapsamını ayırt eder:
  * `per_ip`: istekte bulunan IP öneki için kayıt bütçesi tükenmiştir.
  * `global`: toplam platform kayıt sınırı tükenmiştir.
* Kayıt hız sınırları yalnızca yeni hesap kayıtlarını değerlendirir. Mevcut hesapların oturum açması kayıt hız sınırları tarafından engellenmez.

## İlgili kaynaklar

* Anahtarsız MCP sunucusu ve makine tarafından okunabilir bağlam dosyaları hakkında bilgi edinmek için [AI Agent entegrasyon rehberini](https://docs.blockvectra.com/tr/guides/ai-agents/) okuyun.
* Çok dilli istemci örnekleri için [Hızlı Başlangıç](https://docs.blockvectra.com/tr/quickstart/) sayfasını inceleyin.
* Tam hata kodları, nedenleri ve otomatik kurtarma eylemleri için [Hata referansını](https://docs.blockvectra.com/tr/errors/) inceleyin.

## Sonraki adımlar

* `x-api-key: $BLOCKVECTRA_API_KEY` ile ilk JSON-RPC veya Data API çağrınızı gönderin.
* `GET /v1/account` kullanarak [hesap bakiyesini ve limitlerini kontrol edin](https://docs.blockvectra.com/tr/guides/ai-agents/#query-balance-get-v1account).
* Bakiyeyi korumak için [Agent programatik bakiye yükleme rehberini](https://docs.blockvectra.com/tr/guides/agent-topup/) takip edin.
