Overview
Every protected endpoint in GRAIL takes a single header: x-api-key. There is one partner scope (PARTNER) — one key grants access to everything a partner can do (trades, redemptions, users).
Keys are minted via challenge-response: you sign a short-lived message with your partner wallet’s private key, and exchange the signature for a key. GRAIL stores only a hash — raw keys are shown once.
The two endpoints in the auth flow are the only /v1/* routes that do not themselves require authentication. They are rate-limited to 10 requests per minute per IP.
Prerequisites
Before you start, you need:
- Your partner ID (UUID) — given to you by ORO at onboarding
- Your partner wallet keypair — the private key that signs the challenge. This must correspond to a wallet registered under your partner (the wallet is the one ORO whitelists on-chain).
- A way to sign a UTF-8 message with Ed25519 and produce a base64 signature. The snippet below uses
tweetnacl.
The signature format is base64, not base58. Submitting base58 produces 400 invalid_signature. This is the most common onboarding mistake.
The flow at a glance
Challenges expire after 2 minutes. Complete signing and exchange before then.
Step 1 — Request a challenge
Response:
Save challenge_id and the full message string.
Step 2 — Sign the challenge message
Sign the message (UTF-8 bytes) with the partner wallet’s Ed25519 private key. Encode the signature as base64.
You can use any Ed25519 signing library in any language — tweetnacl is just the common JS choice. The only hard requirement is that the final output is base64 of the 64-byte signature.
Step 3 — Exchange the signature for an API key
Response:
api_key is shown only once. Store it somewhere you can retrieve it later (secret manager, encrypted env var). If lost, revoke it via Revoke API Key and mint a new one.
Step 4 — Verify the key works
Hit a protected endpoint. The denominations list is a safe one — read-only, no side effects:
If you get a JSON body with a denominations array, you’re authenticated. If you get 401 unauthorized, check that the header name is x-api-key (lowercase, with hyphen) and that you pasted the full key including the grail_partner_ prefix.
Managing keys
List your keys
Returns metadata for every key minted against your partner’s wallet (including revoked ones). Raw keys are never returned.
Revoke a key
Revocation is immediate and permanent. Use this when a key is compromised or no longer needed.
It’s common to mint a separate key per environment or per deployed service (e.g., prod-backend, staging-cron). Label them clearly via key_name — the list endpoint shows the label, which makes tracking easier.
Troubleshooting
Next steps
With your API key working, proceed to Creating & Managing Users.