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

# UMA history (REST)

> Query the complete record of Polymarket's UMA resolutions — proposals, disputes and settlements over all history — by question or condition id, with from/to ranges, cursor paging and resolution_source provenance.

Use these routes for **backtests, audits, dashboards — anything older than the [live window](https://docs.bravadotrade.com/api/analytics/uma-live.md)**. They read the complete record of Polymarket's UMA requests, proposals, disputes and settlements from the warehouse (`pm_uma_oracle_events`), with `from` / `to` ranges, cursor paging and `resolution_source` provenance. Only Polymarket's UMA adapters asking the `YES_OR_NO_QUERY` identifier are included; other protocols that use the same oracle are not.

Read the [UMA overview](https://docs.bravadotrade.com/products/uma-api.md) for the lifecycle and [coverage](https://docs.bravadotrade.com/products/uma-api.md#coverage-and-history).

> [!NOTE]
>
> UMA REST is part of the [Data API](https://docs.bravadotrade.com/products/data-api.md): sign every request with your **Data API** credential (`analytics.read`) using [HMAC authentication](https://docs.bravadotrade.com/authentication.md). Each successful response costs **1 API credit**, and the surface has its own per-key rate bucket. The UMA **WebSocket** is different: it stays on the Market Data credential (`stream.read`) — see [UMA WebSocket](https://docs.bravadotrade.com/api/market-data/uma-websocket.md). There is no separate UMA key.

> [!WARNING]
>
> A key that carries only `stream.read` can no longer read the UMA REST routes: they answer `403 INSUFFICIENT_SCOPE`. Use a Data API credential for REST and keep the Market Data credential for [`/uma/ws`](https://docs.bravadotrade.com/api/market-data/uma-websocket).

## Make a signed request

Use your Data API key and secret from [Console](https://console.bravadotrade.com/keys). This example makes one REST request. A successful response costs **1 API credit**. Keep the secret on your server.

```javascript Node.js theme={null}
import { createHash, createHmac } from "node:crypto";

const key = process.env.BRAVADO_API_KEY;
const secret = process.env.BRAVADO_API_SECRET;
if (!key || !secret) throw new Error("Set BRAVADO_API_KEY and BRAVADO_API_SECRET");

const url = new URL("https://partner-api.bravadotrade.com/uma/history/recent");
url.searchParams.set("stage", "settled");
url.searchParams.set("limit", "1");
url.searchParams.sort();
const timestamp = String(Date.now());
const requestTarget = url.pathname + url.search;
const bodyHash = createHash("sha256").update("").digest("hex");
const payload = [timestamp, "GET", requestTarget, bodyHash].join("\n");
const response = await fetch(url, {
  headers: {
    "X-Bravado-API-Key": key,
    "X-Bravado-Timestamp": timestamp,
    "X-Bravado-Signature": createHmac("sha256", secret).update(payload).digest("hex"),
  },
});
console.log("HTTP", response.status);
console.log(await response.json());
```

A `200` with `questions` confirms that your authenticated REST read succeeded. An empty `questions` array reports no matching records in the range. A `401` only confirms an authentication refusal, not a successful read of resolution data.

Base URL: `https://partner-api.bravadotrade.com`. Sign every REST request with HMAC, including its sorted query parameters. JSON; ids are hex, `payout` is an integer micro-USDC string.

The history routes serve the same fields as the [live routes](https://docs.bravadotrade.com/api/analytics/uma-live.md), from the complete on-chain record rather than the retained live window. `updated_ms` is the on-chain time of the latest event, and `seen_pending` is never present: history has no mempool pre-image.

## `GET /uma/history/question`

```text theme={null}
GET https://partner-api.bravadotrade.com/uma/history/question?id=<0x questionId>
```

**id** `string` *required*

The `0x` 32-byte `question_id`.

**from** `string`

Search window start: RFC3339, unix seconds or unix milliseconds. Default: all history.

**to** `string`

Search window end. Default: now.

Response fields are those of [`GET /uma/question`](https://docs.bravadotrade.com/api/analytics/uma-live#get-/uma/question), plus:

**resolution\_source** `string`

How `condition_id` was established. `derived`: computed from the question and confirmed against the condition map. `derived_unverified`: the same computation, **not** confirmed, and wrong for NegRisk markets; use `/uma/history/market` for the real condition id.

**lifecycles** `integer`

How many lifecycles of this question have proposal, dispute or settlement events in the window. `1` is normal. A question that is re-requested after it finished has more than one, and the **latest** is served. A dispute re-requests the price in the same transaction and stays one lifecycle, so a disputed question keeps its `disputed` stage and the final settlement that follows the token-holder vote.

A `404` with code `not_found` means no proposal, dispute or settlement for that id exists in the window.

## `GET /uma/history/market`

The same body, looked up by the market's CTF condition id.

```text theme={null}
GET https://partner-api.bravadotrade.com/uma/history/market?condition_id=<0x conditionId>
```

**condition\_id** `string` *required*

The `0x` 32-byte CTF condition id.

**from** `string`

Pin the search window start. Default: derived from the market.

**to** `string`

Pin the search window end. Default: now.

`condition_id` is always the **real** condition id, including for NegRisk markets. `resolution_source` says how the market was tied to its oracle request:

| `resolution_source` | Meaning |
| - | - |
| `derived` | Found through the condition map (standard markets). |
| `gamma_join` | Found by matching the market's Gamma id to the oracle request (covers NegRisk). |
| `unknown` | Neither join found an oracle request. |

When the answer is `unknown`, the response is **HTTP 200** with an explicit marker, not an empty success and not a `404`:

```json Unknown example theme={null}
{
  "condition_id": "0x…",
  "question_id": null,
  "resolution_source": "unknown",
  "reason": "no_oo_request_found",
  "searched_from_ms": 1792000000000,
  "searched_to_ms": 1792600000000
}
```

`reason` is one of `condition_not_in_pm_tokens`, `market_time_unknown`, `no_oo_request_found` or `no_stage_events` (an oracle request exists but nothing has been proposed yet; `question_id` is then set). An unknown carries no `settled`, `answer` or stage field: do not read it as an unsettled market.

## `GET /uma/history/recent`

Questions with proposal, dispute or settlement activity in a time range, newest activity first, one entry per question, paged with a cursor.

```text theme={null}
GET https://partner-api.bravadotrade.com/uma/history/recent?stage=settled&limit=100&from=1792000000
```

**stage** `string`

Filter on the question's state within the range: `proposed` (proposed, not disputed or settled) · `disputed` (disputed, not settled) · `settled`. Omit for all.

**limit** `integer` (default: 100)

Questions per page, 1–500.

**from** `string`

Range start: RFC3339, unix seconds or unix milliseconds. Default: `to` minus 24 hours.

**to** `string`

Range end. Default: now.

**cursor** `string`

The `next_cursor` of the previous page.

**questions** `array`

Question records (same shape as `GET /uma/history/question`). Each carries `resolution_source: "derived_unverified"`.

**count** `integer`

Records in this page.

**from\_ms** `integer`

Resolved range start (unix ms).

**to\_ms** `integer`

Resolved range end (unix ms).

**limit** `integer`

Page size applied.

**has\_more** `boolean`

Whether another page exists.

**next\_cursor** `string`

Pass as `cursor` to read the next page; `null` on the last page.

The range is at most **7 days**. A wider one is refused with `400 range_too_large` rather than silently shortened, so page a long period with consecutive `from` / `to` windows. Paging is keyset-based on the block time, block number and log index of each question's latest event in the range, so a page never repeats or skips a question.

> [!NOTE]
>
> For stable paging, pin `to` (an explicit timestamp) on every page request. With the default `to=now` the window moves between requests, and a question can be skipped across a page boundary. Each response echoes the resolved `to_ms` so you can reuse it.

## Errors

| Status | Code | Meaning |
| - | - | - |
| `400` | `invalid_id`, `invalid_condition_id`, `invalid_from`, `invalid_to`, `invalid_range`, `invalid_stage`, `invalid_limit`, `invalid_cursor`, `range_too_large` | The request is malformed, or the range is wider than 7 days. |
| `404` | `not_found` | `/uma/history/question`: no event for that id in the window. |
| `401` / `403` / `402` / `429` | | Signature, scope (`INSUFFICIENT_SCOPE`), credit allowance, rate limit. |
| `503` | `data_source_unavailable` | The history store is unavailable; retry. |

## Recover after a disconnect

After consuming the live [WebSocket](https://docs.bravadotrade.com/api/market-data/uma-websocket.md), call `/uma/history/recent` with `from` set to your last event to recover the questions that changed while you were away, in order, with no gap. For the lowest-latency reads of what is resolving right now, use the [live window](https://docs.bravadotrade.com/api/analytics/uma-live.md) instead.

## Related

* [UMA live window (REST)](https://docs.bravadotrade.com/api/analytics/uma-live.md) — lowest-latency reads of the realtime feed's retained window.
* [UMA WebSocket](https://docs.bravadotrade.com/api/market-data/uma-websocket.md) — the live `stream.read` stream of new proposals, disputes and settlements.
* [UMA overview](https://docs.bravadotrade.com/products/uma-api.md) — lifecycle, identifiers and coverage limits.


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