Skip to content

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 }
  • chainId4663 (Robinhood mainnet) or 46630 (testnet/staging)
  • verifyingContract — the deployed PartiVault address (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.

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 get 401 unauthenticated regardless of how correct the order signature is.
  • The returned api_key is stable per user — re-registering after the cookie expires returns the same key. It doubles as your builder_api_key (below).
  • Renewal: call /v1/builders/register again 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 Withdraw nonce is the sequential on-chain vault nonce (vault.nonces(user), starting at 0), verified on-chain by PartiVault. Signing a timestamp nonce for a withdraw reverts with BadNonce. 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_key is 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.