The request
Each entry inpendingSignatures[] looks like this:
What each type asks for
Send exactly one of
signature and txHash per step. Signatures are 0x-prefixed hex.
A fill funded entirely from a legacy Polymarket Safe asks for one safe_tx signature and never an
approve or an EIP-7702 authorization. A fill funded from a Safe plus another wallet on the same
chain also asks for the usual steps for that other wallet.
A complete signer
fillSelfCustody calls your signer once per request. Return a hex signature, or { txHash } for a
transaction.
transaction reaches the trader. Only when the fill has to bridge, the source token needs
an on-chain approve rather than a permit signature, and the fill set
approveMode: "user_broadcast". In the default mode the same approval is signed as eip712.
Venue orders, the Polymarket wrap, and the Hyperliquid builder-fee approval are always signatures.
The other transaction you may see is an app-paid referral payout addressed to your payer wallet.
Check signerAddress before you prompt. See Fees.
After a transaction, return the hash without waiting. AGG watches for the mined receipt and does
not continue until it matches the request.
Timeouts, and what expiry means
Every request carriesexpiresAt. Read it; never hard-code a deadline. Steps in one fill can
have different deadlines, and the shortest is tight:
When a fill dies,
GET /execution/status reports overallState failed or expired with an
errorReason, and fillSelfCustody throws with that state in its message.
An expired request disappears from the pending list rather than showing as expired, so an empty list
never means “done”. Only terminal: true does. Submitting a signature for an expired request
returns 400. There is no recovery: quote again. A user has one live execution at a time. If a
request expires instead of being answered, a new fill does not start until the abandoned one has
finished failing, so ask the user to complete or abandon deliberately.
How many wallet prompts the user sees
Requests parked together arrive in one batch, but the user approves each one. The total is the sum of three parts: the venue order, a Polymarket funding step, and the funding transfers. Venue order
Polymarket funding step
Funding transfers, per funding source. A route funded from two chains pays this twice.
With a partner fee on an EVM-funded buy, the funding batch also carries the fee transfers. A
user-paid referral is one more transfer in the same batch.
Worked examples: a Hyperliquid buy from an existing Hyperliquid balance is 1 prompt (2 while the
builder approval is outstanding). A Polymarket buy bridged from native USDC is 4 (one funding
transfer, one wrap, two order signatures), and stays 4 on repeat. The same buy from a token needing
an approval, before the first authorization, is 6. A Polymarket sell is 2, or 3 the
first time that deposit wallet trades.
Driving the loop yourself
WithoutfillSelfCustody, run this loop:
POST /execution/fillwithquoteIdandsigningAddress. The response never carriespendingSignatures, even for self-custody.- Poll
GET /execution/status?quoteId=, waitingpollAfterMsbetween calls. - For each new
pendingSignatures[]entry, have the wallet named bysignerAddresssign or send the payload. - Submit the results to
POST /execution/fill/{quoteId}/signaturesas{ "signatures": [{ "stepId": "...", "signature": "0x..." }] }(ortxHashfor atransaction). Up to 32 entries per call. The response has the same shape as the fill response. - Repeat from step 2 until
terminalistrue.
Resubmitting a step that already succeeded is ignored, so retrying a batch after a network error is
safe. Do not send the same
stepId twice in one body: duplicates collapse to the last entry, so one
bad entry discards the good one and the request fails.