Mint a PARTNER API key via challenge-response and verify it against a protected endpoint.
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
1. POST /v1/auth/challenge { wallet_address, partner_id } ↓2. GRAIL returns { challenge_id, message, expires_at } ↓3. You sign `message` with the partner wallet's private key → base64 signature ↓4. POST /v1/auth/api-key { challenge_id, signature, key_name } ↓5. GRAIL returns the raw api_key — SHOWN ONCE. Store it.
Challenges expire after 2 minutes. Complete signing and exchange before then.
{ "challenge_id": "ch_9b5a3a12-7b89-4d6c-9f6a-18c52d3f9e3a", "message": "Sign this message to generate an API key for GRAIL: ch_9b5a3a12-7b89-4d6c-9f6a-18c52d3f9e3a", "expires_at": "2026-04-19T12:32:00.000Z"}
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.
import { Keypair } from '@solana/web3.js';import nacl from 'tweetnacl';import bs58 from 'bs58';// Load your partner keypair — secret in base58 is the common CLI export formatconst partner = Keypair.fromSecretKey(bs58.decode(PARTNER_SECRET_BASE58));const message = "Sign this message to generate an API key for GRAIL: ch_9b5a3a12-7b89-4d6c-9f6a-18c52d3f9e3a";const messageBytes = new TextEncoder().encode(message);const sigBytes = nacl.sign.detached(messageBytes, partner.secretKey);const signatureBase64 = Buffer.from(sigBytes).toString("base64");console.log(signatureBase64);
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.
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.
Returns metadata for every key minted against your partner’s wallet (including revoked ones). Raw keys are never returned.
Revoke a key
curl -s -X POST "$BASE/v1/partner/api-keys/$KEY_ID/revoke" \ -H "x-api-key: $API_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
Symptom
Likely cause
400 invalid_signature
Signature sent as base58 instead of base64
400 invalid_challenge
You’re re-using a challenge_id that was already exchanged, or the challenge row doesn’t exist
400 challenge_expired
Took longer than 2 minutes between Step 1 and Step 3. Request a new challenge.
400 wallet_revoked
The partner wallet’s status has been flipped to revoked by ORO. Contact support.
404 wallet_not_found on Step 1
The wallet isn’t registered under the supplied partner_id. Check you’re using the correct pubkey and partner ID.
429 rate_limited
More than 10 requests to /v1/auth/* from this IP in the last minute. Wait, then retry.
401 unauthorized on a protected endpoint after minting a key
Key may have been revoked, or the header name is wrong (must be x-api-key, lowercase with hyphen)
{"docsConfig":{"$schema":"https://mintlify.com/docs.json","theme":"mint","name":"Oro Grail","colors":{"primary":"#00261c","light":"#00261c","dark":"#00261c"},"favicon":"https://mintcdn.com/oro-a5494d30/CRdlCQgjAt0bkkeb/images/oro-finance-logo.png?fit=max\u0026auto=format\u0026n=CRdlCQgjAt0bkkeb\u0026q=85\u0026s=a8a1d535fb3e82aecf71f61c154492c3","logo":{"light":"https://mintcdn.com/oro-a5494d30/CRdlCQgjAt0bkkeb/images/oro-finance-logo.png?fit=max\u0026auto=format\u0026n=CRdlCQgjAt0bkkeb\u0026q=85\u0026s=a8a1d535fb3e82aecf71f61c154492c3","dark":"https://mintcdn.com/oro-a5494d30/CRdlCQgjAt0bkkeb/images/oro-finance-logo.png?fit=max\u0026auto=format\u0026n=CRdlCQgjAt0bkkeb\u0026q=85\u0026s=a8a1d535fb3e82aecf71f61c154492c3"},"api":{"openapi":"openapi.json","mdx":{"server":"https://grail-stack-dev.onrender.com"}},"appearance":{"default":"light","strict":true},"navbar":{"links":[{"label":"Support","href":"mailto:support@oro.finance"}],"primary":{"type":"button","label":"Get Started","href":"https://grail.oro.finance"}},"footer":{"socials":{"website":"https://oro.finance","discord":"https://discord.gg/kb3buk7pgR","x":"https://x.com/orogoldapp"}},"styling":{"latex":false},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"oro-a5494d30","hasAudioNarration":false,"actualSubdomain":"oro-a5494d30","customDomains":["docs.grail.oro.finance"],"gitSource":{"type":"github","owner":"oroweb3","repo":"oro-grail-docs","deployBranch":"main","contentDirectory":"docs","isPrivate":true,"mountPath":"","label":""},"entitlements":{},"buildId":"6ae946832b3cf0697cfc9908:success","clientVersion":"0.0.4143","searchFilterCounts":[{"language":"default","tag":"API Reference","count":22},{"language":"default","tag":"Documentation","count":10},{"language":"default","tag":"Grail Overview","count":7},{"language":"default","tag":"Guides","count":7},{"language":"default","tag":"Trades","count":6},{"language":"default","tag":"Redemptions","count":5},{"language":"default","tag":"Integration Guides","count":5},{"language":"default","tag":"Users","count":3},{"language":"default","tag":"Authentication","count":2},{"language":"default","tag":"Partner","count":2}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableAskAiSearchBar":true},"basePath":"","hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-rbac-roles","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-metadata","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-serve","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"live-agents","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[],"page":{"pagePath":"guides/authentication-and-setup","slug":"/guides/authentication-and-setup","pageMetadata":{"title":"Authentication \u0026 Setup","description":"Mint a PARTNER API key via challenge-response and verify it against a protected endpoint.","href":"/guides/authentication-and-setup"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"Mint a PARTNER API key via challenge-response and verify it against a protected endpoint.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,li:`li`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,tbody:`tbody`,td:`td`,th:`th`,thead:`thead`,tr:`tr`,ul:`ul`,..._provideComponents(),...props.components},{CodeBlock,Heading,Table,Tip,Warning}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Table)_missingMdxReference(`Table`,true);if(!Tip)_missingMdxReference(`Tip`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsx(Heading,{level:`2`,id:`overview`,children:`Overview`}),`\n`,_jsxs(_components.p,{children:[`Every protected endpoint in GRAIL takes a single header: `,_jsx(_components.code,{children:`x-api-key`}),`. There is one partner scope (`,_jsx(_components.code,{children:`PARTNER`}),`) — one key grants access to everything a partner can do (trades, redemptions, users).`]}),`\n`,_jsxs(_components.p,{children:[`Keys are minted via `,_jsx(_components.strong,{children:`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.`]}),`\n`,_jsxs(_components.p,{children:[`The two endpoints in the auth flow are the only `,_jsx(_components.code,{children:`/v1/*`}),` routes that do not themselves require authentication. They are rate-limited to `,_jsx(_components.strong,{children:`10 requests per minute per IP`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`prerequisites`,children:`Prerequisites`}),`\n`,_jsx(_components.p,{children:`Before you start, you need:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Your `,_jsx(_components.strong,{children:`partner ID`}),` (UUID) — given to you by ORO at onboarding`]}),`\n`,_jsxs(_components.li,{children:[`Your `,_jsx(_components.strong,{children:`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).`]}),`\n`,_jsxs(_components.li,{children:[`A way to sign a UTF-8 message with Ed25519 and produce a `,_jsx(_components.strong,{children:`base64`}),` signature. The snippet below uses `,_jsx(_components.code,{children:`tweetnacl`}),`.`]}),`\n`]}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[`The signature format is `,_jsx(_components.strong,{children:`base64`}),`, not base58. Submitting base58 produces `,_jsx(_components.code,{children:`400 invalid_signature`}),`. This is the most common onboarding mistake.`]})}),`\n`,_jsx(Heading,{level:`2`,id:`the-flow-at-a-glance`,children:`The flow at a glance`}),`\n`,_jsx(CodeBlock,{numberOfLines:`10`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`10`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`1. POST /v1/auth/challenge { wallet_address, partner_id }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` ↓`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`2. GRAIL returns { challenge_id, message, expires_at }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` ↓`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:\"3. You sign `message` with the partner wallet's private key → base64 signature\"})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` ↓`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`4. POST /v1/auth/api-key { challenge_id, signature, key_name }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` ↓`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`5. GRAIL returns the raw api_key — SHOWN ONCE. Store it.`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsxs(_components.p,{children:[`Challenges expire after `,_jsx(_components.strong,{children:`2 minutes`}),`. Complete signing and exchange before then.`]}),`\n`,_jsx(Heading,{level:`2`,id:`step-1-—-request-a-challenge`,children:`Step 1 — Request a challenge`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`7`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`7`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`BASE`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`https://grail-stack-dev.onrender.com`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`PARTNER_WALLET`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`Fd31QxW7RRZwvMfNnhNaPvczJpMh7wyzBTWvtMA66wjN`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`PARTNER_ID`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`d8a8df53-36ca-4f4d-96ef-cf0a49d51e5d`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`curl`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -s`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -X`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` POST`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$BASE`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`/v1/auth/challenge\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"Content-Type: application/json\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -d`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"{`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:`\\\\\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`wallet_address`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:`\\\\\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`:`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:`\\\\\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$PARTNER_WALLET`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:`\\\\\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`,`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:`\\\\\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`partner_id`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:`\\\\\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`:`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:`\\\\\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$PARTNER_ID`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:`\\\\\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`}\"`})]}),`\n`]})})}),`\n`,_jsx(_components.p,{children:`Response:`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`5`,language:`json`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`json`,children:_jsxs(_components.code,{language:`json`,numberOfLines:`5`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`{`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"challenge_id\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"ch_9b5a3a12-7b89-4d6c-9f6a-18c52d3f9e3a\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"message\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"Sign this message to generate an API key for GRAIL: ch_9b5a3a12-7b89-4d6c-9f6a-18c52d3f9e3a\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"expires_at\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"2026-04-19T12:32:00.000Z\"`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}`})}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`Save `,_jsx(_components.code,{children:`challenge_id`}),` and the full `,_jsx(_components.code,{children:`message`}),` string.`]}),`\n`,_jsx(Heading,{level:`2`,id:`step-2-—-sign-the-challenge-message`,children:`Step 2 — Sign the challenge message`}),`\n`,_jsxs(_components.p,{children:[`Sign the `,_jsx(_components.code,{children:`message`}),` (UTF-8 bytes) with the partner wallet’s Ed25519 private key. Encode the signature as `,_jsx(_components.strong,{children:`base64`}),`.`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`14`,language:`typescript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`typescript`,children:_jsxs(_components.code,{language:`typescript`,numberOfLines:`14`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`import`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` { `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`Keypair`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` } `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`from`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` '@solana/web3.js'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`import`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:` nacl`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:` from`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'tweetnacl'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`import`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:` bs58`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:` from`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'bs58'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`// Load your partner keypair — secret in base58 is the common CLI export format`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`const`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#4FC1FF`},children:` partner`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:` Keypair`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`fromSecretKey`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`bs58`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`decode`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#4FC1FF`},children:`PARTNER_SECRET_BASE58`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`));`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`const`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#4FC1FF`},children:` message`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"Sign this message to generate an API key for GRAIL: ch_9b5a3a12-7b89-4d6c-9f6a-18c52d3f9e3a\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`const`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#4FC1FF`},children:` messageBytes`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` new`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:` TextEncoder`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`().`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`encode`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`message`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`);`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`const`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#4FC1FF`},children:` sigBytes`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:` nacl`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`sign`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`detached`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`messageBytes`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`partner`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`secretKey`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`);`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`const`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#4FC1FF`},children:` signatureBase64`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:` Buffer`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`from`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`sigBytes`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`).`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`toString`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"base64\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`);`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`console`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`log`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`signatureBase64`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`);`})]}),`\n`]})})}),`\n`,_jsx(Tip,{children:_jsxs(_components.p,{children:[`You can use any Ed25519 signing library in any language — `,_jsx(_components.code,{children:`tweetnacl`}),` is just the common JS choice. The only hard requirement is that the final output is base64 of the 64-byte signature.`]})}),`\n`,_jsx(Heading,{level:`2`,id:`step-3-—-exchange-the-signature-for-an-api-key`,children:`Step 3 — Exchange the signature for an API key`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`7`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`7`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`curl`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -s`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -X`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` POST`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$BASE`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`/v1/auth/api-key\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"Content-Type: application/json\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -d`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` '{`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"challenge_id\": \"ch_9b5a3a12-7b89-4d6c-9f6a-18c52d3f9e3a\",`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"signature\": \"\u003cbase64 from Step 2>\",`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"key_name\": \"integration test\"`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` }'`})}),`\n`]})})}),`\n`,_jsx(_components.p,{children:`Response:`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`8`,language:`json`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`json`,children:_jsxs(_components.code,{language:`json`,numberOfLines:`8`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`{`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"api_key\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"grail_partner_7f3a...\u003c64 hex chars>\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"key_id\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"0c9bd7e4-6b2e-4f3a-a9a7-1f6e5d4c3b2a\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"scope\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"PARTNER\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"key_name\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"integration test\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"wallet_address\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"Fd31QxW7RRZwvMfNnhNaPvczJpMh7wyzBTWvtMA66wjN\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"created_at\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"2026-04-19T12:30:45.123Z\"`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}`})}),`\n`]})})}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[_jsx(_components.code,{children:`api_key`}),` is shown `,_jsx(_components.strong,{children:`only once`}),`. Store it somewhere you can retrieve it later (secret manager, encrypted env var). If lost, revoke it via `,_jsx(_components.a,{href:`/api-reference/partner/revoke-api-key`,children:`Revoke API Key`}),` and mint a new one.`]})}),`\n`,_jsx(Heading,{level:`2`,id:`step-4-—-verify-the-key-works`,children:`Step 4 — Verify the key works`}),`\n`,_jsx(_components.p,{children:`Hit a protected endpoint. The denominations list is a safe one — read-only, no side effects:`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`2`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`2`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`curl`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -s`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$BASE`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`/v1/denominations?country=PK\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"x-api-key: `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$API_KEY`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`If you get a JSON body with a `,_jsx(_components.code,{children:`denominations`}),` array, you’re authenticated. If you get `,_jsx(_components.code,{children:`401 unauthorized`}),`, check that the header name is `,_jsx(_components.code,{children:`x-api-key`}),` (lowercase, with hyphen) and that you pasted the full key including the `,_jsx(_components.code,{children:`grail_partner_`}),` prefix.`]}),`\n`,_jsx(Heading,{level:`2`,id:`managing-keys`,children:`Managing keys`}),`\n`,_jsx(Heading,{level:`3`,id:`list-your-keys`,children:`List your keys`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`curl`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -s`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$BASE`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`/v1/partner/api-keys\"`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"x-api-key: `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$API_KEY`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"`})]}),`\n`]})})}),`\n`,_jsx(_components.p,{children:`Returns metadata for every key minted against your partner’s wallet (including revoked ones). Raw keys are never returned.`}),`\n`,_jsx(Heading,{level:`3`,id:`revoke-a-key`,children:`Revoke a key`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`2`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`2`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`curl`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -s`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -X`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` POST`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$BASE`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`/v1/partner/api-keys/`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$KEY_ID`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`/revoke\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"x-api-key: `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$API_KEY`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"`})]}),`\n`]})})}),`\n`,_jsx(_components.p,{children:`Revocation is immediate and permanent. Use this when a key is compromised or no longer needed.`}),`\n`,_jsx(Tip,{children:_jsxs(_components.p,{children:[`It’s common to mint a separate key per environment or per deployed service (e.g., `,_jsx(_components.code,{children:`prod-backend`}),`, `,_jsx(_components.code,{children:`staging-cron`}),`). Label them clearly via `,_jsx(_components.code,{children:`key_name`}),` — the list endpoint shows the label, which makes tracking easier.`]})}),`\n`,_jsx(Heading,{level:`2`,id:`troubleshooting`,children:`Troubleshooting`}),`\n`,_jsxs(Table,{children:[_jsx(_components.thead,{children:_jsxs(_components.tr,{children:[_jsx(_components.th,{children:`Symptom`}),_jsx(_components.th,{children:`Likely cause`})]})}),_jsxs(_components.tbody,{children:[_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`400 invalid_signature`})}),_jsx(_components.td,{children:`Signature sent as base58 instead of base64`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`400 invalid_challenge`})}),_jsxs(_components.td,{children:[`You’re re-using a `,_jsx(_components.code,{children:`challenge_id`}),` that was already exchanged, or the challenge row doesn’t exist`]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`400 challenge_expired`})}),_jsx(_components.td,{children:`Took longer than 2 minutes between Step 1 and Step 3. Request a new challenge.`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`400 wallet_revoked`})}),_jsxs(_components.td,{children:[`The partner wallet’s status has been flipped to `,_jsx(_components.code,{children:`revoked`}),` by ORO. Contact support.`]})]}),_jsxs(_components.tr,{children:[_jsxs(_components.td,{children:[_jsx(_components.code,{children:`404 wallet_not_found`}),` on Step 1`]}),_jsxs(_components.td,{children:[`The wallet isn’t registered under the supplied `,_jsx(_components.code,{children:`partner_id`}),`. Check you’re using the correct pubkey and partner ID.`]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`429 rate_limited`})}),_jsxs(_components.td,{children:[`More than 10 requests to `,_jsx(_components.code,{children:`/v1/auth/*`}),` from this IP in the last minute. Wait, then retry.`]})]}),_jsxs(_components.tr,{children:[_jsxs(_components.td,{children:[_jsx(_components.code,{children:`401 unauthorized`}),` on a protected endpoint after minting a key`]}),_jsxs(_components.td,{children:[`Key may have been revoked, or the header name is wrong (must be `,_jsx(_components.code,{children:`x-api-key`}),`, lowercase with hyphen)`]})]})]})]}),`\n`,_jsx(Heading,{level:`2`,id:`next-steps`,children:`Next steps`}),`\n`,_jsxs(_components.p,{children:[`With your API key working, proceed to `,_jsx(_components.a,{href:`/guides/creating-and-managing-users`,children:`Creating \u0026 Managing Users`}),`.`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Authentication \u0026 Setup","description":"Mint a PARTNER API key via challenge-response and verify it against a protected endpoint.","href":"/guides/authentication-and-setup"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Overview","slug":"overview","depth":2,"children":[]},{"title":"Prerequisites","slug":"prerequisites","depth":2,"children":[]},{"title":"The flow at a glance","slug":"the-flow-at-a-glance","depth":2,"children":[]},{"title":"Step 1 — Request a challenge","slug":"step-1-—-request-a-challenge","depth":2,"children":[]},{"title":"Step 2 — Sign the challenge message","slug":"step-2-—-sign-the-challenge-message","depth":2,"children":[]},{"title":"Step 3 — Exchange the signature for an API key","slug":"step-3-—-exchange-the-signature-for-an-api-key","depth":2,"children":[]},{"title":"Step 4 — Verify the key works","slug":"step-4-—-verify-the-key-works","depth":2,"children":[]},{"title":"Managing keys","slug":"managing-keys","depth":2,"children":[{"title":"List your keys","slug":"list-your-keys","depth":3,"children":[]},{"title":"Revoke a key","slug":"revoke-a-key","depth":3,"children":[]}]},{"title":"Troubleshooting","slug":"troubleshooting","depth":2,"children":[]},{"title":"Next steps","slug":"next-steps","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"oro-a5494d30","siteName":"Oro Grail","title":"Authentication \u0026 Setup - Oro Grail","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-04-20T10:27:39.830Z","originalFileLocation":"guides/authentication-and-setup.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=\"light\";if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"light\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Mint a PARTNER API key via challenge-response and verify it against a protected endpoint.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Oro Grail\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/oro-a5494d30/zm95Ikh3MBH1y1Nr/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/oro-a5494d30/zm95Ikh3MBH1y1Nr/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/oro-a5494d30/zm95Ikh3MBH1y1Nr/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/oro-a5494d30/zm95Ikh3MBH1y1Nr/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/oro-a5494d30/zm95Ikh3MBH1y1Nr/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/oro-a5494d30/zm95Ikh3MBH1y1Nr/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/oro-a5494d30/zm95Ikh3MBH1y1Nr/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/oro-a5494d30/zm95Ikh3MBH1y1Nr/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/oro-a5494d30/zm95Ikh3MBH1y1Nr/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Authentication \u0026amp; Setup - Oro Grail\">\u003cmeta data-mint-head property=\"og:description\" content=\"Mint a PARTNER API key via challenge-response and verify it against a protected endpoint.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Oro Grail\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.grail.oro.finance/guides/authentication-and-setup\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://oro-a5494d30.mintlify.site/_mintlify/api/og/v2?division=Integration+Guides\u0026amp;title=Authentication+%26+Setup\u0026amp;description=Mint+a+PARTNER+API+key+via+challenge-response+and+verify+it+against+a+protected+endpoint.\u0026amp;theme=304c9bec0b792d02be331269\u0026amp;signature=6f8b1a74b4e949eadf42ef9b2be65ebacb5383aed5e2d9c82b5ba6e9d9ea9769\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Authentication \u0026amp; Setup - Oro Grail\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Mint a PARTNER API key via challenge-response and verify it against a protected endpoint.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://oro-a5494d30.mintlify.site/_mintlify/api/og/v2?division=Integration+Guides\u0026amp;title=Authentication+%26+Setup\u0026amp;description=Mint+a+PARTNER+API+key+via+challenge-response+and+verify+it+against+a+protected+endpoint.\u0026amp;theme=304c9bec0b792d02be331269\u0026amp;signature=6f8b1a74b4e949eadf42ef9b2be65ebacb5383aed5e2d9c82b5ba6e9d9ea9769\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/oro-a5494d30/zm95Ikh3MBH1y1Nr/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Oro Grail\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#00261c\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.grail.oro.finance/guides/authentication-and-setup\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/guides/authentication-and-setup.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.grail.oro.finance/#organization\",\"name\":\"Oro Grail\",\"url\":\"https://docs.grail.oro.finance\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/oro-a5494d30/CRdlCQgjAt0bkkeb/images/oro-finance-logo.png?fit=max\u0026auto=format\u0026n=CRdlCQgjAt0bkkeb\u0026q=85\u0026s=a8a1d535fb3e82aecf71f61c154492c3\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.grail.oro.finance#website\",\"name\":\"Oro Grail\",\"url\":\"https://docs.grail.oro.finance\",\"publisher\":{\"@id\":\"https://docs.grail.oro.finance/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.grail.oro.finance/guides/authentication-and-setup#webpage\",\"url\":\"https://docs.grail.oro.finance/guides/authentication-and-setup\",\"name\":\"Authentication \u0026 Setup\",\"description\":\"Mint a PARTNER API key via challenge-response and verify it against a protected endpoint.\",\"dateModified\":\"2026-04-20T10:27:39.830Z\",\"isPartOf\":{\"@id\":\"https://docs.grail.oro.finance#website\"},\"breadcrumb\":{\"@id\":\"https://docs.grail.oro.finance/guides/authentication-and-setup#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.grail.oro.finance/guides/authentication-and-setup#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Oro Grail\",\"item\":\"https://docs.grail.oro.finance\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Authentication \u0026 Setup\",\"item\":\"https://docs.grail.oro.finance/guides/authentication-and-setup\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://docs.grail.oro.finance/guides/authentication-and-setup#article\",\"headline\":\"Authentication \u0026 Setup\",\"name\":\"Authentication \u0026 Setup\",\"description\":\"Mint a PARTNER API key via challenge-response and verify it against a protected endpoint.\",\"url\":\"https://docs.grail.oro.finance/guides/authentication-and-setup\",\"mainEntityOfPage\":{\"@id\":\"https://docs.grail.oro.finance/guides/authentication-and-setup#webpage\"},\"dateModified\":\"2026-04-20T10:27:39.830Z\",\"publisher\":{\"@id\":\"https://docs.grail.oro.finance/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.grail.oro.finance#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Oro Grail\",\"publisher\":{\"@type\":\"Organization\",\"name\":\"Oro Grail\"},\"provider\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","agentPointerHref":"/guides/authentication-and-setup.md","siteName":"Oro Grail","nav":{"global":null,"tabs":[{"tab":"Documentation","pages":[{"title":"Documentation","href":""}]},{"tab":"Guides","groups":[{"group":"Guides","pages":[{"title":"Integration Guides","description":"Step-by-step guides for integrating with the Oro GRAIL API.","href":"/guides/overview"}]},{"group":"Integration Guides","pages":[{"title":"Authentication \u0026 Setup","href":"/guides/authentication-and-setup"},{"title":"Creating \u0026 Managing Users","description":"Register end-users under your partner with flexible-JSON KYC. Users must reach kyc_level: full before they can trade or redeem.","href":"/guides/creating-and-managing-users"},{"title":"Buying Gold","href":"/guides/buying-gold"},{"title":"Selling Gold","href":"/guides/selling-gold"},{"title":"Redeeming Physical Gold","href":"/guides/redeeming-physical-gold"}]}]},{"tab":"API Reference","pages":[{"title":"API Reference","href":"/api-reference/overview"}]}]},"basePath":"","title":"Authentication \u0026 Setup - Oro Grail","buildId":"dpl_Fyv1NbzwP68g2bEH2Roy98uY1zmE","deploymentId":"dpl_Fyv1NbzwP68g2bEH2Roy98uY1zmE","dynamics":[],"chunks":[]}