Skip to main content
POST
/
v1
/
auth
/
api-key
curl -X POST https://grail-stack-dev.onrender.com/v1/auth/api-key \
  -H "Content-Type: application/json" \
  -d '{
    "challenge_id": "ch_9b5a3a12-7b89-4d6c-9f6a-18c52d3f9e3a",
    "signature": "r8JvAw...base64...Q==",
    "key_name": "integration test"
  }'
{
  "api_key": "grail_partner_7f3a...<64 hex chars total>",
  "key_id": "0c9bd7e4-6b2e-4f3a-a9a7-1f6e5d4c3b2a",
  "scope": "PARTNER",
  "key_name": "integration test",
  "wallet_address": "Fd31QxW7RRZwvMfNnhNaPvczJpMh7wyzBTWvtMA66wjN",
  "created_at": "2026-04-19T12:30:45.123Z"
}

Overview

Submits the signature produced in Request Challenge to mint a new API key. On success, the raw API key is returned exactly once — store it somewhere safe. GRAIL only retains a hash.
The returned api_key is shown only once. It cannot be retrieved later — if lost, revoke it and mint a new one.
The signature must be a base64-encoded Ed25519 signature of the challenge message (the full string returned by Step 1). Do not send base58 — that produces 400 invalid_signature. See the Authentication & Setup guide for a signer snippet.
This endpoint is rate-limited to 10 requests per minute per IP. Exceeding the limit returns 429 rate_limited.

Request Body

challenge_id
string
required
The challenge_id returned from POST /v1/auth/challenge.
signature
string
required
Base64-encoded Ed25519 signature of the challenge message, signed by the partner wallet’s private key.
key_name
string
required
Human-readable label for this key (e.g., "production integration", "staging tests"). Shown in the key list.

Response

api_key
string
The raw API key. Format: grail_partner_<64-hex>. Pass this in the x-api-key header on all subsequent requests. Shown only once.
key_id
string
UUID of the key record. Use this to revoke the key later.
scope
string
Always "PARTNER" for keys minted via this endpoint.
key_name
string
Echo of the key_name you supplied.
wallet_address
string
The partner wallet that signed the challenge.
created_at
string
ISO-8601 timestamp of key creation.

Errors

HTTPerrorWhen
400invalid_requestMissing challenge_id, signature, or key_name
400invalid_challengeChallenge not found, or already used
400challenge_expiredChallenge older than 2 minutes
400wallet_revokedPartner wallet’s status is revoked
400invalid_signatureEd25519 verification failed — most often because the signature was sent as base58
429rate_limitedMore than 10 requests in the last minute from this IP
curl -X POST https://grail-stack-dev.onrender.com/v1/auth/api-key \
  -H "Content-Type: application/json" \
  -d '{
    "challenge_id": "ch_9b5a3a12-7b89-4d6c-9f6a-18c52d3f9e3a",
    "signature": "r8JvAw...base64...Q==",
    "key_name": "integration test"
  }'
{
  "api_key": "grail_partner_7f3a...<64 hex chars total>",
  "key_id": "0c9bd7e4-6b2e-4f3a-a9a7-1f6e5d4c3b2a",
  "scope": "PARTNER",
  "key_name": "integration test",
  "wallet_address": "Fd31QxW7RRZwvMfNnhNaPvczJpMh7wyzBTWvtMA66wjN",
  "created_at": "2026-04-19T12:30:45.123Z"
}