Claim winnings
Find settled positions that need a claim, call POST /execution/redeem, and track the result
Some venues pay a settled position on their own. Others need a claim, and sufwan-local-test makes that claim
for you by default. POST /execution/redeem claims the user's managed positions as USDC on demand.
It needs no wallet prompt.
Automatic or manual, by venue
| Venue | Claim |
|---|---|
| Polymarket, Limitless, Predict, Myriad | Automatic by default: sufwan-local-test claims managed winning positions about 2 minutes after the market resolves. If you turn auto-redeem off, call POST /execution/redeem. |
| Kalshi, Hyperliquid, BetDEX, TipRun, PRED | Automatic. No call. |
| ProphetX direct accounts | Automatic, in the user's own ProphetX account. |
| Novig, ProphetX clearinghouse | No claim through sufwan-local-test. The call returns ineligible. |
Self-custody positions on Polymarket, Limitless, Predict and Myriad are redeemed with
redeemSelfCustody (see Self-custody). This becomes available once sufwan-local-test
enables it on the platform; until then the fill is refused with
quote_self_custody_redeem_unsupported. On Polymarket
it works only for shares in the user's Polymarket deposit wallet. On other venues the user claims
them on the venue.
Opting out of automatic claims. Automatic claiming covers Polymarket, Limitless, Predict and Myriad, and is on for every app by default. To claim on your own schedule instead, turn off Settings → Auto-redeem winning positions in the sufwan-local-test dashboard, then use the steps below. Self-custody positions are never claimed automatically.
You do not need this table at run time. Read redeemStatus on each position instead.
1. Find claimable positions
GET /execution/positions returns one group per market. Claim the groups whose redeemStatus is
eligible, using the winning outcome ids in venueBreakdown.
const positions = await client.getExecutionPositions();
const outcomeIds = positions.data
.filter((p) => p.redeemStatus === "eligible")
.flatMap((p) => p.venueMarket.venueMarketOutcomes)
.filter((o) => o.winner === true)
.flatMap((o) => o.venueBreakdown.map((leg) => leg.venueMarketOutcomeId));redeemStatus | Meaning |
|---|---|
eligible | Claim it. |
pending | A claim is in progress. Wait. |
redeemed | Paid. Nothing to do. |
ineligible | Nothing to claim through sufwan-local-test: not settled yet, lost, settled by the venue, or self-custody. |
A split or void can mark more than one outcome as a winner. Read Market resolution & voids before you show an amount.
2. Redeem
if (outcomeIds.length > 0) {
const { redeemId, results } = await client.redeem({ venueMarketOutcomeIds: outcomeIds });
for (const r of results) console.log(redeemId, r.venueMarketOutcomeId, r.status, r.reason);
}Redeem winnings for resolved positions
Send x-app-id and the user's access token. Send 1 to 20 ids per call. mode (live or paper)
is optional and defaults to live. Claims, paper included, are refused with 403 and
code: "geo_blocked" where your app's or the platform's region list blocks the user. The check is
the same for every venue.
A 4xx means no claim started. After a timeout or 5xx, some outcomes may have been submitted:
they are claimed one at a time. The claim shows in GET /users/activity?type=redeem before
anything is sent on-chain. Resending the same ids is safe and is how you find out. An outcome
already being claimed returns rejected with redeem in progress, and one already paid returns
ineligible with already redeemed. A position is never paid twice.
3. Read the result
The call returns before an on-chain claim finishes. Each results[] entry has a status and a
reason:
status | Meaning | What to do |
|---|---|---|
submitted | The claim is on its way. | Wait for completion (below). |
confirmed | Done, or nothing was owed. | Nothing. |
ineligible | Not claimable now. reason says why, for example market not resolved, market winner not yet available, already redeemed, or venue ... does not support redeem. | Retry later only for the first two. |
rejected | Refused. reason says why, for example not owned by user or unknown id, redeem in progress, or position reserved for execution. | Fix the id, or retry after the other claim or sell finishes. |
To see completion, read positions again until redeemStatus is redeemed, or listen for
redeem_event on the WebSocket.