POST /v1/auth/wallet/link

Attach a wallet to the account you are signed in to, proving ownership with a signed link challenge. One wallet per chain family.

POSThttps://triport.io/v1/auth/wallet/link
Network
— (EVM and Solana wallets)
Auth
Console session cookie + CSRF header (X-CSRF-Token = nl_csrf cookie)

Links a wallet to the current account without signing anyone in. It is how an account created with email or Google gets a wallet it can pay from, and how a wallet-login account adds a wallet of another family.

Flow:

  1. POST /v1/auth/wallet/challenge with purpose: "link" for the wallet's address and chain.
  2. The wallet signs the returned message (EVM: personal_sign; Solana: signMessage).
  3. Call this endpoint with the nonce, address, chain and signature.

Policy:

  • One wallet per family. If the account already has a different wallet of this family, the call returns 409 family_already_linked with the existing address. There is no self-service replace (removed on 2026-09-29): a linked wallet is changed only by support.
  • One account per wallet. A wallet already linked to (or used to sign in to) another account returns 409 address_taken; accounts are never merged.
  • Linking the same address again is idempotent (already: true).
  • One family per call. Each call links exactly one wallet of one family with one signature; a refusal leaves every other binding untouched.
  • No Tron. chain: "tron" returns 400 chain_not_supported before the nonce is looked up (the nonce is not consumed).

The nonce is consumed only after the signature and purpose check pass.

Parameters

Request body (JSON), all required:

noncestring (UUID)
Nonce from a purpose: "link" challenge.
addressstring
Wallet address (same forms as the challenge).
chainstring
evm or solana.
signaturestring
Signature over the challenge message (encodings as in wallet/verify).

Response

200 OK

FieldTypeDescription
userobjectThe signed-in user (same shape as in wallet/verify).
providerstringwallet_evm or wallet_solana.
familystringevm or solana.
addressstringCanonical address that is now linked.
alreadybooleanPresent and true when this address was already linked to this account.

Errors

CodeHTTPWhen it happens
unauthenticated401No valid session.
csrf_missing / csrf_invalid403X-CSRF-Token header absent or not equal to the nl_csrf cookie.
invalid_json400Body is not valid JSON.
chain_not_supported400chain is tron (Tron linking removed on 2026-09-29). The nonce is not consumed.
invalid_nonce400Unknown, used or expired nonce.
invalid_address400Address malformed for chain, or unknown chain.
address_mismatch400address/chain differ from the challenge.
purpose_mismatch400The nonce was not issued with purpose: "link" (not consumed).
nonce_user_mismatch400The link challenge was requested in another signed-in account's session; a link nonce is bound to the session that requested it. Checked before the signature; the nonce is not consumed.
invalid_signature400Signature does not verify.
address_taken409The wallet belongs to another account.
family_already_linked409Body { "error", "family", "address", "hint": "support" } — the account already has another wallet of this family; only support can change it. (hint was "replace" before 2026-09-29.)
internal500Server-side failure.

Notes

  • Linking does not create a session and never switches accounts.
  • The linked wallet is what in-app payments check: a build with any other payer address of that family returns 403 payer_not_linked (Build in-app transaction).