/v1 call (except minting a key) needs an API key:
X-Api-Key: hk_test_... works too. Keys issued by a testnet deployment start with hk_test_, mainnet keys with hk_live_.
Scopes
Only the endpoints above check a scope. Every other
/v1 endpoint — reads, wallets, threads, swaps, settlement builders, broadcast, webhooks — works with any valid key; threads and swaps are readable only by their two parties. A call that needs a scope the key lacks returns 403. GET /v1/me shows the key’s scopes.
Key rules
- Expiry: a key works for 90 days (
expiresAtin the mint response). To renew, mint a new one. - Shown once: the plaintext key is returned once. The server stores only its hash and a display prefix.
- Owner is told: every new key is announced to the account owner in Telegram, when Telegram is linked.
- Wallet-only accounts hold 1 key. An account with only a wallet (an agent that minted by signature, or a wallet sign-in) holds one live key. A new signed mint replaces the previous key, which stops working. This is also how an agent recovers a lost or leaked key.
- Person-backed accounts hold up to 10. An account signed up at /developers with email, Google or Telegram holds up to 10 live keys. Minting an eleventh is refused until one is revoked.
- One rate budget per account: all keys of an account share it. More keys do not buy more rate. See Rate limits.
Mint a key in the browser
Sign in at hashlock.markets/developers and create a key. Revoke keys there too.Mint a key with a wallet signature
No browser, no existing key. This is the path for agents.GET /v1/keys/nonce→{ "nonce": "…" }. The nonce is single-use and lives 5 minutes.- Sign a message with the wallet (rules below).
POST /v1/keyswith{ rail, address, message, signature }, whererailisevm,tron,solanaorbitcoin.
The message
The name and scopes are read from the signed text only. What the wallet signed is exactly what is minted.
Signing per rail is the same as for proving wallets: EVM
personal_sign (EIP-191), TRON signMessageV2, Solana ed25519 signMessage (base58 signature), Bitcoin BIP-322.
replaced: true means a previous key of this account was revoked by this mint.
Minting is limited per client IP: 20 mint requests per hour.
Proving wallets
An order that gives an asset needs a proven wallet on that asset’s chain. A key starts with only the address its account signed in with.GET /v1/me lists, per chain, the login address and the proven ones.
-
GET /v1/wallets/nonce→ a single-use link nonce. -
Sign a message that contains
Hashlock Markets, the wordslink this wallet, the address, andNonce: <nonce>:For Bitcoin theAddress:line may be left out; the BIP-322 signature is verified against the address. -
POST /v1/wallets/{evm|tron|solana|bitcoin}with{ address, message, signature }.
Responses:
200 proven (repeating a proof you hold is also 200); 400 bad signature, missing link this wallet, or a bad nonce; 409 the wallet belongs to another account.
The link nonce is not a login nonce, and a key-mint nonce is not either. Each purpose has its own nonce pool, so a signature collected for one cannot be replayed for another. A link signature is not a session for that wallet’s account.
DELETE /v1/wallets/{address} withdraws a proof. Login addresses are not proofs and cannot be removed this way. Putting a proof back needs a new signature from that wallet.
Payout addresses
A proof made with an API key widens what the account can trade. It does not count as a place to receive money. Over the API,POST /v1/swaps/{id}/address accepts an address that receives money — the payout address, and on Bitcoin and Solana the refund address too — only if it is:
- a wallet the account signed in with, or
- a wallet proven from a signed-in wallet session on the web. For Bitcoin, the pubkey of such an address.