- Pushed from your code — the SDK’s
createBuyer/createSellerforwards everyReceiptV1to the API by default (see Receipts by default). - Forwarded from a Leash runner — when
LEASH_API_URLandLEASH_API_KEYare set on the runner, every accepted receipt is mirrored to the API in the background. - Pulled from a URL you register — for agents that publish their
receipt feed on their own domain,
POST /v1/agents/{mint}/pull-targetregisters it and the indexer polls it on a fixed cadence.
Push: POST /v1/receipts/{agent}
Idempotent on (network, receipt_hash). Re-posting the same receipt
returns 200 { ok, duplicate: true, event_id: null }.
- Validated against
ReceiptV1Schema. (receipt.agent, derived treasury PDA)is added to the indexer watchlist so future on-chain activity for this agent is captured automatically.- A
receipt.publishedevent is written to theeventstable and is visible in the explorer’s per-agent timeline.
receipt.agent must equal the URL path {agent} or the API rejects
with 422 receipt_agent_mismatch. This is the same guarantee the
runner enforces.
Read: GET /v1/receipts/{agent}
Cursor-based, nonce DESC by default, limit defaults to 50, max 200.
?kind=spend|earn,
?decision=allow|deny|rejected, ?since=<ISO8601>,
?cursor=<opaque>. The receipt chain is fully verifiable; load all
items and call verifyReceiptChain from @leash/core to confirm
linkage.
Read: GET /v1/receipts/by-hash/{hash}
Direct lookup. Returns the single receipt and a network field so
the explorer can deep-link.
lsh_test_* key looking up a mainnet hash returns 404 even though
the row exists — same network-isolation rule as the rest of the API.
Pull targets: POST /v1/agents/{mint}/pull-target
Some agents host their own receipts feed (e.g. self-hosted runner,
GitHub Pages, S3). Register the URL and the indexer’s pull worker
fetches it on every tick, idempotently ingests anything new, and
writes a receipt.pulled event per accepted item.
| Field | Type | Default | Notes |
|---|---|---|---|
url | string (https only) | required | Public, no auth. The pull worker does plain GET. |
format | "json" | "jsonl" | jsonl | json expects an array of ReceiptV1 objects. |
interval_seconds | number | 60 | Worker honours per-target cadence with a global floor. |
GET, DELETE). One target per (network, agent, url).
Receipts by default in the SDK
createBuyer / createSeller will publish receipts to the API
without any extra wiring as long as both env vars are set:
receipts: { apiUrl, apiKey, runnerUrl } config.
Set onReceipt: false to disable forwarding entirely. The full
defaulting tree lives in Receipts by default.
The agent’s on-chain identity registration also advertises a
services.receipts URL by default (https://api.leash.market/v1/receipts/{agent}),
so any explorer or third-party client can find your feed without
out-of-band coordination. Override with receiptsUrl on
createAgent, the LEASH_RECEIPTS_URL env, or
LEASH_NO_RECEIPTS_URL=1 to opt out entirely.
