> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sesameterminal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 0014. Combos go through the Requester API with CLOB L2 credentials only

# 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](../venue/polymarket.md#builder-credentials-not-needed)):

* 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](0012-session-key-scope.md) 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](0009-venue-credentials-in-memory.md)).
  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](../runbooks/session-key-setup.md#8-combos-scope)).
* 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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.