Skip to content

Authentication

Every /api/v1 data endpoint requires authentication. The one exception is the health check, GET /api/v1/health, which is public and useful for connectivity tests. Unauthenticated requests to protected endpoints receive 401 UNAUTHORIZED.

Qeychain authenticates requests with a JSON Web Token (JWT) session access token sent in the standard Authorization header:

GET /api/v1/wallets/{address}/dashboard HTTP/1.1
Host: api.qeychain.xyz
Authorization: Bearer <access_token>

The middleware reads the token from the Authorization: Bearer header. A few details worth knowing:

  • The token is a signed session token, not an API key. It encodes the user identity and an expiry.
  • Access tokens are short-lived — they expire 15 minutes after issue.
  • A longer-lived refresh token (valid 7 days) is issued alongside it so you can obtain a new access token without re-entering credentials.

Sign up with an email and password. Signup creates the account and sends a verification email — you must verify your email before you can log in.

Terminal window
curl -X POST https://api.qeychain.xyz/api/v1/auth/signup \
-H "Content-Type: application/json" \
-d '{
"email": "advisor@example.com",
"password": "a-strong-password",
"full_name": "Jordan Advisor",
"accepted_terms": true
}'

Once your email is verified, log in to receive your tokens. The access and refresh tokens are returned in the response body (and also set as cookies).

Terminal window
curl -X POST https://api.qeychain.xyz/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "advisor@example.com",
"password": "a-strong-password"
}'
{
"success": true,
"data": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"email": "advisor@example.com",
"full_name": "Jordan Advisor",
"email_verified": true,
"plan_name": "professional",
"plan_expired": false,
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}

Send the access_token value as a Bearer credential:

Terminal window
curl https://api.qeychain.xyz/api/v1/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/dashboard \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

To confirm which identity a token belongs to, call:

Terminal window
curl https://api.qeychain.xyz/api/v1/auth/me \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

When the 15-minute access token expires, requests return 401 UNAUTHORIZED. Obtain a fresh access token from the refresh endpoint, which reads the refresh_token from a cookie:

Terminal window
curl -X POST https://api.qeychain.xyz/api/v1/auth/refresh \
--cookie "refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

The endpoint issues a new access token as an access_token cookie. Clients that cannot use cookies can simply log in again to mint a new token pair.

For server-to-server access, Qeychain issues partner keys. Present a partner key in the X-Partner-Key header (it may also be passed as a partner_key query parameter):

Terminal window
curl https://api.qeychain.xyz/api/v1/wallets/0xd8dA...6045/positions \
-H "X-Partner-Key: <your-partner-key>"

Partner keys are provisioned by Qeychain — contact us to enroll. Partner applications can also sign users in through a one-time magic-link SSO flow, which returns the same JWT tokens described above.

Beyond session tokens, Qeychain supports database-backed API keys for programmatic access with granular, SOC 2-friendly permissions. You create and manage them under /api/v1/api-keys using your Bearer token.

Create a key (the full key value is returned only once — store it securely):

Terminal window
curl -X POST https://api.qeychain.xyz/api/v1/api-keys \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{
"name": "reporting-service",
"permissions": ["read:holdings", "read:tax-reports"]
}'
{
"success": true,
"data": {
"id": "a1b2c3d4-0000-0000-0000-000000000000",
"key_prefix": "qk_1a2b3c4d",
"name": "reporting-service",
"permissions": ["read:holdings", "read:tax-reports"],
"is_active": true,
"total_requests": 0,
"key": "qk_1a2b3c4d...full-value-shown-once..."
}
}

Generated keys carry a qk_ prefix and are presented in the X-API-Key header. Available permission strings are:

Permission Grants
read:holdings Read wallet holdings and positions
read:transactions Read transaction history
read:lots Read tax lot details
read:tax-reports Read tax reports and Form 8949
write:portfolios Create and manage portfolios
write:webhooks Create and manage webhooks
admin:api-keys Manage API keys

List, inspect, and revoke keys with the same collection:

Terminal window
# List keys (values are never returned again — only prefixes and metadata)
curl https://api.qeychain.xyz/api/v1/api-keys \
-H "Authorization: Bearer eyJhbGci..."
# Revoke a key permanently
curl -X DELETE "https://api.qeychain.xyz/api/v1/api-keys/a1b2c3d4-0000-0000-0000-000000000000?reason=rotated" \
-H "Authorization: Bearer eyJhbGci..."
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication required"
},
"meta": {
"request_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"timestamp": "2026-01-15T09:24:11.482Z",
"version": "1.0.0"
}
}

See Response format for the full error contract.