explorer.leash.market
and every counter in /v1/metrics/events comes from a
single source: the events table. Two writers populate it:
- The API. Every
prepare → submit → confirmhop appends a row with the rightkindand bumps itsphase. Anything you drive through the public HTTP surface lands here automatically. - The chain indexer. A dual-network worker that walks signatures
for every
(network, address)row on its watchlist and emitseventsfor the on-chain activity it sees, including activity the API never initiated (deposits to a treasury, an SDK-drivenExecute, a third-party top-up).
- API-driven kinds — call the matching
prepareendpoint, sign locally, thenPOST /v1/submit. The event is created inphase=preparedbefore the network call returns and gets stamped tosubmittedthenconfirmedautomatically. See Prepare → Submit lifecycle. - Indexer-only kinds — perform the on-chain action through any channel (SDK, wallet, faucet, another protocol), and make sure the agent + ATA are on the indexer watchlist (covered below).
Auto-registration: how the indexer learns about your agent
The indexer never crawls the cluster from scratch. It iterates a strict, per-network watchlist seeded by the API. Four watch kinds exist:asset, treasury, treasury_ata, and leash_fee_ata. Once
an agent is on the watchlist, every transaction touching its asset,
treasury PDA, or any of its ATAs is decoded and emitted as the right
event kind. leash_fee_ata rows are seeded automatically at indexer
startup for every supported stablecoin, so on-chain protocol fee
inflows surface even when no receipt is pushed (see
Protocol fee).
| Endpoint | Watchlist side-effect |
|---|---|
Any prepare* endpoint targeting /v1/agents/{mint}/... | Adds asset + treasury rows for the agent. |
POST /v1/agents/{mint}/treasury/provision/prepare | Adds the provisioned ATAs as treasury_ata. |
POST /v1/agents/{mint}/treasury/withdraw/prepare | Adds the source ATA as treasury_ata. |
POST /v1/agents/{mint}/treasury/withdraw-all/prepare | Adds the source ATA as treasury_ata. |
GET /v1/agents/{mint}/treasury/balances | Adds the PDA + every SPL ATA it surfaces. |
POST /v1/receipts/{agent} (first push for the agent) | Adds an asset row. |
POST /v1/agents/{mint}/pull-target (registers a feed URL) | Adds an asset row. |
treasury
row it has never paged, it issues one getTokenAccountsByOwner
per token program and registers each result as a treasury_ata.
The single most useful “make it light up” call is
GET /v1/agents/{mint}/treasury/balances. It is read-only, free,
and side-effects all three watch kinds at once. If you have an
agent that was funded out-of-band before any API call, hit
/treasury/balances once and the indexer will surface its history
on the next tick.
Per-event-kind matrix
| Event kind | Source | Endpoint / trigger that surfaces it |
|---|---|---|
agent.create | API only | POST /v1/agents/prepare (then /v1/submit). HTTP twin of the SDK’s prepareAgentMint. |
agent.identity.register | API + indexer | POST /v1/agents/{mint}/identity/prepare (then /v1/submit); or any mpl-agent-identity::CreateIdentity for a watched asset (e.g. an SDK-driven prepareAgentMint). |
agent.executive.register | API + indexer | POST /v1/agents/{mint}/executive/register/prepare; or mpl-agent-tools::CreateExecutive. |
agent.executive.delegate | API + indexer | POST /v1/agents/{mint}/executive/delegate/prepare; or mpl-agent-tools::Delegate. |
agent.delegation.set | API + indexer | POST /v1/agents/{mint}/delegation/prepare; or an Execute-wrapped SPL Approve for the asset’s signer PDA. |
agent.delegation.revoke | API + indexer | POST /v1/agents/{mint}/delegation/revoke/prepare; or an Execute-wrapped SPL Revoke. |
agent.token.set | API + indexer | POST /v1/agents/{mint}/token/set/prepare; or mpl-agent-identity::SetAgentToken. |
agent.treasury.provision | API + indexer | POST /v1/agents/{mint}/treasury/provision/prepare (skipped if every ATA already exists); or any CreateAssociatedTokenAccount for the PDA. |
agent.treasury.withdraw | API + indexer | POST /v1/agents/{mint}/treasury/withdraw[-all]/prepare; or any Execute-wrapped TransferChecked debiting the PDA’s ATA. |
agent.treasury.withdraw_sol | API + indexer | POST /v1/agents/{mint}/treasury/withdraw-sol[-all]/prepare; or any Execute reducing the PDA’s lamport balance. |
agent.treasury.fund | Indexer only | A plain SPL TransferChecked into a treasury ATA. Make sure the ATA is registered (see auto-registration table above). |
agent.treasury.fund_sol | Indexer only | A plain SystemProgram::Transfer ≥ 5 000 lamports into the treasury PDA. Sub-fee wobbles are filtered out as noise. |
submit.raw | API only | POST /v1/submit (any signed tx broadcast through the API). |
payment_link.created | API only | POST /v1/payment-links. |
payment_link.updated | API only | PATCH /v1/payment-links/{id}. |
payment_link.deleted | API only | DELETE /v1/payment-links/{id}. |
payment_link.served | API only | An anonymous client hits /x/{id} with no X-PAYMENT header (paywall serves a 402). |
payment_link.settled | API only | An anonymous client hits /x/{id} with a valid X-PAYMENT header and the facilitator settles the payment. |
buyer.payment.prepare | API only | POST /v1/buyer/payment/prepare. |
receipt.published | API only | POST /v1/receipts/{agent} (also auto-emitted by POST /v1/buyer/payment/execute and the paywall on settle). |
receipt.pulled | Indexer only | The pull worker fetches a receipt from a registered pull-target URL. |
protocol.fee.collected | API + indexer | Every settled earn receipt with price.fee (push, paywall, or pull); plus on-chain fallback when the indexer sees a positive token-balance delta to a Leash fee ATA. Idempotent on (network, receipt_hash) so the receipt-side and on-chain paths never double-count. See Protocol fee. |
POST /v1/agents/prepare — the HTTP twin of the
SDK’s prepareAgentMint. You can either mint via the SDK and start
hitting /v1/agents/{mint}/... from the second action onwards, or
do the entire lifecycle (mint → identity → executive → delegation →
treasury) over plain HTTPS.
Standard integration sequences
These are the four flows almost every integration ends up running. Each one lists the calls in order, what each call does, and — where relevant — the explicit follow-up call you should run so the indexer registers everything the explorer needs.Sequence 1 — Stand up a brand-new agent end-to-end
Run these in order. After this sequence, the agent + treasury PDA + every stable ATA are on the indexer watchlist, so every future on-chain event for this agent will surface on the explorer automatically.Sequence 2 — Issue a buyer payment against an x402 endpoint
/x/{id}), step 5 is
unnecessary — the paywall already auto-watches the payee on every
settle.
Sequence 3 — Onboard an agent that already exists on chain
You inherited an agent that the API has never written to (maybe it was minted with the SDK, or by a different team). One call is enough to make every past and future on-chain event surface.- Registers the asset as
asseton the watchlist. - Registers the treasury PDA as
treasury. - Registers every SPL ATA owned by the PDA as
treasury_ata.
Sequence 4 — Receive deposits to an agent treasury
Funding is indexer-only because deposits are unauthenticated SPL transfers anyone can send. To make sure they land in the explorer:Recipes
”Show every state-changing action my agent takes”
Use theprepare → submit flow for every action you initiate. The
event lifecycle (prepared → submitted → confirmed) renders directly
on the explorer’s per-agent timeline, plus the matching
/tx/<signature> page once the chain confirms.
”Make sure deposits to my agent’s treasury show up”
Funds are indexer-only because anyone can deposit and there is no authority to satisfy. The one-time setup:agent.treasury.fund,
and every SOL deposit ≥ 5 000 lamports will land as
agent.treasury.fund_sol.
To generate a real deposit on devnet for testing, see the
fund.ts reference script:
“Surface withdraws an SDK driver did out-of-band”
The chain indexer treats SDK-drivenExecute instructions identically
to API-driven ones. As long as the agent is on the watchlist (any
prior prepare* call or one /treasury/balances ping is enough),
agent.treasury.withdraw and agent.treasury.withdraw_sol will
surface within one indexer tick of confirmation.
”Get push notifications for explorer-relevant events”
Subscribe via Webhooks. You can subscribe to the empty event list (= every kind) or a curated subset:“Aggregate event volume for a dashboard”
GET /v1/metrics/events
returns event counts grouped by phase and kind for the active
network. Combined with GET /v1/metrics/usage
that gives you a full “API traffic in → on-chain volume out” picture.
Quick checklist
When something should be on the explorer but isn’t:- Is the indexer worker running?
GET /v1/indexer/status—cursors.last_run_atshould be < 30 seconds old. - Is the agent on the watchlist? Hit
GET /v1/agents/{mint}/treasury/balancesonce. Read-only, side-effects three watch kinds. - Is the action one the API initiates? Did you complete the full
prepare → submitround-trip (not justprepare)? Calls that stop atpreparedshow up withphase=preparedand never advance — that’s almost always a stuck client, not a stuck API. - Is the action a deposit? Confirm the treasury ATA is on the
watchlist (the
/treasury/balancescall above guarantees it), then wait one indexer tick. - Is your API key on the right network?
lsh_test_*keys see devnet only;lsh_live_*keys see mainnet only. Cross-network reads return404by design.

