API reference, contract version 1.0.0
Indexer API
The OP_DROP indexer exposes a small read-only HTTP contract over the confirmed ledger. This page documents that contract: paths, parameters, and response shapes.
No base URL is published here. The origin is supplied by whoever operates a deployment, and it is not a browser-facing endpoint. Browsers must never call an OP_DROP indexer directly; a first-party application backend holds the origin and proxies what it needs. The indexer's database is private service state and must not be read or written by other services.
Service boundary
The indexer owns confirmed-chain scanning, leaf validation, token state, balances, events, and launch metadata. It is the source of truth for OP_DROP protocol state within a deployment. It does not create transactions, hold keys, or broadcast: those belong to the application that builds an order.
- Read endpoints under
/op-drop/*require no authentication. - There is one operator-only scan trigger. It is disabled unless an operator token is configured, it must stay behind operator access control, and it is not part of the surface an application should use. It is intentionally not documented here.
- There is no global path prefix. The routes below are exact.
Conventions
| Amounts | Always decimal strings, never JSON numbers, so full 64-bit and 128-bit precision survives. Never parse them as floating point. |
|---|---|
| Network | One of mainnet, testnet, signet, regtest, fixed per deployment. A response never mixes networks. |
| Confirmed only | Every figure reflects confirmed state at the deployment's confirmation depth. Mempool activity is never included. |
| Paging | start is an offset from 0 to 10000000, default 0. limit is 1 to 100, default 20. |
| Filters | Exact match after trimming and case normalisation, not patterns. A malformed value matches nothing rather than erroring. |
Endpoints
| Method and path | Purpose | Parameters |
|---|---|---|
GET /op-drop/health |
Liveness. Returns {"status":"ok","protocol":"op-drop"} while the process is up. |
none |
GET /op-drop/launch/drop |
The launch manifest for $DROP: the fixed anchor convention and the exact deploy payload. |
none |
GET /op-drop/tokens |
Paginated list of deployments. | tick, sort (deploy, holders, minted; default deploy), start, limit |
GET /op-drop/tokens/{tick} |
One deployment plus up to its 100 most recent events. 404 when the ticker has no valid deployment. | tick in the path, case-insensitive and trimmed |
GET /op-drop/balances/{address} |
Confirmed balances for one address. Only tickers with a non-zero available or reserved balance are included. | address in the path |
GET /op-drop/events |
Paginated event ledger, including invalid events. | tick, address, txid, start, limit |
GET /op-drop/indexer/status |
Scanner cursor and configuration: network, start height, confirmation depth, poll interval, batch size, and last processed block. | none |
Response schemas
Launch manifest
GET /op-drop/launch/drop
{
"protocol": "op-drop",
"anchor": {
"revealOutput": 0,
"transferSettlement": "first confirmed spend credits spender transaction output 0"
},
"launch": {
"display": "$DROP",
"tick": "drop",
"max": "21000000",
"lim": "1000",
"fullLimitMintCount": "21000",
"deploymentJson": "{\"p\":\"op-drop\",\"op\":\"deploy\",\"tick\":\"drop\",\"max\":\"21000000\",\"lim\":\"1000\"}"
}
}
Compare deploymentJson byte for byte against what you intend to sign. It is the only field on this endpoint that is worth verifying.
Deployment
| Field | Type | Meaning |
|---|---|---|
tick | string | The wire ticker. |
max | decimal string | Maximum supply. |
lim | decimal string | Limit per mint. |
fullLimitMints | decimal string | Number of full-limit mints that would exhaust the supply. |
finalLimitMintRemainder | decimal string | What a final partial mint would receive. |
minted | decimal string | Minted supply so far. |
remaining | decimal string | max - minted. |
progress | number | Percent minted, 0 to 100, rounded to two decimals. The only floating-point field in the contract, and it is presentational. |
mints | integer | Count of valid mint events. |
holders | integer | Addresses with a non-zero available or reserved balance. |
deploy | object | eventId, txid, height, owner of the winning deploy. |
Balances
GET /op-drop/balances/{address}
{
"address": "bc1p…",
"balances": [
{ "tick": "drop", "available": "750", "transferable": "250", "total": "1000" }
]
}
transferable is the field name for what the interfaces call "reserved": units held by an unsettled transfer. total is always available + transferable, so never add them yourself on top of total.
Event
| Field | Type | Meaning |
|---|---|---|
eventId | string | {txid}:i{vin}:w1 or {txid}:s{vin}:{transferEventId}. |
height, blockHash, txid | integer, string, string | Where the event was found. |
action | enum | deploy, mint, transfer, transfer_settle. |
tick | string or null | The ticker. |
requestedAmount | decimal string or null | What the payload asked for. Null on a deploy. |
amount | decimal string or null | What was actually credited. Null until the event is finalized. Differs from requestedAmount on a partial final mint. |
owner | string or null | The anchor address, or on a settlement the destination address. |
anchor | string or null | {txid}:{vout}. |
sourceTransferEventId | string or null | On a settlement, the transfer it settles. |
status | enum | valid, invalid, transfer_pending, settled. |
reason | enum or null | One of the codes in section 11. |
Comparing requestedAmount with amount is the cleanest way to spot a partial final mint: they differ only in that case.
Indexer status
{
"enabled": true,
"configured": true,
"network": "mainnet",
"startHeight": 0,
"confirmations": 6,
"pollSeconds": 15,
"batchBlocks": 10,
"nextHeight": 0,
"lastProcessedHeight": 0,
"lastBlockHash": null
}
The values shown are the shape, not any deployment's real cursor. Use lastProcessedHeight against your own node's tip to judge freshness, and confirmations to know how deep an event must be before it counts.
Errors
Errors use the framework's default HTTP exception body.
{ "statusCode": 404, "error": "Not Found", "message": "OP-DROP token drop not found" }
| Code | When |
|---|---|
| 200 | Success, including an empty list. |
| 400 | Invalid parameters, for example start outside 0 to 10000000 or limit outside 1 to 100, or a missing address. |
| 404 | No deployment for the requested ticker. |
message is usually a single string and may be an array when several validation errors are aggregated. Treat it as human-readable text, not as a stable machine code. The stable machine codes are the reason values on events.
Related: the raw artifact surface
The same service also indexes every confirmed reveal as a raw artifact, whether or not it carries an OP_DROP token operation. That surface belongs to the DROPS protocol rather than to OP_DROP, and it is documented separately. Two facts are worth knowing here because they cross the boundary:
- An artifact identity is
drops:<network>:<txid>:d<vinIndex>, which is the same reveal input that produces an OP_DROP event id of{txid}:i{vinIndex}:w1. - An artifact body is at most 256 bytes, matching the carrier's push limit, and is served with its committed MIME type and a content-addressed cache identity.
Do not read artifact presence as an OP_DROP token event. A reveal can be indexed as an artifact and still be an invalid OP_DROP event, or carry no token payload at all.
Consuming safely
- Keep the origin server-side. Proxy through your own backend. Do not put an indexer origin in a browser bundle.
- Treat amounts as strings. Every balance, supply, and amount field is a decimal string precisely so it does not lose precision. Parsing to a JavaScript number silently corrupts values above 253.
- Check freshness before you trust totals. Compare
lastProcessedHeightwith your node's tip. - An empty result is not proof. It can mean the scanner is behind, or that a valid deploy has not reached the confirmation depth yet.
- Handle reversal. A recent event can disappear after a reorg. Do not treat a shallow event as final.
- Do not mix protocols. An Ordinals or BRC-20 balance is not an OP_DROP balance, and an artifact is not a token event.
- Verify what matters yourself. For anything valuable, re-derive the result from block data using the specification instead of trusting a single indexer.