{"openapi":"3.1.0","info":{"title":"ApeCover API","version":"0.1.0","description":"Read-only view of indexed protocol state. Answers are derived from the event log and carry `asOfSlot`, so a consumer can see how stale they are. Not authoritative for decisions that move money — the chain is."},"servers":[{"url":"http://127.0.0.1:8080","description":"Local"}],"paths":{"/health":{"get":{"summary":"Liveness and projection freshness","description":"Always 200 while the process is up. The body says how far the projection has got, which is the question that actually matters.","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok","degraded"],"description":"degraded exactly when orphanedEvents is non-zero — the answers below are incomplete. The process is up either way; this is not a liveness signal, and ok is scoped exactly as narrowly as orphanedEvents is"},"asOfSlot":{"type":"string","pattern":"^\\d+$","description":"Highest slot folded into this answer"},"asOfTs":{"type":"integer","description":"Server time when the answer was produced, in unix seconds"},"eventsApplied":{"type":"integer","minimum":0,"description":"Chain events folded into the projection since it was built"},"orphanedEvents":{"type":"integer","minimum":0,"description":"Trade mutations arriving for a trade the projection never registered. Non-zero means the backfill started too late and every count here undercounts. Zero is not a completeness guarantee: this counts that one class only, and pool, keeper-registry and attestor-registry rows are created as empty skeletons on first mention and counted as applied"}},"required":["status","asOfSlot","asOfTs","eventsApplied","orphanedEvents"],"additionalProperties":false}}}},"400":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The requested resource does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/pool/{address}":{"get":{"summary":"Pool state as of the latest indexed slot","parameters":[{"name":"address","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"asOfSlot":{"type":"string","pattern":"^\\d+$","description":"Highest slot folded into this answer"},"asOfTs":{"type":"integer","description":"Server time when the answer was produced, in unix seconds"},"address":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"admin":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"vault":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"settlementMint":{"type":["string","null"],"minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"paused":{"type":"boolean","description":"Whether the pool is halted. While true the program refuses new policies and trade registrations; claims already filed still settle"},"totalPremiums":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"totalLiabilities":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"totalPaid":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"totalContributed":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"totalWithdrawn":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"policiesIssued":{"type":"integer","minimum":0,"description":"Policies created against this pool, over all time"},"tradesRegistered":{"type":"integer","minimum":0,"description":"Trades ever covered by this pool, over all time — not the live count"},"claimsPaid":{"type":"integer","minimum":0,"description":"Claims that settled in the trader’s favour, over all time"},"claimsRejected":{"type":"integer","minimum":0,"description":"Claims a verifier refused, over all time. A rejection forfeits the claimant’s bond"},"liveExposure":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"expiryBacklog":{"type":["string","null"],"pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"}},"required":["asOfSlot","asOfTs","address","admin","vault","settlementMint","paused","totalPremiums","totalLiabilities","totalPaid","totalContributed","totalWithdrawn","policiesIssued","tradesRegistered","claimsPaid","claimsRejected","liveExposure","expiryBacklog"],"additionalProperties":false}}}},"400":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The requested resource does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/trades":{"get":{"summary":"Insured trades, filterable and paginated","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Default 50, max 200"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0}},{"name":"owner","in":"query","required":false,"schema":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"Filter to one trader"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["registered","claimed","paid","rejected","expired"],"description":"Filter by lifecycle status"}},{"name":"tokenMint","in":"query","required":false,"schema":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"asOfSlot":{"type":"string","pattern":"^\\d+$","description":"Highest slot folded into this answer"},"asOfTs":{"type":"integer","description":"Server time when the answer was produced, in unix seconds"},"trades":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"pool":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"policy":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"owner":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"tokenMint":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"tier":{"type":"string","description":"The cover tier bought for this trade. Tier names and their windows are pool parameters, so they are read from the pool rather than fixed by this API"},"tradeSize":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"reservedLiability":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"windowStart":{"type":"integer","description":"Unix second the cover window opens — the trade’s registration time"},"windowEnd":{"type":"integer","description":"Unix second the cover window closes. Half-open: a collapse observed exactly at windowEnd is outside the cover, so \"10 minutes\" means 10 minutes"},"status":{"type":"string","enum":["registered","claimed","paid","rejected","expired"],"description":"Lifecycle. registered → claimed once a claim is filed, then paid or rejected; expired means the window closed with no claim and the liability was released"},"registeredSlot":{"type":"string","pattern":"^\\d+$","description":"Slot at registration. Orders trades that share a timestamp, which windowStart alone cannot"},"claim":{"type":["string","null"],"minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"payout":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"rejectionReason":{"type":["string","null"],"description":"Why a verifier refused the claim. Non-null only when status is rejected"}},"required":["address","pool","policy","owner","tokenMint","tier","tradeSize","reservedLiability","windowStart","windowEnd","status","registeredSlot","claim","payout","rejectionReason"],"additionalProperties":false},"description":"This page of trades, newest first. Page with limit and offset"},"total":{"type":"integer","minimum":0,"description":"Trades matching the filter across every page, not the length of this one"}},"required":["asOfSlot","asOfTs","trades","total"],"additionalProperties":false}}}},"400":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The requested resource does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/trades/{address}":{"get":{"summary":"One insured trade","parameters":[{"name":"address","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"asOfSlot":{"type":"string","pattern":"^\\d+$","description":"Highest slot folded into this answer"},"asOfTs":{"type":"integer","description":"Server time when the answer was produced, in unix seconds"},"trade":{"type":"object","properties":{"address":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"pool":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"policy":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"owner":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"tokenMint":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"tier":{"type":"string","description":"The cover tier bought for this trade. Tier names and their windows are pool parameters, so they are read from the pool rather than fixed by this API"},"tradeSize":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"reservedLiability":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"windowStart":{"type":"integer","description":"Unix second the cover window opens — the trade’s registration time"},"windowEnd":{"type":"integer","description":"Unix second the cover window closes. Half-open: a collapse observed exactly at windowEnd is outside the cover, so \"10 minutes\" means 10 minutes"},"status":{"type":"string","enum":["registered","claimed","paid","rejected","expired"],"description":"Lifecycle. registered → claimed once a claim is filed, then paid or rejected; expired means the window closed with no claim and the liability was released"},"registeredSlot":{"type":"string","pattern":"^\\d+$","description":"Slot at registration. Orders trades that share a timestamp, which windowStart alone cannot"},"claim":{"type":["string","null"],"minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"payout":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"rejectionReason":{"type":["string","null"],"description":"Why a verifier refused the claim. Non-null only when status is rejected"}},"required":["address","pool","policy","owner","tokenMint","tier","tradeSize","reservedLiability","windowStart","windowEnd","status","registeredSlot","claim","payout","rejectionReason"],"additionalProperties":false,"description":"The trade at the requested address"}},"required":["asOfSlot","asOfTs","trade"],"additionalProperties":false}}}},"400":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The requested resource does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/trades/{address}/proof":{"get":{"summary":"The claim-submission kit for a rugged trade","description":"The proof digest `submit_claim` takes, the CID the sealed evidence bundle is retrievable at, and the deadline the claim must be filed by. Relayed from the watcher that built the proof: 404 while no proof is held for the trade (not yet ruled, not a rug, or retention passed), 502 when the watcher cannot be reached, 503 on a deployment that has no watcher configured.","parameters":[{"name":"address","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"trade":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"tokenMint":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"digest":{"type":"string","pattern":"^[0-9a-f]{64}$","description":"Lowercase hex of the sha2-256 digest the on-chain claim commits to. Pass digestBytes, not this, to submit_claim"},"digestBytes":{"type":"array","items":{"type":"integer","minimum":0,"maximum":255},"description":"The same digest as the 32-byte array submit_claim takes"},"cid":{"type":["string","null"],"description":"Content address of the pinned evidence bundle, fetchable from any IPFS gateway. null for a proof built but not yet pinned — the digest is still valid"},"retainUntil":{"type":"integer","description":"Last unix second submit_claim will accept a claim on this trade. Past it the proof is released and the claim can no longer be filed"},"collapseBps":{"type":"integer","minimum":0,"description":"How far the price fell from entry, in basis points — 10000 is a fall to zero. 0 when the exit price met or beat entry, so there is nothing to claim"},"windowStart":{"type":"integer","description":"Unix second the covered window opened, copied from the trade"},"windowEnd":{"type":"integer","description":"Unix second the covered window closed, copied from the trade"},"builtAt":{"type":"integer","description":"Unix second the watcher sealed this bundle. Not a freshness signal for the projection — this route is relayed live and carries no asOfSlot"}},"required":["trade","tokenMint","digest","digestBytes","cid","retainUntil","collapseBps","windowStart","windowEnd","builtAt"],"additionalProperties":false}}}},"400":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The requested resource does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/policies":{"get":{"summary":"Issued policies","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Default 50, max 200"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0}},{"name":"owner","in":"query","required":false,"schema":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"asOfSlot":{"type":"string","pattern":"^\\d+$","description":"Highest slot folded into this answer"},"asOfTs":{"type":"integer","description":"Server time when the answer was produced, in unix seconds"},"policies":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"pool":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"owner":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"tradesRegistered":{"type":"integer","minimum":0,"description":"Credits spent from this policy’s pack. A policy covers a fixed number of trades; when they are used up the SDK buys another rather than topping this one up. Zero is a real and important answer: the pack was bought and nothing was ever registered against it, so the premium bought no cover"},"maxTrades":{"type":["integer","null"],"minimum":0,"description":"Trades in the pack, from PolicyCreated. Null means the projection never saw that event — a backfill that began after the policy was created knows the policy only through a trade. Null is “not known”, never “none”: a pack of zero cannot exist, so do not render it as one or compute credits remaining from it"},"tier":{"type":["string","null"],"description":"The coverage tier the pack was bought at, which fixes the payout ceiling. Null under exactly the condition maxTrades is null"},"premiumPaid":{"type":["string","null"],"pattern":"^\\d+$","description":"What the pack cost, in the pool’s settlement asset — not necessarily lamports; scale by the pool’s settlementMint. Null under exactly the condition maxTrades is null"}},"required":["address","pool","owner","tradesRegistered","maxTrades","tier","premiumPaid"],"additionalProperties":false},"description":"This page of policies. Page with limit and offset"},"total":{"type":"integer","minimum":0,"description":"Policies matching the filter across every page, not the length of this one"}},"required":["asOfSlot","asOfTs","policies","total"],"additionalProperties":false}}}},"400":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The requested resource does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/claims":{"get":{"summary":"Claims and their outcomes","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Default 50, max 200"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["claimed","paid","rejected"]}},{"name":"owner","in":"query","required":false,"schema":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"Filter to one trader"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"asOfSlot":{"type":"string","pattern":"^\\d+$","description":"Highest slot folded into this answer"},"asOfTs":{"type":"integer","description":"Server time when the answer was produced, in unix seconds"},"claims":{"type":"array","items":{"type":"object","properties":{"trade":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"claim":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"owner":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"status":{"type":"string","enum":["claimed","paid","rejected"],"description":"claimed while the claim is pending a verifier, then paid or rejected once one resolves it. These are the on-chain Pending, Verified and Rejected states"},"payout":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"rejectionReason":{"type":["string","null"],"description":"Why the claim was refused. Non-null only when status is rejected"},"claimedAt":{"type":["integer","null"],"description":"Unix seconds the claim was filed, from the settlement time of its ClaimSubmitted event. Null when the projection never saw one — never 0, which formats as 1970"},"settledAt":{"type":["integer","null"],"description":"Unix seconds the claim was paid or rejected. Null while it is still pending"}},"required":["trade","claim","owner","status","payout","rejectionReason","claimedAt","settledAt"],"additionalProperties":false},"description":"This page of claims. Page with limit and offset"},"total":{"type":"integer","minimum":0,"description":"Claims matching the filter across every page, not the length of this one"}},"required":["asOfSlot","asOfTs","claims","total"],"additionalProperties":false}}}},"400":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The requested resource does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/state":{"get":{"summary":"Operational detail: backlog, drift, and pause state","description":"Admin-only. Carries the expiry backlog breakdown from M2-10, which is the signal that the pool is losing capacity to un-cranked trades.","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"asOfSlot":{"type":"string","pattern":"^\\d+$","description":"Highest slot folded into this answer"},"asOfTs":{"type":"integer","description":"Server time when the answer was produced, in unix seconds"},"pools":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"A base58 Solana address"},"paused":{"type":"boolean","description":"Whether the pool is halted. While true the program refuses new policies and trade registrations; claims already filed still settle"},"totalLiabilities":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"liveExposure":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"expiryBacklog":{"type":["string","null"],"pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"expiryBacklogCount":{"type":["integer","null"],"minimum":0,"description":"How many trades make up expiryBacklog. null for the same reason it is — the grace period could not be read, so there is no honest count"},"oldestBacklogAgeSeconds":{"type":["integer","null"],"minimum":0,"description":"Age of the longest-waiting uncranked trade, in seconds. This is the number to alert on: the backlog’s size says how much is stuck, this says for how long"}},"required":["address","paused","totalLiabilities","liveExposure","expiryBacklog","expiryBacklogCount","oldestBacklogAgeSeconds"],"additionalProperties":false},"description":"Every pool this deployment indexes, with the operator-only figures attached"},"eventsApplied":{"type":"integer","minimum":0,"description":"Chain events folded into the projection since it was built"},"orphanedEvents":{"type":"integer","minimum":0,"description":"Trade mutations arriving for a trade the projection never registered. Non-zero means the backfill started too late and every count here undercounts. Zero is not a completeness guarantee: this counts that one class only, and pool, keeper-registry and attestor-registry rows are created as empty skeletons on first mention and counted as applied"}},"required":["asOfSlot","asOfTs","pools","eventsApplied","orphanedEvents"],"additionalProperties":false}}}},"400":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid admin bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The requested resource does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"adminToken":[]}]}},"/quote":{"post":{"summary":"Price a policy and pre-check whether the program would accept it","description":"Reads pool parameters from chain rather than from the indexed projection, because a quote is a number the caller is about to act on. Amounts are decimal strings: a u64 served as a JSON number loses precision past 2^53. The split prices an unrouted purchase: `partnerFee` is zero and the pool’s partner share is included in `toReserve`, matching what `create_policy` charges when no `Partner` account is named.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"pool":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"The pool to quote against"},"tokenMint":{"type":"string","minLength":32,"maxLength":44,"pattern":"^[1-9A-HJ-NP-Za-km-z]+$","description":"The token being traded. Priced against the pool’s entry gate, so two mints can quote differently at the same trade size"},"tradeSize":{"type":"string","pattern":"^\\d+$","description":"Notional per covered trade, in lamports as a decimal string. This is the size each trade in the pack may cover, not the total across the pack"},"tier":{"type":"integer","minimum":0,"maximum":2,"description":"0 Basic, 1 Standard, 2 DegenMax"},"packSize":{"type":"integer","description":"Trades in the pack. Must be one of 1, 10, 20, 50, 100"},"marketCapMicroUsd":{"type":"string","pattern":"^\\d+$","description":"Current market cap in micro-USD (1_000_000 = $1), as a decimal string — not lamports. Feeds the entry gate. Omit it and this deployment looks the figure up; if it cannot, the gate does not run and marketCap comes back null"}},"required":["pool","tokenMint","tradeSize","tier","packSize"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"eligible":{"type":"boolean","description":"Whether the program would accept this policy right now"},"issues":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","enum":["pool-paused","trade-too-small","trade-too-large","market-cap-too-high","exceeds-policy-covered-size","insufficient-pool-capacity","invalid-pack-size","unknown-pool"],"description":"Machine-readable reason. A closed set — switch on this rather than on detail, which is prose and may be reworded"},"detail":{"type":"string","description":"The same reason in words, safe to show a user. Do not parse it"}},"required":["code","detail"],"additionalProperties":false},"description":"Empty when eligible"},"quote":{"type":["object","null"],"properties":{"premium":{"type":"string","pattern":"^\\d+$","description":"Total the buyer pays"},"protocolFee":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"partnerFee":{"type":"string","pattern":"^\\d+$","description":"Zero unless the policy is created naming an active on-chain `Partner` account. This endpoint prices an unrouted purchase, which is what `create_policy` charges when no partner is passed — the pool's `partnerFeeBps` share goes to reserves."},"underwriterFee":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"toReserve":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"perTradeCap":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"payoutPerTrade":{"type":"string","pattern":"^\\d+$","description":"Paid if a covered trade rugs"},"liabilityPerTrade":{"type":"string","pattern":"^\\d+$","description":"Reserved against the pool per registered trade"}},"required":["premium","protocolFee","partnerFee","underwriterFee","toReserve","perTradeCap","payoutPerTrade","liabilityPerTrade"],"additionalProperties":false,"description":"The price, present whenever the pool is known — even when eligible is false, because a caller deserves the number they were refused at. null only for a pool this deployment cannot read"},"availableCapacity":{"type":["string","null"],"pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"marketCap":{"type":["object","null"],"properties":{"microUsd":{"type":"string","pattern":"^\\d+$","description":"An amount in lamports, as a decimal string — u64 does not survive JSON numbers"},"source":{"type":"string","description":"`caller` when supplied in the request, otherwise the provider that answered"}},"required":["microUsd","source"],"additionalProperties":false,"description":"The market cap the entry gate was checked against, and where it came from. null means the gate did not run at all — materially different from a figure that passed, and not something to round up to \"eligible\""},"asOfTs":{"type":"integer","description":"Server time this quote was produced, in unix seconds. Prices move; re-quote rather than caching this"}},"required":["eligible","issues","quote","availableCapacity","marketCap","asOfTs"],"additionalProperties":false}}}},"400":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The requested resource does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/whoami":{"get":{"summary":"Check an API key and see what it is attached to","description":"The first call to make with a new key. Send it as `Authorization: Bearer <keyId>.<secret>`, or in `X-API-Key`. Answers 401 for a key that is not valid and 403 for one belonging to a suspended or rejected partner — the distinction matters, because a new key fixes the first and not the second.","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"partner":{"type":"object","properties":{"id":{"type":"string","description":"Your partner id. Stable, and safe to log"},"label":{"type":"string","description":"The integration name you registered"},"status":{"type":"string","enum":["pending","approved","rejected","suspended"],"description":"pending, approved, rejected or suspended"},"onchainPartner":{"type":["string","null"],"description":"Your on-chain Partner account, or null if an operator has not registered one yet"},"earning":{"type":"boolean","description":"Whether revenue share is accruing. Approved alone is not enough — the on-chain account has to exist, because attribution is the policy.partner field"}},"required":["id","label","status","onchainPartner","earning"],"additionalProperties":false,"description":"The integration this key belongs to. Read earning, not status, to decide whether revenue share is actually accruing"},"keyId":{"type":"string","description":"The key that authenticated this request"},"rateLimit":{"type":"object","properties":{"remaining":{"type":"integer","description":"Requests left in the current window"},"resetsAt":{"type":"integer","description":"Unix seconds when the window rolls"}},"required":["remaining","resetsAt"],"additionalProperties":false,"description":"Budget for this key as of this response. A 429 carries the same figures in headers"}},"required":["partner","keyId","rateLimit"],"additionalProperties":false}}}},"400":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The requested resource does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/attest":{"post":{"summary":"Attest a swap and get back a signed register_trade transaction","description":"The attestor holds the pool’s trade_attestor key (ADR-0003) and builds the instruction itself. It does not co-sign a transaction you supply — that would be blind-signing with the key the protocol’s entry prices rest on. Send the swap; the price, the market cap, the token and the size are all measured here, and a size larger than the swap actually spent is refused. Authenticate with an API key, or with a session cookie from the dApp.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"owner":{"type":"string","description":"The wallet that signed the swap. It will own the cover and must sign the returned transaction — an attestation is issued for one wallet and is useless to any other"},"pool":{"type":"string","description":"The pool to register against"},"policyIndex":{"type":"string","pattern":"^\\d+$","description":"Which of this wallet’s policies in this pool to spend a credit from"},"tradeIndex":{"type":"string","pattern":"^\\d+$","description":"Position within that policy. Also a PDA seed, so it cannot be reused"},"tokenMint":{"type":"string","description":"The token bought"},"swapSignature":{"type":"string","description":"The swap being insured. Read from chain — its size, its token and its signer are measured, not taken from this request"},"tradeSize":{"type":"string","pattern":"^\\d+$","description":"Lamports to insure. Defaults to everything the swap spent, and may not exceed it"}},"required":["owner","pool","policyIndex","tradeIndex","tokenMint","swapSignature"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"const":"signed","type":"string","description":"Always \"signed\" on this shape. A refusal is also 200, with status \"refused\" — branch on this field, not on the HTTP status"},"transaction":{"type":"string","description":"The register_trade transaction, base64, signed by the attestor and missing only the owner’s signature. Deserialise, sign, send — do not rebuild it, because any change invalidates the attestor’s signature"},"attestor":{"type":"string","description":"The key that authored this attestation. On a legacy pool it equals the pool’s trade_attestor and has already co-signed the transaction; on a quorum pool it is the proposing attestor, whose signature rides in the ed25519 instruction instead"},"coSigners":{"type":"array","items":{"type":"string"},"description":"Quorum pools only: every attestor whose detached signature is packed into the transaction’s ed25519 verify instruction, proposer included, in the order packed"},"blockhash":{"type":"string","description":"The blockhash the transaction was built against. Sign and send promptly — it is what gives the attestation its lifetime"},"lastValidBlockHeight":{"type":"integer","description":"Past this block height the transaction is dead and a new attestation is needed"},"accounts":{"type":"object","properties":{"policy":{"type":"string","description":"The Policy the credit is spent from — existing or about to be created"},"trade":{"type":"string","description":"The InsuredTrade this will create"},"swapCover":{"type":"string","description":"The one-cover-per-swap marker (ADR-0006)"}},"required":["policy","trade","swapCover"],"additionalProperties":false,"description":"The addresses this transaction will touch, derived so a caller can watch for them without decoding the transaction"},"facts":{"type":"object","properties":{"tokenMint":{"type":"string","description":"The token bought, as read from the swap on chain rather than as the caller named it"},"tradeSize":{"type":"string","pattern":"^\\d+$","description":"Lamports insured"},"swapLamportsSpent":{"type":"string","pattern":"^\\d+$","description":"What the swap actually spent, as measured on chain"},"swapSlot":{"type":"string","pattern":"^\\d+$","description":"The slot the swap landed in, on the chain it was made on"},"swapBlockTime":{"type":"integer","description":"When the swap’s block was produced, unix seconds"},"swapAgeSeconds":{"type":"integer","description":"How old the swap was when it was attested. Bounded, because cover attaches at entry"},"entryPrice":{"type":"object","properties":{"price":{"type":"string","pattern":"^\\d+$","description":"Mantissa"},"expo":{"type":"integer","description":"Base-10 exponent, so the price is price × 10^expo"},"conf":{"type":"string","pattern":"^\\d+$","description":"The source’s confidence band, in the same units as the mantissa"},"publishTs":{"type":"integer","description":"When the source published the price, unix seconds"},"source":{"type":"string","description":"The PriceSourceKind variant recorded on chain"},"quoteMint":{"type":["string","null"],"description":"What the price is denominated in, or null for a source quoting USD"}},"required":["price","expo","conf","publishTs","source","quoteMint"],"additionalProperties":false,"description":"The entry the cover is measured from, as a mantissa and exponent rather than a float. Every later collapse is computed against this, so it is the single number a payout turns on"},"marketCapMicroUsd":{"type":"string","pattern":"^\\d+$","description":"Market cap at entry, checked against the tier’s limit"},"attestedSlot":{"type":"string","pattern":"^\\d+$","description":"The slot the attestor observed at. Bounds how stale this may get"},"liability":{"type":"string","pattern":"^\\d+$","description":"What the pool will reserve against this trade if it registers"}},"required":["tokenMint","tradeSize","swapLamportsSpent","swapSlot","swapBlockTime","swapAgeSeconds","entryPrice","marketCapMicroUsd","attestedSlot","liability"],"additionalProperties":false,"description":"Everything the attestor put its name to, echoed so a caller can see what they are about to countersign. Check these before signing — the signature covers them"},"swapChain":{"type":"string","description":"Which cluster the swap was read from — not necessarily the cover’s own"}},"required":["status","transaction","attestor","blockhash","lastValidBlockHeight","accounts","facts","swapChain"],"additionalProperties":false}}}},"400":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The requested resource does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/attest/cosign":{"post":{"summary":"Re-verify a proposed attestation and return a detached co-signature","description":"The attestor-to-attestor half of the quorum (ADR-0009). The proposing attestor sends the attestation it intends to sign; this deployment re-reads the swap from chain and compares the price and market cap against its own sources within its own tolerances, and only then returns an ed25519 signature over the canonical digest. Not a caller endpoint: traders and bots use POST /v1/attest, which aggregates these signatures.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"pool":{"type":"string","description":"The pool the registration is for"},"owner":{"type":"string","description":"The wallet that will own the cover. Bound into the signed digest, so a signature collected for one wallet cannot be replayed onto another’s registration"},"attestations":{"type":"array","items":{"type":"object","properties":{"tokenMint":{"type":"string","description":"The token bought"},"tradeSize":{"type":"string","pattern":"^\\d+$","description":"Lamports the proposal insures"},"entryPrice":{"type":"object","properties":{"price":{"type":"string","pattern":"^\\d+$","description":"Mantissa"},"expo":{"type":"integer","minimum":-2147483648,"maximum":2147483647,"description":"Base-10 exponent, so the price is price × 10^expo"},"conf":{"type":"string","pattern":"^\\d+$","description":"The proposer’s confidence band, same units as the mantissa"},"publishTs":{"type":"string","pattern":"^\\d+$","description":"When the proposer’s source published, unix seconds"},"source":{"type":"string","description":"The PriceSourceKind variant name, as the IDL spells it"},"quoteMint":{"type":["string","null"],"description":"What the price is denominated in, or null for a source quoting USD"}},"required":["price","expo","conf","publishTs","source","quoteMint"],"additionalProperties":false,"description":"The entry price the proposer read, as a mantissa and exponent rather than a float. This deployment re-reads it from its own sources and refuses if the two disagree beyond tolerance — it is a proposal, not an input that is trusted"},"marketCapMicroUsd":{"type":"string","pattern":"^\\d+$","description":"Market cap at entry, as the proposer read it"},"swapSignature":{"type":"string","description":"The swap this entry covers. Re-read from chain here — never taken on faith"},"attestedSlot":{"type":"string","pattern":"^\\d+$","description":"The slot the proposer observed at"}},"required":["tokenMint","tradeSize","entryPrice","marketCapMicroUsd","swapSignature","attestedSlot"],"additionalProperties":false},"description":"One entry for a single registration. A batch is one digest over every entry in order, so the whole batch is co-signed or none of it is"}},"required":["pool","owner","attestations"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"const":"signed","type":"string","description":"Always \"signed\" on this shape. A refusal is also 200, with status \"refused\" — branch on this field, not on the HTTP status"},"attestor":{"type":"string","description":"The key that signed. Counts only while it holds an active registry seat"},"signature":{"type":"string","description":"64 bytes of ed25519 over the canonical digest, base64"},"digest":{"type":"string","description":"The sha256 digest that was signed, hex. An aggregator whose own digest differs has a canonical-encoding disagreement to fix, not a signature to pack"}},"required":["status","attestor","signature","digest"],"additionalProperties":false}}}},"400":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The requested resource does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"adminToken":{"type":"http","scheme":"bearer"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string"},"detail":{"type":"string"}},"required":["error"],"additionalProperties":false}}}}