Authentication
Every state-changing request to the oracle (place order, redeem, withdraw) is
authorized by an EIP-712 typed-data signature from an EVM wallet — there is
no API-key or HMAC bypass. A session cookie and an optional builder_api_key
layer on top of that, but neither replaces the per-request signature.
Your identity is an EVM 0x address, carried lowercase on the wire
(the EIP-55 checksum is display-only). This replaces the old Fogo/Ed25519
base58-keypair scheme.
The EIP-712 domain
Every signature binds to the PartiVault domain for the environment you target:
domain = { name: 'PartiVault', version: '1', chainId, verifyingContract }
chainId—4663(Robinhood mainnet) or46630(testnet/staging)verifyingContract— the deployedPartiVaultaddress (see Environments)
chainId + verifyingContract must match the gateway you point at; a
mismatch recovers the signature to the wrong address and the gateway returns
403 invalid_signature.
The signed structs
Four typed structs cover every authenticated action:
SessionBootstrap = { user, timestamp } (cookie)
Order = { market, user, outcome, side, price, size, nonce }
RedeemSets = { user, market, nonce }
Withdraw = { user, recipient, amount, nonce, deadline }
The order fields, with their EIP-712 types:
| Field | Type | Notes |
|---|---|---|
market |
bytes32 |
market-id slot (see API Reference) |
user |
address |
your lowercase 0x address |
outcome |
uint8 |
0 = yes, 1 = no |
side |
uint8 |
0 = buy, 1 = sell |
price |
uint256 |
basis points, 1–9999 |
size |
uint256 |
contracts |
nonce |
uint256 |
monotonic timestamp nonce (see below) |
The gateway verifies the signature recovers to user before the engine ever
sees the order — a missing or bad signature is rejected
(invalid_order_shape / invalid_signature). The struct hashing is pinned
byte-for-byte against shared ground-truth vectors shipped with the reference
bot, so a signer that reproduces them authenticates against the live gateway.
Cancels are the exception
/v1/orders/cancel and /v1/orders/cancel-all are authorized by the session
cookie alone — there is no CancelAll EIP-712 struct, and the engine
owner-checks each resting order against the acting user. Send cancels without a
signature.
Session cookie
POST /v1/builders/register issues an HTTP-only session cookie after a
one-time EIP-712 challenge. Sign the SessionBootstrap{user,timestamp} struct
under the PartiVault domain with your EVM key:
POST /v1/builders/register
{
"user": "<lowercase 0x address>",
"signature": "<65-byte EIP-712 signature>",
"timestamp": 1777500000
}
# Response (also sets Set-Cookie: parti_oracle_session=…; HttpOnly):
{ "api_key": "<stable per-user key>" }
- The cookie (
parti_oracle_session, HttpOnly/Secure/SameSite=None, ~7 days) authorizes session-scoped requests; orders still require the per-order signature above. Without the cookie you get401 unauthenticatedregardless of how correct the order signature is. - The returned
api_keyis stable per user — re-registering after the cookie expires returns the same key. It doubles as yourbuilder_api_key(below). - Renewal: call
/v1/builders/registeragain after expiry.
Builder attribution (builder_api_key)
builder_api_key is attribution, not authentication. Attach it to an
order/cancel body and, if your builder row is enabled with a non-zero
fee_bps, you earn your fee on every resulting fill. The fee is net-zero
(charged to the order's owner, not minted — see
Maker Rebates / Fees). It does not
bypass the signature and does not change order matching.
{
"market_id": "...",
"user": "<lowercase 0x address>",
"side": "buy",
"outcome": "yes",
"price": 6000,
"size": 100,
"order_type": "gtc",
"signature": "<65-byte EIP-712 Order signature — REQUIRED, never a placeholder>",
"nonce": 1777500000123,
"builder_api_key": "<your api_key>"
}
Your builder fee_bps starts at 0 on self-registration; an operator
raises it. Register/inspect builders in the operator dashboard.
Replay protection
- Registration timestamp must be within 60 seconds of server time (keep your clock NTP-synced).
- Orders use a per-user monotonic timestamp
nonce— verified off-chain; the engine rejects any nonce ≤ the last it accepted for that wallet. Any strictly-increasing value works (wall-clock ms is typical). - Withdraws are different: the
Withdrawnonce is the sequential on-chain vault nonce (vault.nonces(user), starting at0), verified on-chain byPartiVault. Signing a timestamp nonce for a withdraw reverts withBadNonce. See Deposits → Withdraw.
Security notes
- Sign on your backend; never ship a trading wallet's private key to a browser.
- Run a dedicated EVM key per bot — never reuse a real wallet's key. Generate
one with
viem:require('viem/accounts').generatePrivateKey(). - A
builder_api_keyis tied to one wallet and earns to that wallet only — it can't authorize trades for another wallet. - Withdrawals always require a fresh EIP-712 signature (with a
deadline), independent of session/builder auth.