For the complete documentation index, see llms.txt. This page is also available as Markdown.

Resolution

Resolution lifecycle endpoints: observe every market's resolution status, read the parameters needed to file a dispute on-chain, and submit a dispute rationale. How disputes work (stakes, rewards, review) is described in Dispute — this section covers the API.

The three GET endpoints are public (no API key). Responses are cached server-side for ~10 seconds. Business errors return HTTP 200 with a non-zero errno — handle errors by errno, not HTTP status.

Markets in the resolution flow are in one of three phases:

  • proposed — an outcome has been proposed; the dispute window is open until disputeDeadline

  • disputed — a dispute has been filed; review in progress until arbitrationDeadline

  • finalized — terminal: resultTokenId is set; if disputed, disputeOutcome = upheld / overturned / timeout (dispute rejected / accepted / review timed out)

Fields default to "" (strings) or null (numbers/times) until their stage occurs. Markets resolved automatically by a price oracle (e.g. recurring crypto price markets) have no dispute window: they appear as finalized-only records with disputeDeadline: null and dispute: null.

Error codes

Returned with HTTP 200 in the envelope:

  • 10003 — invalid parameters (errmsg says which)

  • 11011 / 11012 / 11013 — rationale signature invalid / expired / already used

  • 11020 — no resolution record for this market

  • 11021 — no dispute filed for this question

  • 11022 — signer is not the on-chain disputor

  • 11023 — rationale frozen (already finalized)

  • 11024 — rationale too long (max 2000 bytes)

Get resolution market list

get

Markets in the resolution flow, viewed from the resolution side.

Fixed ordering: proposed rows by disputeDeadline ascending, then disputed rows by arbitrationDeadline ascending, then finalized rows by finalizedAt descending.

Self-indexing with events

If you index on-chain events directly, subscribe on the disputeResolver addresses returned by the API. Event signatures:

ResolutionReceived(bytes32 indexed questionId, uint256[2] payouts, uint256 disputeDeadline)
RewardDeposited(bytes32 indexed questionId, address indexed stakeToken, uint256 amount)
DisputeFiled(bytes32 indexed questionId, address indexed disputor, uint256 stakeAmount, uint8 reason, uint256 reVoteDeadline)
DisputeResolved(bytes32 indexed questionId, bool overturned, uint256[2] finalPayouts)
DisputeTimedOut(bytes32 indexed questionId)
ResolutionFinalized(bytes32 indexed questionId, uint256[2] payouts)
StakeClaimed(bytes32 indexed questionId, address indexed recipient, uint256 amount)

A market enters the feed as proposed when RewardDeposited lands (that transaction fixes the dispute window); ResolutionReceived is a slightly earlier step carrying the proposed result, and anchors proposedAt.

Query parameters
phasestring · enumOptional

Filter by phase

Possible values:
labelIdinteger · int64Optional

Filter by category label id — same ids as GET /label

pageinteger · min: 1Optional

Page number

Default: 1
limitinteger · max: 20Optional

Number of items per page (max 20)

Default: 10
Responses
200

Successful response

application/json

Envelope used by the Resolution endpoints. The outcome is reported in errno / errmsg with HTTP 200; see the Resolution section for the error-code list.

errnointegerOptional

Business result code (0 for success)

Example: 0
errmsgstringOptional

Error message (empty on success)

get/resolution/markets
200

Successful response

Get resolution market detail

get

Full resolution view of one market: all list-row fields plus the rules text, the tx-anchored timeline, and the dispute object carrying everything needed to send dispute/claim transactions.

Returns errno 11020 when the market has no resolution record (nothing proposed yet, categorical parent id — children resolve independently — or a market predating the feed).

Disputing on-chain

Filing and claiming are direct contract calls with the parameters from the dispute object.

Always call isDisputable(questionId) on-chain immediately before sending a dispute transaction. API responses are cached ~10 s; isDisputable is the on-chain truth for deadline boundaries and the one-dispute-per-resolution slot. Skipping it risks a reverted transaction.

const DR_ABI = [
  "function dispute(bytes32 questionId, uint8 reason) external",
  "function claim(bytes32 questionId) external",
  "function isDisputable(bytes32 questionId) external view returns (bool)",
];
const ERC20_ABI = ["function approve(address spender, uint256 amount) returns (bool)"];

const dr  = new ethers.Contract(dispute.disputeResolver, DR_ABI, wallet);
const opn = new ethers.Contract(dispute.stakeToken, ERC20_ABI, wallet);

if (!(await dr.isDisputable(questionId))) throw new Error("not disputable");

const stake = ethers.parseUnits(dispute.requiredStake, dispute.stakeTokenDecimals);
await (await opn.approve(dispute.disputeResolver, stake)).wait();
await (await dr.dispute(questionId, 0 /* 0 = WrongResult, 1 = TooEarly */)).wait();

// after finalization, when claimStatus = "claimable":
await (await dr.claim(questionId)).wait();
Path parameters
marketIdstringRequired

Numeric market id, or a 0x-prefixed question id

Responses
200

Successful response

application/json

Envelope used by the Resolution endpoints. The outcome is reported in errno / errmsg with HTTP 200; see the Resolution section for the error-code list.

errnointegerOptional

Business result code (0 for success)

Example: 0
errmsgstringOptional

Error message (empty on success)

get/resolution/market/{marketId}
200

Successful response

Get dispute list

get

Disputes across all markets, with reason, rationale, outcome and claim state. Ordered by disputedAt descending (newest first).

proposedPayouts / proposedTokenId are the original proposal that was disputed; resultTokenId / finalizeTxHash stay empty while review is in progress.

Query parameters
disputorstringOptional

Filter by disputor wallet address

outcomestring · enumOptional

Filter by outcome (pending = review in progress)

Possible values:
pageinteger · min: 1Optional

Page number

Default: 1
limitinteger · max: 20Optional

Number of items per page (max 20)

Default: 10
Responses
200

Successful response

application/json

Envelope used by the Resolution endpoints. The outcome is reported in errno / errmsg with HTTP 200; see the Resolution section for the error-code list.

errnointegerOptional

Business result code (0 for success)

Example: 0
errmsgstringOptional

Error message (empty on success)

get/resolution/disputes
200

Successful response

Submit dispute rationale

post

After your dispute transaction confirms, attach a public statement. It is shown with the dispute in the endpoints above. Authenticated by an EIP-712 signature from the disputing wallet — no API key or account needed; the server verifies the signer against the on-chain disputor, so you can submit as soon as the dispute transaction confirms. Verification is ECDSA recovery, so the disputing wallet must be an EOA — disputes filed from a contract wallet (e.g. a Safe) cannot attach a rationale.

Accepted while the dispute is under review; resubmit to overwrite; frozen after finalization. Signatures expire after 5 minutes and are single-use.

Sign the following typed data — walletAddress lowercase, questionId exactly as in the URL, rationale exactly the string sent in the body. The server strips leading/trailing whitespace before verifying, so sign and send the rationale without surrounding whitespace. Maximum 2000 bytes (UTF-8):

const domain = { name: "Opinion OpenAPI", version: "1", chainId: 56 };
const types = {
  OpinionDisputeRationale: [
    { name: "walletAddress", type: "address" },
    { name: "questionId",    type: "string"  },
    { name: "rationale",     type: "string"  },
    { name: "timestamp",     type: "uint256" },
  ],
};
const timestamp = Math.floor(Date.now() / 1000).toString();
const message = {
  walletAddress: wallet.address.toLowerCase(),
  questionId,
  rationale: "Final score was 3-2 per MLB.com; the proposal resolves the wrong side.",
  timestamp,
};
const signature = await wallet.signTypedData(domain, types, message);

Send signature in OPINION_SIGNATURE, the wallet address in OPINION_ADDRESS and the signed timestamp in OPINION_TIMESTAMP.

Path parameters
questionIdstringRequired

0x-prefixed question id, exactly as returned by the API

Header parameters
OPINION_ADDRESSstringRequired

Disputor wallet address (must equal the on-chain disputor)

OPINION_SIGNATUREstringRequired

EIP-712 signature over the OpinionDisputeRationale typed data (see the endpoint description)

OPINION_TIMESTAMPstringRequired

Unix timestamp in seconds; must equal the signed timestamp field. Valid for 5 minutes.

Body
rationalestringRequired

Public statement explaining the dispute — must equal the signed rationale; at most 2000 bytes (UTF-8). Surrounding whitespace is stripped server-side

Example: Final score was 3-2 per MLB.com; the proposal resolves the wrong side.
Responses
200

Successful response (check errno in the body)

application/json

Envelope used by the Resolution endpoints. The outcome is reported in errno / errmsg with HTTP 200; see the Resolution section for the error-code list.

errnointegerOptional

Business result code (0 for success)

Example: 0
errmsgstringOptional

Error message (empty on success)

post/resolution/dispute/{questionId}/rationale
200

Successful response (check errno in the body)

Last updated