0014. Combos go through the Requester API with CLOB L2 credentials only
- Status: Proposed
- Date: 2026-10-01
Context
Phase 8 adds Combos: the user asks the market makers for a quote on 2 to 50 legs and accepts it by signing an Exchange V3 order. Polymarket runs two gateways for this (venue notes §16):- the Requester API (
combos-rfq-gateway-requester-api.polymarket.com,/v1/requester/rfq), authenticated with CLOB L2 headers only, which requires the order’sbuilderfield to be zero; - the Builder Gateway (
/v1/builder/rfq), which also needsPOLY_BUILDER_*headers from a Builder API key.
/submit (wallet batches: approvals, split, merge, redeem, transfers)
and session-key authorisation. The app holds trade-only credentials and has no relayer
code (CLAUDE.md, PLAN §1), and ADR 0012 rests on the relayer
being the one barrier between a leaked Session Key and the funds. A builder key on the app
host would open that barrier from our side.
Decision
- The Combos adapter (
backend/internal/venue/polymarket/combos) calls the Requester API directly over HTTP:POST /requests,POST /requests/{rfq_id}/acceptandGET /requests/{rfq_id}. It does not use the SDK. - Every call carries CLOB L2 headers from the Predictions credential actor, the same
credentials that belong to the Session Key (ADR 0009).
The HMAC covers the full
/v1/requester/rfq/...path and the exact body bytes, and each attempt is signed with a new timestamp. - No Builder API credentials. The app reads no variable for them, and the scope lint
(
scripts/scope-patterns.txt) fails onPOLY_BUILDER_headers and on the Builder Gateway host. The signing package refuses an Exchange V3 order with a non-zerobuilder. RefusingPOLY_BUILDER_*andPOLYMARKET_BUILDER_*variables at startup, so a builder key is not left on the host, is open as security finding P8-08. - The host is pinned.
POLYMARKET_COMBOS_URLmust behttps, with no path, query or credentials, and its host must be exactly the Requester API host; the adapter refuses any other host at start, the Builder Gateway included. The Data API reads (combo positions and activity) usePOLYMARKET_DATA_API_URLas before. The HTTP client follows no redirects. - Quote requests and Accepts need a signer whose scopes include
COMBOSRFQorALL, as the session-signers check reports them (PLAN §3.7, credential state). Without it the adapter andengine/riskrefuse them withcombos_unavailableand nothing is sent. A status read needs no scope check: it only reads an RFQ this app accepted. - Without
POLYMARKET_SESSION_KEYthe adapter still reads combo positions and activity by wallet address; the combo commands answer 503combos_unavailable.
Consequences
- The app gains no relayer access, and ADR 0012’s analysis still holds for Phase 8.
- The app carries its own client for an API the SDK does not cover. The create and accept bodies and headers are checked against golden vectors from the SDK’s Builder path recomputed for the requester paths (venue notes §16, “Golden vectors”).
- The Session Key has to be re-issued with the combos scope, and the old
CLOB-only key revoked (Session Key setup §8). - Whether the gateway accepts a Session Key’s L2 credentials and its wrapped accept signature is UNVERIFIED until the live run (live verification rows 16.40 and 16.41).
- If Polymarket moves the Requester API to another host, the app refuses to start with a config error until the pinned host is changed in code.
Alternatives considered
- Builder Gateway with a Builder API key. Rejected: the key also authenticates relayer calls, which the app must not be able to make.
- Use the SDK. Rejected: it implements only the Builder Gateway and refuses Session Keys for Combos.
- Allow any
polymarket.comhost forPOLYMARKET_COMBOS_URL, as for the other endpoints. Rejected: a misconfigured URL could point at the Builder Gateway.