- CLOB: Place prediction market orders on the central limit order book.
- Combos: Trade Combos through RFQs.
- All: A shortcut that grants access to CLOB, Combos, and any future supported venues.
Authorize a Session Key
The Deposit Wallet Owner authorizes a session signer address with an expiration and scoped permissions.- TypeScript
- Python
- API
Generate a Session Key
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
const sessionKeyPrivateKey = generatePrivateKey();
const { address: sessionKeyAddress } =
privateKeyToAccount(sessionKeyPrivateKey);
Create the Deposit Wallet Owner Client
SecureClient with the Deposit Wallet Owner’s private key
and Builder API credentials.import { createSecureClient } from "@polymarket/client";
import { builderApiKey } from "@polymarket/client/node";
import { privateKey } from "@polymarket/client/viem";
const secureClient = await createSecureClient({
signer: privateKey(process.env.POLYMARKET_PRIVATE_KEY),
wallet: process.env.POLYMARKET_DEPOSIT_WALLET!,
apiKey: builderApiKey({
key: process.env.POLYMARKET_BUILDER_API_KEY!,
secret: process.env.POLYMARKET_BUILDER_SECRET!,
passphrase: process.env.POLYMARKET_BUILDER_PASSPHRASE!,
}),
});
Authorize the Session Key
authorizeSessionKey() to authorize your session key with
the desired expiration.const authorization = await secureClient.authorizeSessionKey({
address: sessionKeyAddress,
validUntil: Math.floor(Date.now() / 1_000) + 2 * 60 * 60,
});
// authorization: AuthorizeSessionKeyResult
scopes
parameter:import { SessionKeyKnownScope } from "@polymarket/client";
const scopedAuthorization = await secureClient.authorizeSessionKey({
address: sessionKeyAddress,
scopes: [SessionKeyKnownScope.CLOB, SessionKeyKnownScope.COMBOSRFQ],
validUntil: Math.floor(Date.now() / 1_000) + 2 * 60 * 60,
});
// scopedAuthorization: AuthorizeSessionKeyResult
Generate a Session Key
from eth_account import Account
session_key = Account.create()
session_key_private_key = "0x" + session_key.key.hex().removeprefix("0x")
session_key_address = session_key.address
Create the Deposit Wallet Owner Client
AsyncSecureClient with the Deposit Wallet Owner’s
private key and Builder API credentials.import os
from polymarket import AsyncSecureClient, BuilderApiKey
secure_client = await AsyncSecureClient.create(
private_key=os.environ["POLYMARKET_PRIVATE_KEY"],
wallet=os.environ["POLYMARKET_DEPOSIT_WALLET"],
api_key=BuilderApiKey(
key=os.environ["POLYMARKET_BUILDER_API_KEY"],
secret=os.environ["POLYMARKET_BUILDER_SECRET"],
passphrase=os.environ["POLYMARKET_BUILDER_PASSPHRASE"],
),
)
Authorize the Session Key
authorize_session_key() to authorize your session key
with the desired expiration.from datetime import UTC, datetime, timedelta
authorization = await secure_client.authorize_session_key(
address=session_key_address,
valid_until=datetime.now(UTC) + timedelta(hours=2),
)
# authorization: AuthorizeSessionKeyResult
scopes
parameter:from polymarket import SessionKeyKnownScope
scoped_authorization = await secure_client.authorize_session_key(
address=session_key_address,
scopes=(
SessionKeyKnownScope.CLOB,
SessionKeyKnownScope.COMBOSRFQ,
),
valid_until=datetime.now(UTC) + timedelta(hours=2),
)
# scoped_authorization: AuthorizeSessionKeyResult
Generate a Session Key
$ cast wallet new
Successfully created new keypair.
Address: <session_signer_address>
Private key: <session_signer_private_key>
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
const sessionSignerPrivateKey = generatePrivateKey();
const { address: sessionSignerAddress } = privateKeyToAccount(
sessionSignerPrivateKey,
);
<session_signer_private_key> in a secrets manager or another
secure key store.Fetch the Wallet Nonce
WALLET nonce by passing the Deposit
Wallet Owner address.curl -G "https://relayer-v2.polymarket.com/v1/account/transactions/params" \
--data-urlencode "address=<deposit_wallet_owner_address>" \
--data-urlencode "type=WALLET"
{
"address": "<relayer_address>",
"nonce": "<wallet_nonce>"
}
Define the Session Key Authorization
authorizeSessionSigner call that the Deposit Wallet
will execute:function authorizeSessionSigner(address sessionSigner, uint256 validUntil)
validUntil to a future whole Unix timestamp in seconds, no more
than 180 days from now. The following example uses Viem:import { encodeFunctionData, parseAbi } from "viem"
const sessionSignerAddress = "<session_signer_address>" as `0x${string}`
const sessionExpiryUnixSeconds = BigInt("<session_expiry_unix_seconds>")
const calldata = encodeFunctionData({
abi: parseAbi([
"function authorizeSessionSigner(address sessionSigner, uint256 validUntil)",
]),
functionName: "authorizeSessionSigner",
args: [sessionSignerAddress, sessionExpiryUnixSeconds],
})
Prepare the Owner Authorization
{
"domain": {
"name": "DepositWallet",
"version": "1",
"chainId": 137,
"verifyingContract": "<deposit_wallet_address>"
},
"types": {
"Call": [
{ "name": "target", "type": "address" },
{ "name": "value", "type": "uint256" },
{ "name": "data", "type": "bytes" }
],
"Batch": [
{ "name": "wallet", "type": "address" },
{ "name": "nonce", "type": "uint256" },
{ "name": "deadline", "type": "uint256" },
{ "name": "calls", "type": "Call[]" }
]
},
"primaryType": "Batch",
"message": {
"wallet": "<deposit_wallet_address>",
"nonce": "<wallet_nonce>",
"deadline": "<batch_deadline_unix_seconds>",
"calls": [
{
"target": "<deposit_wallet_address>",
"value": "0",
"data": "<calldata>"
}
]
}
}
<deposit_wallet_address>is the Deposit Wallet address.<wallet_nonce>is the nonce returned when fetching the Deposit Wallet nonce.<batch_deadline_unix_seconds>is a future whole Unix timestamp in seconds after which the signed authorization request can no longer be executed. The Relayer requires at least 10 seconds of remaining validity.<calldata>is the encoded authorization call from the previous step.
Sign as the Deposit Wallet Owner
import { privateKeyToAccount } from "viem/accounts"
const depositWalletOwner = privateKeyToAccount(
process.env.DEPOSIT_WALLET_OWNER_PRIVATE_KEY as `0x${string}`,
)
const signature = await depositWalletOwner.signTypedData(walletBatchTypedData)
Build the Authorization Body
{
"walletAddress": "<deposit_wallet_address>",
"sessionSignerAddress": "<session_signer_address>",
"scopes": ["CLOB", "COMBOSRFQ"],
"validUntil": "<session_expiry_unix_seconds>",
"nonce": "<wallet_nonce>",
"deadline": "<batch_deadline_unix_seconds>",
"signature": "<signature>"
}
<deposit_wallet_address>is the Deposit Wallet address.<session_signer_address>and<session_expiry_unix_seconds>are the signer and expiration encoded in<calldata>.scopescontains one or more venue literals ("CLOB"or"COMBOSRFQ"), or["ALL"]by itself.<wallet_nonce>and<batch_deadline_unix_seconds>match the values in the signed typed data.<signature>is the value returned in the previous step.
Create the Builder Signature
authorization_body = <exact_serialized_authorization_body>
request_timestamp = <unix_seconds>
method = "POST"
request_path = "/v1/session-signers/authorizations"
message = request_timestamp + method + request_path + authorization_body
builder_signature = urlsafeBase64WithPadding(
HMAC-SHA256(base64Decode(<builder_api_secret>), message)
)
Submit the Authorization
curl -X POST "https://relayer-v2.polymarket.com/v1/session-signers/authorizations" \
-H "Content-Type: application/json" \
-H "POLY_BUILDER_API_KEY: <builder_api_key>" \
-H "POLY_BUILDER_TIMESTAMP: <request_timestamp>" \
-H "POLY_BUILDER_PASSPHRASE: <builder_api_passphrase>" \
-H "POLY_BUILDER_SIGNATURE: <builder_signature>" \
-H "Idempotency-Key: <idempotency_key>" \
-d '{
"walletAddress": "<deposit_wallet_address>",
"sessionSignerAddress": "<session_signer_address>",
"scopes": ["CLOB", "COMBOSRFQ"],
"validUntil": "<session_expiry_unix_seconds>",
"nonce": "<wallet_nonce>",
"deadline": "<batch_deadline_unix_seconds>",
"signature": "<signature>"
}'
<idempotency_key>uniquely identifies this authorization request. Reuse it when retrying the same request.<request_timestamp>is the Unix timestamp in seconds used to create the Builder signature.<builder_api_key>is your Builder API key.<builder_api_passphrase>is the passphrase for your Builder API key.<builder_signature>is the signature created in the previous step.
{
"operationId": "<operation_id>",
"status": "SUBMITTED",
"transactionHash": "<transaction_hash>",
"transactionId": "<transaction_id>"
}
Confirm the Relayer Transaction
<transaction_id>:curl "https://relayer-v2.polymarket.com/v1/account/transactions/<transaction_id>"
state is STATE_CONFIRMED. Treat STATE_FAILED and
STATE_INVALID as terminal failures.{
"transaction_id": "<transaction_id>",
"transaction_hash": "<transaction_hash>",
"state": "STATE_CONFIRMED",
"error_msg": null
}
Poll for the Session Key
curl "https://clob.polymarket.com/v1/user/session-signers" \
-H "POLY_ADDRESS: <signer_address>" \
-H "POLY_API_KEY: <clob_api_key>" \
-H "POLY_PASSPHRASE: <clob_api_passphrase>" \
-H "POLY_SIGNATURE: <clob_l2_signature>" \
-H "POLY_TIMESTAMP: <clob_request_timestamp>"
<clob_l2_signature> from the
exact request path:clob_request_timestamp = <unix_seconds>
method = "GET"
request_path = "/v1/user/session-signers"
message = clob_request_timestamp + method + request_path
clob_l2_signature = urlsafeBase64WithPadding(
HMAC-SHA256(base64Decode(<clob_api_secret>), message)
)
signers contains the requested session signer with the
expected scopes and expiration. The authorization is ready for use
only after the session signer appears in the response.{
"wallet": "<deposit_wallet_address>",
"signers": [
{
"address": "<session_signer_address>",
"scopes": ["CLOB", "COMBOSRFQ"],
"valid_until": 1800000000 // <session_expiry_unix_seconds>
}
]
}
Place an Order
This section shows how to place an order using a session key. See Place Orders for the complete order workflow.- TypeScript
- Python
- API
Create the Session Client
SecureClient for the Deposit Wallet with the session
signer.import { createSecureClient } from "@polymarket/client";
import { privateKey } from "@polymarket/client/viem";
const sessionPrivateKey = process.env.POLYMARKET_SESSION_PRIVATE_KEY!;
const depositWallet = process.env.POLYMARKET_DEPOSIT_WALLET!;
const sessionClient = await createSecureClient({
signer: privateKey(sessionPrivateKey),
wallet: depositWallet,
});
Place an Order
sessionClient to submit a limit order.import { OrderSide } from "@polymarket/client";
const response = await sessionClient.placeLimitOrder({
tokenId: yesTokenId,
side: OrderSide.BUY,
price: "0.52",
size: "10",
});
// response: OrderResponse
10 shares at a price of 0.52 USD per share. See
Place Orders for market
constraints, order types, and response handling.Create the Session Client
AsyncSecureClient for the Deposit Wallet with the
session key.from polymarket import AsyncSecureClient
session_client = await AsyncSecureClient.create(
private_key=os.environ["POLYMARKET_SESSION_PRIVATE_KEY"],
wallet=os.environ["POLYMARKET_DEPOSIT_WALLET"],
)
Place an Order
session_client to submit a limit order.response = await session_client.place_limit_order(
token_id=yes_token_id,
side="BUY",
price="0.52",
size="10",
)
# response: OrderResponse
10 shares at a price of 0.52 USD per share. See
Place Orders for market
constraints, order types, and response handling.Create a CLOB L1 Signature
<clob_l1_signature> with the session key to prove
control of the session signer.{
"domain": {
"name": "ClobAuthDomain",
"version": "1",
"chainId": 137
},
"types": {
"ClobAuth": [
{ "name": "address", "type": "address" },
{ "name": "timestamp", "type": "string" },
{ "name": "nonce", "type": "uint256" },
{ "name": "message", "type": "string" }
]
},
"primaryType": "ClobAuth",
"message": {
"address": "<session_signer_address>",
"timestamp": "<unix_seconds>",
"nonce": "<nonce>",
"message": "This message attests that I control the given wallet"
}
}
Create or Derive CLOB Credentials
curl -X POST "https://clob.polymarket.com/auth/api-key" \
-H "POLY_ADDRESS: <session_signer_address>" \
-H "POLY_SIGNATURE: <clob_l1_signature>" \
-H "POLY_TIMESTAMP: <unix_seconds>" \
-H "POLY_NONCE: <nonce>"
curl "https://clob.polymarket.com/auth/derive-api-key" \
-H "POLY_ADDRESS: <session_signer_address>" \
-H "POLY_SIGNATURE: <clob_l1_signature>" \
-H "POLY_TIMESTAMP: <unix_seconds>" \
-H "POLY_NONCE: <nonce>"
<nonce> identifies the credential set. Use 0 unless you manage
multiple credential sets.The response contains the credentials used to authenticate the order
request:{
"apiKey": "<clob_api_key>",
"secret": "<clob_api_secret>",
"passphrase": "<clob_api_passphrase>"
}
Create the Order Typed Data
| Field | Value |
|---|---|
maker | <deposit_wallet_address> |
signer | <deposit_wallet_address> |
signatureType | 3 |
TypedDataSign Example
TypedDataSign Example
{
"domain": {
"name": "Polymarket CTF Exchange",
"version": "2",
"chainId": 137,
"verifyingContract": "<exchange_address>"
},
"types": {
"Order": [
{ "name": "salt", "type": "uint256" },
{ "name": "maker", "type": "address" },
{ "name": "signer", "type": "address" },
{ "name": "tokenId", "type": "uint256" },
{ "name": "makerAmount", "type": "uint256" },
{ "name": "takerAmount", "type": "uint256" },
{ "name": "side", "type": "uint8" },
{ "name": "signatureType", "type": "uint8" },
{ "name": "timestamp", "type": "uint256" },
{ "name": "metadata", "type": "bytes32" },
{ "name": "builder", "type": "bytes32" }
],
"TypedDataSign": [
{ "name": "contents", "type": "Order" },
{ "name": "name", "type": "string" },
{ "name": "version", "type": "string" },
{ "name": "chainId", "type": "uint256" },
{ "name": "verifyingContract", "type": "address" },
{ "name": "salt", "type": "bytes32" }
]
},
"primaryType": "TypedDataSign",
"message": {
"contents": {
"salt": "479249096354",
"maker": "<deposit_wallet_address>",
"signer": "<deposit_wallet_address>",
"tokenId": "<yes_token_id>",
"makerAmount": "5200000",
"takerAmount": "10000000",
"side": 0,
"signatureType": 3,
"timestamp": "<unix_milliseconds>",
"metadata": "0x0000000000000000000000000000000000000000000000000000000000000000",
"builder": "0x0000000000000000000000000000000000000000000000000000000000000000"
},
"name": "DepositWallet",
"version": "1",
"chainId": 137,
"verifyingContract": "<deposit_wallet_address>",
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
}
Sign the Order
inner_signature = signTypedData(typed_data)
deposit_wallet_order_signature = wrapDepositWalletSignature(
typed_data,
inner_signature
)
wrapDepositWalletSignature() helper from the linked example.Add the Session Signer Envelope
<deposit_wallet_order_signature> with the session signer
address:signer_id = leftPadToBytes32(<session_signer_address>)
session_payload = abiEncode(
["bytes32", "bytes32", "bytes"],
[signer_id, bytes32(0), <deposit_wallet_order_signature>]
)
session_wrapped_order_signature =
session_payload +
0x6492649264926492649264926492649264926492649264926492649264926492
Create the Order Request
<session_wrapped_order_signature>:{
"deferExec": false,
"order": {
"builder": "0x0000000000000000000000000000000000000000000000000000000000000000",
"expiration": "0",
"maker": "<deposit_wallet_address>",
"makerAmount": "5200000",
"metadata": "0x0000000000000000000000000000000000000000000000000000000000000000",
"salt": 479249096354,
"side": "BUY",
"signature": "<session_wrapped_order_signature>",
"signatureType": 3,
"signer": "<deposit_wallet_address>",
"takerAmount": "10000000",
"timestamp": "<unix_milliseconds>",
"tokenId": "<yes_token_id>"
},
"orderType": "GTC",
"owner": "<clob_api_key>"
}
Submit the Order
curl -X POST "https://clob.polymarket.com/order" \
-H "Content-Type: application/json" \
-H "POLY_ADDRESS: <session_signer_address>" \
-H "POLY_API_KEY: <clob_api_key>" \
-H "POLY_PASSPHRASE: <clob_api_passphrase>" \
-H "POLY_SIGNATURE: <clob_l2_signature>" \
-H "POLY_TIMESTAMP: <clob_request_timestamp>" \
--data '<request_body>'
<clob_l2_signature> authenticates the request. Generate it from the
exact serialized request body and a fresh <clob_request_timestamp>:message = <clob_request_timestamp> + "POST" + "/order" + <request_body>
clob_l2_signature = urlsafeBase64WithPadding(
HMAC-SHA256(base64Decode(<clob_api_secret>), message)
)
Fetch Session Keys
List the active session keys for a Deposit Wallet to see which signers can currently act on its behalf.- TypeScript
- Python
- API
fetchSessionKeys() on the Deposit Wallet Owner’s SecureClient:const sessionKeys = await secureClient.fetchSessionKeys();
// sessionKeys: SessionKey[]
Output: SessionKey[]
Output: SessionKey[]
type SessionKey = {
address: EvmAddress;
scopes: SessionKeyScope[];
validUntil: number;
};
[
{
"address": "<session_signer_address>",
"scopes": ["CLOB", "COMBOSRFQ"],
"validUntil": 1800000000
}
]
fetch_session_keys() on the Deposit Wallet Owner’s
AsyncSecureClient. The synchronous SecureClient provides the same
method without await.session_keys = await secure_client.fetch_session_keys()
# session_keys: tuple[AuthorizedSessionKey, ...]
Output: tuple[AuthorizedSessionKey, ...]
Output: tuple[AuthorizedSessionKey, ...]
class AuthorizedSessionKey:
address: EvmAddress
scopes: tuple[SessionKeyScope, ...]
valid_until: datetime
[
{
"address": "<session_signer_address>",
"scopes": ["CLOB", "COMBOSRFQ"],
"valid_until": "2027-01-15T08:00:00Z"
}
]
Create a CLOB L1 Signature
<clob_l1_signature> with the Deposit Wallet Owner to
prove control of <deposit_wallet_owner_address>.{
"domain": {
"name": "ClobAuthDomain",
"version": "1",
"chainId": 137
},
"types": {
"ClobAuth": [
{ "name": "address", "type": "address" },
{ "name": "timestamp", "type": "string" },
{ "name": "nonce", "type": "uint256" },
{ "name": "message", "type": "string" }
]
},
"primaryType": "ClobAuth",
"message": {
"address": "<deposit_wallet_owner_address>",
"timestamp": "<unix_seconds>",
"nonce": 0,
"message": "This message attests that I control the given wallet"
}
}
Create or Derive CLOB Credentials
curl -X POST "https://clob.polymarket.com/auth/api-key" \
-H "POLY_ADDRESS: <deposit_wallet_owner_address>" \
-H "POLY_SIGNATURE: <clob_l1_signature>" \
-H "POLY_TIMESTAMP: <unix_seconds>" \
-H "POLY_NONCE: 0"
curl "https://clob.polymarket.com/auth/derive-api-key" \
-H "POLY_ADDRESS: <deposit_wallet_owner_address>" \
-H "POLY_SIGNATURE: <clob_l1_signature>" \
-H "POLY_TIMESTAMP: <unix_seconds>" \
-H "POLY_NONCE: 0"
{
"apiKey": "<clob_api_key>",
"secret": "<clob_api_secret>",
"passphrase": "<clob_api_passphrase>"
}
Fetch the Session Keys
curl "https://clob.polymarket.com/v1/user/session-signers" \
-H "POLY_ADDRESS: <deposit_wallet_owner_address>" \
-H "POLY_API_KEY: <clob_api_key>" \
-H "POLY_PASSPHRASE: <clob_api_passphrase>" \
-H "POLY_SIGNATURE: <clob_l2_signature>" \
-H "POLY_TIMESTAMP: <clob_request_timestamp>"
<clob_l2_signature> authenticates the request. Generate it from the
request path and a fresh <clob_request_timestamp>:message = <clob_request_timestamp> + "GET" + "/v1/user/session-signers"
clob_l2_signature = urlsafeBase64WithPadding(
HMAC-SHA256(base64Decode(<clob_api_secret>), message)
)
{
"wallet": "<deposit_wallet_address>",
"signers": [
{
"address": "<session_signer_address>",
"scopes": ["CLOB", "COMBOSRFQ"],
"valid_until": 1800000000 // <session_expiry_unix_seconds>
}
]
}
signers contains only usable, unexpired, non-revoked authorizations.
An empty array means the request succeeded but there are no active
session keys.Revoke a Session Key
Revoke a session key when an integration no longer needs access or its private key may have been exposed. Revocation prevents further trading by that key and cancels its open orders without affecting orders placed by other session keys. Revocation completes after the session key’s open orders are canceled and the on-chain transaction is confirmed, which may take several minutes.- TypeScript
- Python
- API
revokeSessionKey() on the Deposit Wallet Owner’s SecureClient with
the session key’s public address:const revocation = await secureClient.revokeSessionKey({
address: sessionKeyAddress,
});
// revocation: RevokeSessionKeyResult
transaction.revoke_session_key() on the Deposit Wallet Owner’s
AsyncSecureClient with the session key’s public address. The synchronous
SecureClient provides the same method without await.transaction = await secure_client.revoke_session_key(
address=session_key_address,
)
# transaction: TransactionOutcome
transaction after order cleanup and
on-chain confirmation.Fetch the Wallet Nonce
WALLET nonce by passing the Deposit
Wallet Owner address.curl -G "https://relayer-v2.polymarket.com/v1/account/transactions/params" \
--data-urlencode "address=<deposit_wallet_owner_address>" \
--data-urlencode "type=WALLET"
{
"address": "<relayer_address>",
"nonce": "<wallet_nonce>"
}
Define the Session Key Revocation
revokeSessionSigner call that the Deposit Wallet will
execute:function revokeSessionSigner(address sessionSigner)
import { encodeFunctionData, parseAbi } from "viem"
const sessionSignerAddress = "<session_signer_address>" as `0x${string}`
const calldata = encodeFunctionData({
abi: parseAbi([
"function revokeSessionSigner(address sessionSigner)",
]),
functionName: "revokeSessionSigner",
args: [sessionSignerAddress],
})
Prepare the Owner Authorization
{
"domain": {
"name": "DepositWallet",
"version": "1",
"chainId": 137,
"verifyingContract": "<deposit_wallet_address>"
},
"types": {
"Call": [
{ "name": "target", "type": "address" },
{ "name": "value", "type": "uint256" },
{ "name": "data", "type": "bytes" }
],
"Batch": [
{ "name": "wallet", "type": "address" },
{ "name": "nonce", "type": "uint256" },
{ "name": "deadline", "type": "uint256" },
{ "name": "calls", "type": "Call[]" }
]
},
"primaryType": "Batch",
"message": {
"wallet": "<deposit_wallet_address>",
"nonce": "<wallet_nonce>",
"deadline": "<batch_deadline_unix_seconds>",
"calls": [
{
"target": "<deposit_wallet_address>",
"value": "0",
"data": "<calldata>"
}
]
}
}
<deposit_wallet_address>is the Deposit Wallet address.<wallet_nonce>is the nonce returned when fetching the Deposit Wallet nonce.<batch_deadline_unix_seconds>is a future whole Unix timestamp in seconds after which the signed revocation request can no longer be executed. The Relayer requires at least 10 seconds of remaining validity.<calldata>is the encoded revocation call from the previous step.
Sign as the Deposit Wallet Owner
import { privateKeyToAccount } from "viem/accounts"
const depositWalletOwner = privateKeyToAccount(
process.env.DEPOSIT_WALLET_OWNER_PRIVATE_KEY as `0x${string}`,
)
const signature =
await depositWalletOwner.signTypedData(walletBatchTypedData)
Build the Revocation Body
{
"walletAddress": "<deposit_wallet_address>",
"sessionSignerAddress": "<session_signer_address>",
"nonce": "<wallet_nonce>",
"deadline": "<batch_deadline_unix_seconds>",
"signature": "<signature>"
}
<session_signer_address>is the session key to revoke.<wallet_nonce>and<batch_deadline_unix_seconds>match the values in the signed typed data.<signature>is the value returned in the previous step.
Create the Builder Signature
revocation_body = <exact_serialized_revocation_body>
request_timestamp = <unix_seconds>
method = "POST"
request_path = "/v1/session-signers/revocations"
message = request_timestamp + method + request_path + revocation_body
builder_signature = urlsafeBase64WithPadding(
HMAC-SHA256(base64Decode(<builder_api_secret>), message)
)
Submit the Revocation
curl -X POST "https://relayer-v2.polymarket.com/v1/session-signers/revocations" \
-H "Content-Type: application/json" \
-H "POLY_BUILDER_API_KEY: <builder_api_key>" \
-H "POLY_BUILDER_TIMESTAMP: <request_timestamp>" \
-H "POLY_BUILDER_PASSPHRASE: <builder_api_passphrase>" \
-H "POLY_BUILDER_SIGNATURE: <builder_signature>" \
-H "Idempotency-Key: <idempotency_key>" \
-d '{
"walletAddress": "<deposit_wallet_address>",
"sessionSignerAddress": "<session_signer_address>",
"nonce": "<wallet_nonce>",
"deadline": "<batch_deadline_unix_seconds>",
"signature": "<signature>"
}'
<idempotency_key>uniquely identifies this revocation request. Reuse it when retrying the same request.<request_timestamp>is the Unix timestamp in seconds used to create the Builder signature.<builder_api_key>is your Builder API key.<builder_api_passphrase>is the passphrase for your Builder API key.<builder_signature>is the signature created in the previous step.
{
"fenced": true,
"operationId": "<operation_id>",
"status": "<revocation_status>",
"transactionId": "<transaction_id>"
}
Confirm the Relayer Transaction
<transaction_id>:curl "https://relayer-v2.polymarket.com/v1/account/transactions/<transaction_id>"
state is STATE_CONFIRMED. Treat STATE_FAILED and
STATE_INVALID as terminal failures.{
"transaction_id": "<transaction_id>",
"transaction_hash": "<transaction_hash>",
"state": "STATE_CONFIRMED",
"error_msg": null
}