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
Base64-encoded Ed25519 signature of the challenge
message, signed by the partner wallet’s private key.Human-readable label for this key (e.g.,
"production integration", "staging tests"). Shown in the key list.Response
The raw API key. Format:
grail_partner_<64-hex>. Pass this in the x-api-key header on all subsequent requests. Shown only once.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"
}
