Skip to content

Authentication

The ORD.NET API is wallet-authenticated. There are no API keys. You do not register an app or apply for credentials. The wallet signs a fresh challenge, the server verifies it, and you get a bearer token to use on subsequent requests.

  1. Ask the server for a challenge: POST /auth/challenge.
  2. Have the wallet sign every message in the challenge.
  3. Submit the signatures: POST /auth/verify.
  4. Use the returned sessionToken as a bearer token on subsequent requests.

Creates a new auth flow with one challenge per address that needs to sign.

FieldTypeRequiredDescription
ordinalsAddressstringyesWallet’s ordinals address.
paymentAddressstringyesWallet’s payment address. This is the address checked for the funding requirement during /auth/verify.

Provider and wallet visibility are not request fields. When this flow links a new wallet binding to an existing signed-in profile, the new binding follows that profile’s existing public/private wallet visibility default.

{
"ordinalsAddress": "bc1p...",
"paymentAddress": "bc1q..."
}
FieldTypeDescription
authRequestIdstring (UUID)Pass back to /auth/verify.
challengesarrayOne entry per address that needs to sign. At least one.
challenges[].challengeIdstring (UUID)Pass back to /auth/verify.
challenges[].messagestringThe exact text to sign.
challenges[].addressstringWallet address that should sign this message.
challenges[].rolestringOne of ordinals, payment.
{
"authRequestId": "11111111-1111-1111-1111-111111111111",
"challenges": [
{
"challengeId": "22222222-2222-2222-2222-222222222222",
"message": "Sign in to ord.net at 2026-05-08T18:00:00Z (nonce: ...)",
"address": "bc1p...",
"role": "ordinals"
},
{
"challengeId": "33333333-3333-3333-3333-333333333333",
"message": "Sign in to ord.net at 2026-05-08T18:00:00Z (nonce: ...)",
"address": "bc1q...",
"role": "payment"
}
]
}
  • 200: challenge created.
  • 400: malformed addresses or request body.
  • 401: signed-in browser session is stale.
  • 403: signed-in browser request came from an invalid origin.
  • 409: wallet is already linked or cannot be linked to this profile.
  • 429: rate limited.

See Errors and rate limits for response shapes.

Verifies one or more signed challenges and issues a session token on success.

FieldTypeRequiredDescription
authRequestIdstring (UUID)yesThe authRequestId from /auth/challenge.
verificationsarrayyes (min 1, max 2)One entry per signed challenge.
verifications[].challengeIdstring (UUID)yesThe challengeId from /auth/challenge.
verifications[].signaturestringyesHex-encoded BIP-322 simple signature for message. Max 8192 characters.
verifications[].addressstringyesThe address that produced the signature.
{
"authRequestId": "11111111-1111-1111-1111-111111111111",
"verifications": [
{
"challengeId": "22222222-2222-2222-2222-222222222222",
"signature": "<hex-signature>",
"address": "bc1p..."
},
{
"challengeId": "33333333-3333-3333-3333-333333333333",
"signature": "<hex-signature>",
"address": "bc1q..."
}
]
}
FieldTypeDescription
profileobjectYour profile metadata.
profile.idstringStable profile id.
profile.usernamestring | nullProfile username.
profile.displayNamestring | nullProfile display name.
profile.biostring | nullProfile bio.
profile.avatarUrlstring | nullProfile avatar image URL.
profile.bannerUrlstring | nullProfile banner image URL.
walletBindingsarrayWallet bindings available to this API session.
walletBindings[].walletBindingIdstring (UUID)Send this as walletBindingId on writes.
walletBindings[].labelstringDisplay label for the binding.
walletBindings[].providerstringWallet provider recorded for the binding.
walletBindings[].ordinalsAddressstringOrdinals address in the binding.
walletBindings[].paymentAddressstringPayment address in the binding.
walletBindings[].isPublicbooleanWhether this binding is public on the profile.
sessionTokenstringThe bearer token.
expiresAtISO 8601 datetimeWhen the token stops being accepted. Tokens are currently valid for 1 hour.
{
"profile": {
"id": "44444444-4444-4444-4444-444444444444",
"username": "alice",
"displayName": "Alice",
"bio": null,
"avatarUrl": null,
"bannerUrl": null
},
"walletBindings": [
{
"walletBindingId": "55555555-5555-5555-5555-555555555555",
"label": "Trading Wallet",
"provider": "unknown",
"ordinalsAddress": "bc1p...",
"paymentAddress": "bc1q...",
"isPublic": true
}
],
"sessionToken": "<bearer-token>",
"expiresAt": "2026-05-08T19:00:00.000Z"
}
  • 200: verification succeeded.
  • 400: missing or malformed fields.
  • 401: signature is invalid.
  • 403: verified payment address does not meet the funding requirement, or the wallet is otherwise not allowed.
  • 404: challenge was not found.
  • 409: challenge flow is invalid or wallet ownership changed before verification completed.
  • 410: challenge or flow expired. Start a fresh /auth/challenge.
  • 429: rate limited.
  • 503: funding check temporarily unavailable. Wait and retry with a fresh auth flow.

Send the token on every request:

Authorization: Bearer <sessionToken>
Content-Type: application/json

Choose one entry from walletBindings and send its walletBindingId on writes.

The token has these properties:

  • It is an API session token. Only /api/v1 bearer auth accepts it.
  • Web session cookies and social-login sessions are not accepted on this surface.
  • The token expires at expiresAt, currently 1 hour after issuance. Re-run the challenge and verify flow to refresh.
  • v1 tokens are not independently revocable or scoped per operation.

Returns the profile and wallet bindings without starting a new auth flow.

FieldTypeDescription
profileobjectYour profile metadata.
walletBindingsarraySame shape as /auth/verify.
{
"profile": {
"id": "44444444-4444-4444-4444-444444444444",
"username": "alice",
"displayName": "Alice",
"bio": null,
"avatarUrl": null,
"bannerUrl": null
},
"walletBindings": [
{
"walletBindingId": "55555555-5555-5555-5555-555555555555",
"label": "Trading Wallet",
"provider": "unknown",
"ordinalsAddress": "bc1p...",
"paymentAddress": "bc1q...",
"isPublic": true
}
]
}

To issue a sessionToken, /auth/verify checks the verified paymentAddress from the auth flow for at least 0.01 BTC confirmed. Pick the wallet address that should satisfy this requirement when you call /auth/challenge.

StatusWhen
403The verified payment address does not meet the funded threshold, or the wallet is otherwise not allowed.
503The funding check is temporarily unavailable. Wait and retry with a fresh auth flow; do not work around the check.

The 503 body looks like this:

{
"error": "Trading API wallet eligibility is temporarily unavailable"
}

Missing token:

{ "error": "Bearer session token required" }

Invalid or expired token:

{ "error": "Invalid or expired bearer session token" }