Skip to main content

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’s builder field to be zero;
  • the Builder Gateway (/v1/builder/rfq), which also needs POLY_BUILDER_* headers from a Builder API key.
The official SDK implements only the Builder Gateway, and refuses Combos for Session Keys. A Builder API key does more than RFQs: the SDK sends it on every relayer request, where it authenticates relayer /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}/accept and GET /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 on POLY_BUILDER_ headers and on the Builder Gateway host. The signing package refuses an Exchange V3 order with a non-zero builder. Refusing POLY_BUILDER_* and POLYMARKET_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_URL must be https, 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) use POLYMARKET_DATA_API_URL as before. The HTTP client follows no redirects.
  • Quote requests and Accepts need a signer whose scopes include COMBOSRFQ or ALL, as the session-signers check reports them (PLAN §3.7, credential state). Without it the adapter and engine/risk refuse them with combos_unavailable and nothing is sent. A status read needs no scope check: it only reads an RFQ this app accepted.
  • Without POLYMARKET_SESSION_KEY the adapter still reads combo positions and activity by wallet address; the combo commands answer 503 combos_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.com host for POLYMARKET_COMBOS_URL, as for the other endpoints. Rejected: a misconfigured URL could point at the Builder Gateway.