Account
Account

Link wallet to current session

Bind the caller's executor profile to their current Supabase session so worker-auth endpoints (apply, submit, withdraw) can resolve it.

This is the bootstrap step that replaces the revoked link_wallet_to_session RPC (migration 092) and the anon-revoked get_or_create_executor (migration 111). The wallet MUST sign:

Execution Market: link wallet <wallet> to Supabase user <sub> at <ISO8601 UTC>

The <sub> is the caller's JWT subject — binding it into the signed message stops a captured signature from being replayed under a different JWT to hijack the executor. The signature proves ownership of the wallet, which authorizes binding executors.user_id to the JWT sub (the 'proven owner' rule from migration 111). Timestamp must be within the last 10 minutes. This endpoint does NOT use worker-auth (that's circular — the link is what makes worker-auth resolvable); it validates the raw Supabase JWT plus the wallet signature directly.

Smart-wallet signatures (ERC-1271 / ERC-6492): signatures that are not 65 bytes are verified on-chain via isValidSignature against the wallet contract on the platform's default network. ERC-6492 envelopes are unwrapped for DEPLOYED wallets; undeployed (counterfactual) 6492 wallets are rejected with a distinct error (erc6492_wallet_undeployed) until the wallet is deployed.

Alternative proof (Dynamic embedded wallets only): pass dynamic_jwt (the Dynamic session JWT) instead of message+signature. The server verifies it against Dynamic's JWKS (RS256, exp, scope includes user:basic, environment_id match) and requires the wallet in verified_credentials[] with wallet_provider == 'embeddedWallet'. External wallets must keep using the signature flow.

Alternative proof (web sign-in through Execution Market's authorization server, e.g. Continue with PayBox): pass oauth_access_token with oauth_code_verifier and nothing else — an access token issued by https://auth.execution.market to the web app's own client (https://execution.market/oauth/client-metadata.json) after PayBox signed the consent message for the browser that started the sign-in, and the PKCE verifier of that sign-in. Accepted for the wallet its sub names, within 10 minutes of issue, once, and only with the verifier behind the PKCE challenge the token carries. That client never gets a one-click consent, a refresh token or a code for a pasted or delivered signature, so each of its tokens follows a wallet signature made for that browser. A token issued to any other client (claude.ai, ChatGPT) is refused.

post/api/v1/account/link-wallet

Request

  • Base URL: https://api.execution.market
  • URL: https://api.execution.market/api/v1/account/link-wallet
  • Auth: API key in header Signature-Input

Headers

authorizationstring nullable

Bearer <supabase_jwt>

Request body

wallet_addressstring required

The wallet that signed the message (0x-prefixed, 42 chars)

messagestring nullable

Exact human-readable message that was signed. Format: 'Execution Market: link wallet <wallet> to session at <ISO8601 UTC>'. Required unless dynamic_jwt is provided.

signaturestring nullable

Signature of the message by wallet_address. 65-byte EIP-191 EOA signatures are verified via ecrecover; any other length is treated as a smart-wallet signature and verified on-chain via ERC-1271 isValidSignature (ERC-6492 envelopes accepted for DEPLOYED wallets). Required unless dynamic_jwt is provided.

dynamic_jwtstring nullable

Dynamic session JWT (alternative ownership proof for Dynamic EMBEDDED wallets only). When present, message/signature are not required.

oauth_access_tokenstring nullable

An access token from Execution Market's own authorization server (https://auth.execution.market), issued to the web dashboard's client after the wallet signed the consent message — the web sign-in with PayBox. Accepted only from that client, within 10 minutes of issue, once, with oauth_code_verifier. Not with dynamic_jwt nor message/signature.

oauth_code_verifierstring nullable

With oauth_access_token, and only with it: the PKCE code_verifier (RFC 7636) of the authorization request that token came from. The token carries that request's S256 challenge, so only the tab that started the sign-in can bind a session with it.

Response

Successful Response

messagestring required

Human-readable result message

executor_idstring required

UUID of the executor bound to the session

wallet_addressstring required

Linked wallet address (lowercase)

linkedboolean required

False when the wallet was already linked to this session (no-op)

Changes

No recorded changes to this operation across all 1 revision of this API.