API reference

Status

The liveness endpoint. It answers four separate questions that are easy to confuse: is the protocol paused, is the indexer current, is the keeper working, and are the venues reachable.

protocol#

GET /status, protocol
"protocol": {
  "paused": false,
  "source": "chain:Config account snapshot",
  "asOf": "2026-09-23T01:37:13.399Z"
}

Config.paused, the protocol-wide switch. When it is true, every money instruction on every policy fails with ProtocolPaused. It says nothing about any individual policy's own pause.

indexer#

GET /status, indexer
"indexer": {
  "lastProcessedSignature": "277996pwt84nwwAvQ3jMGhA5A9WGaYQ13UTkMKSFBggZtUviv3NwrpqR5NMCfma1nwCFbeipvviuAqej6kKcGGJG",
  "lastProcessedSlot": "26609",
  "lastProcessedBlockTime": "2026-09-23T01:34:08.000Z",
  "cursorUpdatedAt": "2026-09-23T01:34:11.209Z"
}
FieldMeans
lastProcessedSignaturethe last transaction the event indexer consumed
lastProcessedSlotits slot, as a string because slots exceed 2^53
lastProcessedBlockTimethe chain's own timestamp for that transaction
cursorUpdatedAtwhen the indexer last wrote its cursor, which is its own wall clock

The two timestamps answer different questions

A large gap between lastProcessedBlockTime and now means the indexer is behind the chain. A large gap between cursorUpdatedAt and now means the indexer process itself has stopped. They can diverge: an indexer that is running happily on a quiet chain has a fresh cursor and an old block time.

keeper#

GET /status, keeper
"keeper": {
  "lastActionAt": "2026-09-23T00:08:44.000Z",
  "last24h": [
    { "kind": "PassiveDeployed", "count": 2, "lastAt": "2026-09-23T00:08:43.000Z" },
    { "kind": "Routed",          "count": 2, "lastAt": "2026-09-23T00:08:41.000Z" },
    { "kind": "BuybackBurned",   "count": 2, "lastAt": "2026-09-23T00:08:44.000Z" },
    { "kind": "ProfitSwept",     "count": 2, "lastAt": "2026-09-23T00:08:44.000Z" }
  ],
  "unroutedFeeBacklogPolicyCount": {
    "value": 2,
    "source": "chain:pda_balances (quote_escrow > 0)",
    "asOf": "2026-09-23T01:37:30.769Z"
  },
  "note": "backlog is 'policies whose quote escrow currently holds unrouted fees', read directly off chain; it is not the same as unclaimed LaunchLab creator fees, which this indexer does not read"
}

last24h groups permissionless money instructions by event kind. Because every one of them is permissionless, this is activity rather than the health of any particular keeper: if the usual one stops and somebody else calls the same instructions, this section looks identical.

The backlog is not unclaimed fees

unroutedFeeBacklogPolicyCount counts policies whose quote escrow currently holds a non-zero balance, read directly off chain. That is money already claimed and waiting to be routed or compounded. Fees still sitting unclaimed at LaunchLab are a different quantity, and this indexer does not read them. The response says so in its own note.

attestor#

Per-venue freshness of the indexer's own poller readings, with lastActionAt for the attestation path overall. On a deployment with no configured venue targets, both venues are absent with a reason.

GET /status, attestor
"attestor": {
  "lastActionAt": "2026-09-23T01:34:08.000Z",
  "perVenue": [
    { "venue": "hyperliquid", "value": null, "source": "venue poller", "asOf": null, "absent": true,
      "reason": "no reading yet: no INDEXER_TARGETS_PATH target configured for this venue, or not polled since indexer start" },
    { "venue": "derive",      "value": null, "source": "venue poller", "asOf": null, "absent": true,
      "reason": "no reading yet: no INDEXER_TARGETS_PATH target configured for this venue, or not polled since indexer start" }
  ],
  "moneyPathsFrozenNote": "a policy's money paths freeze when report_remote_equity trips its pause (a deviation beyond max_deviation_bps) or the protocol is paused; check the specific coin's `paused`/`protocolPaused` fields, this section is venue-level freshness only"
}

venues#

Two different checks under one key. The Solana programs are checked with getAccountInfo, which is a real liveness probe. The remote venues are reported by their pollers.

GET /status, venues
"venues": {
  "launchlab":     { "reachable": true, "programId": "LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj", "source": "chain:getAccountInfo" },
  "meteoraDammV2": { "reachable": true, "programId": "cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG", "source": "chain:getAccountInfo" },
  "hyperliquid":   { "value": null, "source": "venue poller", "absent": true, "reason": "not polled yet" },
  "derive":        { "value": null, "source": "venue poller", "absent": true, "reason": "not polled yet" }
}

Reachable means the program account exists

It does not mean a pool exists, that a claim would succeed, or that the venue is behaving. For LaunchLab in particular, the account being reachable is entirely compatible with the fee path having no input at all, which is currently the case. See what is not built yet.

What this endpoint does not answer#

QuestionAsk instead
Is this particular policy frozen?/coins/:mint, fields paused, protocolPaused and moneyPathsFrozen
Is a specific attestation stale?/coins/:mint/positions, fields ageSeconds and stale
How much is unclaimed at LaunchLab?Nothing here answers it. The indexer does not read the creator fee vault.
Is the trading agent alive?Nothing here answers it. There is no agent heartbeat.

The plain health check#

For a process-liveness probe that does no database work and cannot be affected by chain state, use the root route instead.

GET /health
{ "ok": true, "service": "bankroll indexer API" }