access token and a long-lived refresh token you can use to authenticate subsequent requests.
All three authentication endpoints are unauthenticated — do not include an
Authorization header when calling them.Authentication flow
Request a challenge
Call
POST /v1/auth/challenge with your wallet address and chain kind. The server returns a signed challenge (a SIWE message for EVM or a base64-encoded transaction for Stellar) that you must sign with your wallet’s private key.Sign the challenge
Sign the challenge message locally using your wallet. For EVM, use
eth_signMessage or equivalent. For Stellar, co-sign the returned XDR transaction.Submit the signed message
Call
POST /v1/auth/login with your address, chain kind, and the signature (EVM) or co-signed transaction (Stellar). The server verifies the signature and returns access and refresh tokens.Use the access token
Include
Authorization: Bearer <access> in the headers of every authenticated request.POST /v1/auth/challenge
Request a nonce challenge for your wallet address. The returned value is the message (EVM) or transaction (Stellar) that you must sign.Request body
Your wallet address. Use
0x-prefixed 20-byte hex for EVM (e.g., 0x742d35Cc6634C0532925a3b844Bc454e4438f44e), or a G-strkey for Stellar (e.g., GAHJJJKMOKYE4RVPZEWZTKH5FVI4PA3VL7GK2LFNUBSGBV).The chain family the wallet belongs to. Must be
"EVM" or "STELLAR".Response fields
Echoed chain kind:
"EVM" or "STELLAR".Echoed wallet address in canonical form.
ISO 8601 timestamp after which the challenge is no longer valid.
Unique nonce embedded in the SIWE message. Returned for EVM challenges only.
SIWE domain. Returned for EVM challenges only.
SIWE URI. Returned for EVM challenges only.
Server-signed SEP-10 challenge transaction in base64 XDR format. Returned for Stellar challenges only.
Stellar network passphrase. Returned for Stellar challenges only.
POST /v1/auth/login
Submit your signed challenge to receive JWT tokens. The request body differs slightly depending onchainKind.
Request body
Must match the
chainKind used when requesting the challenge: "EVM" or "STELLAR".The SIWE message string returned from the challenge step. Required when
chainKind is "EVM".Your wallet’s signature over the SIWE message. Required when
chainKind is "EVM".The co-signed SEP-10 challenge transaction in base64 XDR format. Required when
chainKind is "STELLAR".Response fields
POST /v1/auth/refresh
Exchange a valid refresh token for a new access token. Use this when your access token expires to avoid forcing the user to re-sign.Request body
The refresh token returned from the login response.
Response fields
POST /v1/auth/link
Attach a second wallet (on the other chain kind) to the currently authenticated session. Used for the dual-chain sign-in flow described in Connect your wallet. The call takes the same signed-challenge payload as/v1/auth/login
but runs against the existing user instead of creating or looking up
one by address. On success, the wallet is recorded on the caller’s
User record and no new session is issued — the caller’s current
access token keeps working.
Bearer <accessToken> — the session token from the wallet that is
already signed in.Request body
Identical toPOST /v1/auth/login:
- EVM:
{ "message": "...", "signature": "0x...", "chainKind": "EVM" } - Stellar:
{ "transaction": "<signed-XDR>", "chainKind": "STELLAR" }
Behaviour
- The
chainKindmust be the one not currently linked to the account. Re-linking the same chain kind returns409 Conflict. - Linking an address that already belongs to a different account
returns
409 Conflict— disconnect it from the other account first. - On success, returns the updated user record. No new tokens are issued; the existing access / refresh tokens continue to work and now represent both wallets.
JavaScript example: full authentication flow
The following example shows the complete EVM authentication flow usingethers.js and the Fetch API.