sheardocs

Developers

Indexer API

Read API over the finalized Shear indexer: routes, query parameters and response shapes.

The indexer reads Shear accounts and transactions at finalized commitment and stores a projection in PostgreSQL. This HTTP API serves that projection. On-chain state stays authoritative: the database never supplies settlement prices or custody balances, and every row records the finalized slot it was read at.

How it works#

  • Transactions. The indexer pages getSignaturesForAddress for the program at finalized commitment and fetches each transaction. It decodes events only from successful transactions, and only from log lines written while Shear is the active program in the invocation stack. Event-shaped lines from other programs are ignored.
  • Accounts. Each pass reads every program account with getProgramAccounts at finalized commitment and stores one snapshot per address, with the context slot and a SHA-256 of the raw data. An older read never overwrites a newer snapshot. An account that disappears keeps its last snapshot and is marked closed.
  • Cursor. The transaction cursor advances only after every transaction in the batch has been fetched. A pruned or missing transaction stops indexing until the RPC serves it.
  • Governance. With GOVERNANCE_REALM set, each pass also reads the SHEAR realm from SPL Governance (GOVERNANCE_PROGRAM_ID, GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw by default): the realm, its governances with their native treasuries, their proposals and the voter records. These snapshots carry the governance program’s ID and follow the same slot and closed rules.
  • Loop. The service waits 15 seconds between passes by default (POLL_INTERVAL_MS). When the keeper is enabled, it runs after each indexing pass; see keeper.

The service needs a Solana RPC that retains the transaction history being indexed, the cluster genesis hash, the program ID and a PostgreSQL database. The governance routes also need GOVERNANCE_REALM.

Conventions#

Integers
64- and 128-bit values (u64, i64, u128) are decimal strings, such as "12050000000". 8-, 16- and 32-bit values such as bump, bidCount, pendingTickets, otmBps, auctionSeconds and oracleExponent are JSON numbers.
Units
Base units as on chain: 8 decimals for TSLAx, shares and calls; 6 for USDC and USD marks; 9 for multipliers. See units.
Names and keys
Field names are camelCase forms of the Rust names. Public keys are base58 strings.
Enums
An object with one key, such as "phase": { "Active": {} }.
Byte arrays
calendarHash is an array of 32 numbers.
Slots
Snapshot and transaction slots are decimal strings. /ready and /operations/run report the live RPC slot as a number.
Times
On-chain times such as expiry, auctionEnd, open and close are Unix seconds as decimal strings. Database times observedAt, blockTime and updatedAt are ISO 8601 strings in UTC.
Chain
chain is the genesis hash the service is configured with. Every query is scoped to it.

Values in the examples below are illustrative.

Routes#

MethodPathQueryReturns
GET/health—Process liveness.
GET/ready—Database, chain identity and program checks.
GET/vaults—Live Vault snapshots.
GET/roundslimitLive Round snapshots.
GET/accounts/:address—One snapshot of any type, closed accounts included.
GET/eventslimit, owner, roundDecoded events with signature and slot.
GET/stake-config—Each vault’s bidder stake mint and amount.
GET/biddersowner, vault, stakedBidder records: approval and stake.
GET/governance—The SHEAR realm, its governances and treasuries, and whether each vault’s admin is one of them.
GET/governance/proposalsstate, limitProposals, newest first.
GET/governance/voters/:owner—A wallet’s voting power and voter records.
GET/jobs—The 100 most recently updated keeper jobs.
POST/operations/run—One indexer and keeper cycle. Operator route with a bearer token.

Read routes need no authentication and never sign transactions. Wallet actions such as deposits, bids and claims are built with the SDK and signed by their owners; the service holds no user keys.

Health and readiness#

GET /health#

Returns 200 while the process runs.

json
{ "status": "ok" }

GET /ready#

Checks the database connection, that the RPC genesis hash matches the configured chain, and that the configured program account is executable. With the keeper enabled it also checks that the Pyth receiver and Wormhole programs are deployed. Any failure returns 503.

200
{
  "status": "ready",
  "chain": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d",
  "slot": 447774012,
  "program": "GG8Db3XMAfAaa4RT38e5uaeFSDjztEyaATpR6B3AxMcn"
}
503
{
  "status": "unavailable",
  "message": "Database, chain identity, or deployed program validation failed"
}

GET /vaults#

All Vault accounts of the configured program that still exist, ordered by address. No parameters. Each item is an account snapshot:

FieldTypeMeaning
idstring<chain>:<address>.
chainstringConfigured genesis hash.
programstringProgram ID.
addressstringAccount address.
kindstringVault, Round, Position, Session, Bidder, StakeConfig, Ticket or Bid.
slotstringFinalized context slot of the read that produced data.
dataobjectDecoded account. Fields as in accounts.
dataHashstringSHA-256 of the raw account data, hex.
observedAtstringTime the row was last written.
closedbooleanTrue once the account no longer exists on chain.
json
[
  {
    "id": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d:9Q5nHgxW9864oE6q8JLjp6GioUkkQF1f3EsUcSgVYru9",
    "chain": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d",
    "program": "GG8Db3XMAfAaa4RT38e5uaeFSDjztEyaATpR6B3AxMcn",
    "address": "9Q5nHgxW9864oE6q8JLjp6GioUkkQF1f3EsUcSgVYru9",
    "kind": "Vault",
    "slot": "447774012",
    "data": {
      "admin": "FyYA2nnz9XfzAN3kcbHT3MVw31VGoxffz6m9REJRSKve",
      "underlying": "XsDoVfqeBukxuZHWhdvWHBhgEHjGNst4MLodqsJHzoB",
      "shareMint": "E61GhHfpx832wdG5BneKCzsujGP8au7FfKcaUyvv3NR7",
      "custody": "JAVt8HP5aphQPnxisZhXedRHeRCREdcQL2mzUZKDDELe",
      "premium": "7N1ieKGMaJXfhuZnJd5wSWx343iahVLZRT9EXDafd98j",
      "bump": 254,
      "config": {
        "otmBps": 500,
        "maxConfidenceBps": 100,
        "auctionSeconds": 300,
        "reservePremiumMicro": "1000000"
      },
      "premiumIndex": "39000000000000000",
      "roundIndex": "1",
      "active": true,
      "pendingAdmin": "11111111111111111111111111111111"
    },
    "dataHash": "33112ee14ee469c3eb52fe90322ec81dd404a0093d565a6d71ce77cbc8124e3b",
    "observedAt": "2026-10-01T15:02:11.482Z",
    "closed": false
  }
]

GET /rounds#

ParameterTypeDefaultRange
limitinteger251 to 100

Round snapshots of the configured program, ordered by snapshot slot, highest first. Each indexing pass refreshes every live account to the same slot, so rows usually tie: sort by data.index to order rounds. Round accounts are never closed, so past rounds stay in the set. To read one round, derive its address and use /accounts/:address.

GET /rounds?limit=1
[
  {
    "id": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d:HYjrczoD6N7A1Bh5KXYqCyFChNGwF1d3Lz9ZVN6fLrCN",
    "chain": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d",
    "program": "GG8Db3XMAfAaa4RT38e5uaeFSDjztEyaATpR6B3AxMcn",
    "address": "HYjrczoD6N7A1Bh5KXYqCyFChNGwF1d3Lz9ZVN6fLrCN",
    "kind": "Round",
    "slot": "447774012",
    "data": {
      "vault": "9Q5nHgxW9864oE6q8JLjp6GioUkkQF1f3EsUcSgVYru9",
      "index": "1",
      "bump": 255,
      "phase": { "Active": {} },
      "expiry": "1790971200",
      "auctionEnd": "1790602500",
      "strike": "262629629",
      "entryMultiplier": "1000000000",
      "offered": "12050000000",
      "sold": "12050000000",
      "clearing": "3900000",
      "premiumTotal": "469950000",
      "optionMint": "FRCSNi9nFyLrHXoAhnnhxLpzTiuA5HBCPEbVif2Q9XL8",
      "bidCount": 2,
      "claimedBids": 1,
      "bids": [
        {
          "owner": "J2jvoZVGZGWv6Y7rVDaUh69YRZEGyBHq17mf4F4YgSac",
          "quantity": "8000000000",
          "limit": "4250000",
          "allocation": "8000000000",
          "charge": "312000000"
        },
        {
          "owner": "uTUaJqR2k8J9Z3TrAnAQv664eWvJZRFYC8Umhz7fr9V",
          "quantity": "6000000000",
          "limit": "3900000",
          "allocation": "4050000000",
          "charge": "157950000"
        }
      ],
      "pendingTickets": 1,
      "settlement": "0",
      "closeMultiplier": "0",
      "observedAt": "0",
      "oraclePublish": "0",
      "oraclePrice": "0",
      "oracleExponent": 0,
      "reserveTotal": "0",
      "claimedOptions": "0",
      "snapshotNav": "0",
      "snapshotSupply": "0",
      "fallback": false
    },
    "dataHash": "f998fe06afa0cfbe73e0449dc2b1698309e1b5714960f027b2858312b152c275",
    "observedAt": "2026-10-01T15:02:11.482Z",
    "closed": false
  }
]

bids always has 16 entries. The example omits the 14 unused ones, which carry owner 11111111111111111111111111111111 and zero amounts. data.observedAt is the round’s on-chain observation time; the row’s observedAt is the database write time.

In this round 120.5 TSLAx backed 120.5 calls at a strike of 262.629629 USD. Two bids for 140 calls cleared at the lower limit, 3.90 USDC per call, for 469.95 USDC of premium.

GET /accounts/:address#

One snapshot by address, of any account type, including closed accounts. address must be a base58 string of 32 to 44 characters. Returns 404 with {"error":"account_not_indexed"} when the indexer has no row for it.

GET /accounts/21Df9aJXgcq78P62V4QDHWkPB5t9YCBD3cHYVg5kVPgF
{
  "id": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d:21Df9aJXgcq78P62V4QDHWkPB5t9YCBD3cHYVg5kVPgF",
  "chain": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d",
  "program": "GG8Db3XMAfAaa4RT38e5uaeFSDjztEyaATpR6B3AxMcn",
  "address": "21Df9aJXgcq78P62V4QDHWkPB5t9YCBD3cHYVg5kVPgF",
  "kind": "Position",
  "slot": "447774012",
  "data": {
    "vault": "9Q5nHgxW9864oE6q8JLjp6GioUkkQF1f3EsUcSgVYru9",
    "owner": "FPP21sbqhr2LPjSnJkw5NBetPubeFG4PsQFBxHj8noTq",
    "shares": "2500000000",
    "pendingWithdraw": "0",
    "premiumIndex": "0",
    "premiumCredit": "0",
    "premiumFraction": "0"
  },
  "dataHash": "97fb5f8538b89f6c1accfd19836b65a73b61fbc2e0cbf84bb858a0fffa3f1592",
  "observedAt": "2026-10-01T15:02:11.482Z",
  "closed": false
}

This position holds 25 shares and was last checkpointed before round 1 finalized. Its claimable premium is 2,500,000,000 × 39,000,000,000,000,000 / 10^18 = 97,500,000 micro-USDC, or 97.50 USDC.

GET /events#

ParameterTypeDefaultFilter
limitinteger251 to 100 rows.
ownerbase58 string—Events whose owner field equals this wallet.
roundbase58 string—Events whose round field equals this round.

Ordered by transaction slot, then log index, newest first. owner and round combine with AND. VaultInitialized, SessionRegistered, RoundStarted, AuctionFinalized, ExpiryObserved, ExpiryDisputed, RoundSettled, OracleFailureSettled and RoundClosed have no owner field and never match an owner filter.

FieldTypeMeaning
idstring<chain>:<signature>:<logIndex>.
transactionIdstring<chain>:<signature>.
logIndexnumberIndex of the log line in the transaction.
namestringEvent name, such as BidPlaced.
dataobjectEvent fields in camelCase. See events.
transaction.signaturestringTransaction signature.
transaction.slotstringTransaction slot.
transaction.blockTimestring | nullBlock time, ISO 8601.
GET /events?round=HYjrczoD6N7A1Bh5KXYqCyFChNGwF1d3Lz9ZVN6fLrCN&limit=2
[
  {
    "id": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d:3g1L6drRfeVmrEPdFuV5sHFVaHMnE24T5DpTMycT1HqazZWXhr56dTp8A8yiRVMsy4meTLaJxocyXgBHYzCV7HAe:7",
    "transactionId": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d:3g1L6drRfeVmrEPdFuV5sHFVaHMnE24T5DpTMycT1HqazZWXhr56dTp8A8yiRVMsy4meTLaJxocyXgBHYzCV7HAe",
    "logIndex": 7,
    "name": "AuctionFinalized",
    "data": {
      "round": "HYjrczoD6N7A1Bh5KXYqCyFChNGwF1d3Lz9ZVN6fLrCN",
      "sold": "12050000000",
      "clearing": "3900000",
      "premium": "469950000"
    },
    "transaction": {
      "signature": "3g1L6drRfeVmrEPdFuV5sHFVaHMnE24T5DpTMycT1HqazZWXhr56dTp8A8yiRVMsy4meTLaJxocyXgBHYzCV7HAe",
      "slot": "447121705",
      "blockTime": "2026-09-28T13:35:41.000Z"
    }
  },
  {
    "id": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d:52hc5aVxXsS8KCg7NsULoqVxt3ZstfhVdv3MfF9NM7jnLxjPPsAHTuASirrv3XPZMBTr3hN1GmWrhPtSEeUzF1rh:9",
    "transactionId": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d:52hc5aVxXsS8KCg7NsULoqVxt3ZstfhVdv3MfF9NM7jnLxjPPsAHTuASirrv3XPZMBTr3hN1GmWrhPtSEeUzF1rh",
    "logIndex": 9,
    "name": "BidPlaced",
    "data": {
      "round": "HYjrczoD6N7A1Bh5KXYqCyFChNGwF1d3Lz9ZVN6fLrCN",
      "owner": "uTUaJqR2k8J9Z3TrAnAQv664eWvJZRFYC8Umhz7fr9V",
      "quantity": "6000000000",
      "limit": "3900000"
    },
    "transaction": {
      "signature": "52hc5aVxXsS8KCg7NsULoqVxt3ZstfhVdv3MfF9NM7jnLxjPPsAHTuASirrv3XPZMBTr3hN1GmWrhPtSEeUzF1rh",
      "slot": "447121180",
      "blockTime": "2026-09-28T13:32:07.000Z"
    }
  }
]

GET /stake-config#

The StakeConfig snapshot of each vault that has one: the mint new bidder stakes are paid in and the amount each locks. No parameters. See bidder stake.

json
[
  {
    "address": "4nB6fPq3sX9q2tJdN1o1yWm6a9qS1vB3KQfD3qJg8vTq",
    "kind": "StakeConfig",
    "slot": "447774012",
    "data": {
      "vault": "9Q5nHgxW9864oE6q8JLjp6GioUkkQF1f3EsUcSgVYru9",
      "mint": "GvJRrJo7TQTRM9ALWDkRurGBWi9qk8U66xLrJTMYS2d",
      "amount": "1000000000000",
      "bump": 253
    },
    "closed": false
  }
]

GET /bidders#

ParameterTypeDefaultFilter
ownerbase58 string—Records of this wallet.
vaultbase58 string—Records of this vault.
stakedboolean—true: records holding a stake. false: records without one.

Bidder snapshots ordered by address; filters combine with AND. A wallet can bid when allowed is true or when stake is above 0 with unlockAt "0". While openBids is above 0, the stake can’t be withdrawn.

GET /bidders?staked=true
[
  {
    "address": "9MVeDqwaoCGoFmyCYFufdkMH191yuNz1g64VTKkUocoT",
    "kind": "Bidder",
    "data": {
      "vault": "9Q5nHgxW9864oE6q8JLjp6GioUkkQF1f3EsUcSgVYru9",
      "owner": "2HMn5D3UYSR4XiZAm5g6MaHTY9vtgHtnXTK7dvbvEQmF",
      "allowed": false,
      "stake": "1000000000000",
      "unlockAt": "0",
      "openBids": 1
    },
    "closed": false
  }
]

Governance routes#

Served from the SPL Governance snapshots of the configured realm. Without GOVERNANCE_REALM, /governance returns 404 governance_not_configured, and before the first pass it returns 404 governance_not_indexed. Account fields are SPL Governance’s, in camelCase; enums are numbers. See governance.

GET /governance#

The realm, its governances, and each vault’s admin. Each governance carries nativeTreasury, the account that signs what its proposals run, and that account’s balance in lamports. adminIsGovernance is true when a vault’s admin is one of those treasuries.

json
{
  "programId": "GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw",
  "realm": { "address": "Ergw4cXCsXnSjcPnbP1orhHFxnYygCzVt5vJGsfX7Xyh", "kind": "Realm", "data": { "name": "Shear", "communityMint": "GvJRrJo7TQTRM9ALWDkRurGBWi9qk8U66xLrJTMYS2d" } },
  "governances": [
    { "address": "FQmSXhi4huziUs2Qnr1oBoEkketZC3SNjbABSXPrJn47", "kind": "Governance", "data": { "nativeTreasury": "HQD1DesTPxS6mxZLpXm6i3BwHnJc72HnFdUuJcByVw7J", "nativeTreasuryLamports": 1995000000 } }
  ],
  "vaults": [
    { "address": "9Q5nHgxW9864oE6q8JLjp6GioUkkQF1f3EsUcSgVYru9", "admin": "HQD1DesTPxS6mxZLpXm6i3BwHnJc72HnFdUuJcByVw7J", "pendingAdmin": "11111111111111111111111111111111", "adminIsGovernance": true }
  ]
}

GET /governance/proposals#

ParameterTypeDefaultFilter
statestring—State name: Draft, SigningOff, Voting, Succeeded, Executing, Completed, Cancelled, Defeated, ExecutingWithErrors or Vetoed.
limitinteger251 to 100 rows.

Proposal snapshots, newest draft first. data.stateName is the state as a name; data.state keeps SPL Governance’s number.

GET /governance/voters/:owner#

A wallet’s voting power: the SHEAR it has deposited in the realm, summed over its voter records, as a decimal string in 6-decimal atoms. A wallet with no record has "0".

json
{
  "owner": "4wf4SW6X6PApKP9ra3e3txFFC5ZkGc2JTLNJ5mUA7GD6",
  "votingPower": "600000000000000",
  "records": [ { "address": "FnWdALw1Zj9uNxagwHu15x1vrdLseq8NZLUaMxdgjWcg", "kind": "TokenOwnerRecord", "data": { "governingTokenDepositAmount": "600000000000000" } } ]
}

GET /jobs#

The 100 most recently updated keeper jobs for the configured chain. Signed transaction bytes and keys are never returned. No parameters. See keeper.

FieldTypeMeaning
idstringSHA-256 of chain:vault:round:action, hex.
vaultstringVault address.
roundstringRound address, or session:<close> for a session registration.
actionstringstartRound, finalizeAuction, observeExpiry, challengeExpiry, settleRound, timeoutSettle, closeRound, registerSession or processTicket:<ticket>.
statusstringpreparing, signed, submitted, completed, retryable or awaiting_timeout.
attemptsnumberTimes the job was claimed.
signaturesstring[] | nullSignatures of the signed batch. Jobs that post a Pyth update can hold several.
resultobject | null{ "finalized": true } or { "recovered": true } when completed; an object with lastSignature and slot while submitting.
errorstring | nullLast failure message.
updatedAtstringLast update, ISO 8601.
json
[
  {
    "id": "2d73201bd8b91bf89da3b303ad56a67873f65bc5b4cab17be356f9ae91530ea2",
    "vault": "9Q5nHgxW9864oE6q8JLjp6GioUkkQF1f3EsUcSgVYru9",
    "round": "HYjrczoD6N7A1Bh5KXYqCyFChNGwF1d3Lz9ZVN6fLrCN",
    "action": "finalizeAuction",
    "status": "completed",
    "attempts": 1,
    "signatures": ["3g1L6drRfeVmrEPdFuV5sHFVaHMnE24T5DpTMycT1HqazZWXhr56dTp8A8yiRVMsy4meTLaJxocyXgBHYzCV7HAe"],
    "result": { "finalized": true },
    "error": null,
    "updatedAt": "2026-09-28T13:36:02.117Z"
  }
]

POST /operations/run#

Runs one fixed cycle: the readiness checks, one indexing pass, then the keeper if it is enabled. The body must be exactly {}, within 1,024 bytes. The route accepts no transaction bytes, account lists, prices or timestamps. While a cycle is already running, it returns {"status":"busy"}.

bash
curl -X POST "$SHEAR_API/operations/run" \
  -H "Authorization: Bearer $OPERATIONS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
json
{
  "indexed": { "transactions": 0, "slot": 447774012, "decoded": 10, "unknown": 0 },
  "actions": [
    { "vault": "9Q5nHgxW9864oE6q8JLjp6GioUkkQF1f3EsUcSgVYru9", "status": "wait_expiry", "until": 1790971200 }
  ]
}

indexed counts new transactions, decoded accounts and unrecognized accounts. Each actions item names a vault and either a keeper action with its job id and status (processTickets carries a tickets array of job results instead), or a waiting status: wait_auction, wait_expiry, wait_timeout, dispute_window, market_closed, insufficient_session_time, weekly_round_already_completed or waiting_for_initial_deposits. A vault that fails reports status: "failed" with a message. Without the keeper, actions is empty.

Errors#

StatusBodyWhen
400{"error":"invalid_request","message":"Request does not match the API schema"}A path or query value fails its schema, /rounds or /events gets an unknown query parameter, or the POST body is not {}.
401{"error":"unauthorized"}Missing or wrong bearer token on /operations/run.
404{"error":"account_not_indexed"}/accounts/:address has no row for the address.
500{"error":"internal_error","message":"The requested operation failed; inspect the service logs"}Any other failure.
503{"status":"unavailable","message":"Database, chain identity, or deployed program validation failed"}A /ready check failed.
503{"error":"operations_disabled"}/operations/run without a configured token.