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"
}
Authentication
Create API Key
Step 2 of the API key mint flow. Exchanges a signed challenge for a PARTNER scope API key.
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
string
required
The
challenge_id returned from POST /v1/auth/challenge.string
required
Base64-encoded Ed25519 signature of the challenge
message, signed by the partner wallet’s private key.string
required
Human-readable label for this key (e.g.,
"production integration", "staging tests"). Shown in the key list.Response
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.string
UUID of the key record. Use this to revoke the key later.
string
Always
"PARTNER" for keys minted via this endpoint.string
Echo of the
key_name you supplied.string
The partner wallet that signed the challenge.
string
ISO-8601 timestamp of key creation.
Errors
| HTTP | error | When |
|---|---|---|
| 400 | invalid_request | Missing challenge_id, signature, or key_name |
| 400 | invalid_challenge | Challenge not found, or already used |
| 400 | challenge_expired | Challenge older than 2 minutes |
| 400 | wallet_revoked | Partner wallet’s status is revoked |
| 400 | invalid_signature | Ed25519 verification failed — most often because the signature was sent as base58 |
| 429 | rate_limited | More 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"
}
