Authentication Guide
Bound supports three authentication methods depending on the account type:
BIP322
External wallet users
Sign a message with a Bitcoin wallet
Passkey (WebAuthn)
Bound Auth accounts
Authenticate with a device passkey (Face ID, Touch ID, hardware key)
SRP
Bound Auth accounts
Authenticate with a password using the Secure Remote Password protocol
All methods return the same JWT pair, consisting of an access token (10 min) and a refresh token (7 days), used to authorize subsequent API calls.
Using tokens
Authorize API calls
Include the access token in the Authorization header for all protected endpoints:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...Refresh when expired
Each refresh token is single-use. Replace both tokens on every successful refresh call.
POST /api/auth/refresh-tokenRequest body:
{ "refreshToken": "eyJhbGci..." }Response:
BIP322 (External Wallet)
For users connecting an external Bitcoin wallet. Bound verifies ownership of the wallet address via a BIP322 signature.
Step 1 - Sign a message
Ask the user to sign a timestamp (e.g. current Unix ms) using their Bitcoin wallet.
Step 2 - Authenticate
Request body:
Response:
Passkey (WebAuthn)
For Bound Auth accounts registered with a passkey. Uses the browser WebAuthn API (navigator.credentials.get()).
Step 1 - Get a challenge
Response:
Step 2 - Sign and login
Pass the challenge to navigator.credentials.get(), then send the assertion to:
Request body:
Response:
SRP (Password-based)
For Bound Auth accounts registered with a password. The SRP protocol ensures the password is never sent to the server in plaintext.
Step 1 - Init SRP session
Request body:
Response:
Step 2 - Verify client proof
Using salt and serverPublic (B) from Step 1, compute the SRP values locally:
x = H(salt || H(email:password))A = g^a mod N(randoma)M1 = H(H(N)⊕H(g) | H(email) | salt | A | B | K)
Then send:
Request body:
Response (2FA disabled):
Response (2FA enabled):
Step 3 (if 2FA enabled) - Verify TOTP code
Request body:
Response: Same structure as the 2FA-disabled response above.
Error codes
4002
Invalid Token
401
JWT is malformed or cannot be parsed
4003
Token Expired
401
JWT has passed its expiration time
4004
Token Not Found
401
No Authorization header provided
4005
Invalid Refresh Token
400
Refresh token is invalid or expired
4006
User Not Found
401
JWT valid but user doesn't exist
4007
Signature Verification Failed
400
BIP322 verification failed
4008
Wallet Creation Failed
400
Wallet creation failed during auth
4009
Invalid Taproot Address
400
Address doesn't match taproot format
Server-to-Server / Non-Browser Requests
By default, Bound's API is protected by strict WAF (Web Application Firewall) rules that block automated tools, scripts, and server-to-server requests (such as curl, Postman, or backend services), returning an HTML 403 Forbidden error.
If you are accessing the Bound API directly from a server or terminal, you must include a dedicated API key in your request headers to bypass the WAF check.
WAF Bypass Header
Include the following header in every API request:
X-API-Key
YOUR_ASSIGNED_API_KEY
Provided by the Bound team
Example via cURL
Last updated