API Reference — Orders
After you pay, one payment becomes one order per recipient. These endpoints track them.
GET /api/order— a single order, or all orders of one payment.GET /api/orders— a payer's history.POST /api/orders/status— bulk status by order id.GET /api/orders/load— dispense-queue position / ETA.GET /api/orders/stream— Server-Sent Events when your orders change.
Order status values: processing → completed (or failed); pending is reserved and not produced by the current flow. See Order lifecycle.
GET /api/order
Two modes — pass one of id or from_tx_hash.
By payment: ?from_tx_hash=<ref>
The most common tracking call — one payment can emit several orders (multisend). ref is:
| FROM chain | What to pass |
|---|---|
| EVM | The payment transaction hash (0x…). |
| Solana | The transaction signature (base58). |
| TON | The quoteId — your wallet does not learn the treasury-side transaction hash. Resolved once the payment is honoured (afterwards the order's from.txHash, 64 hex characters, works too). A refused payment never resolves here — find its refund in the payer's history, GET /api/orders?address=<UQ…>&view=batches. |
curl 'https://drip.tools/api/order?from_tx_hash=0x<yourFromTx>'{
"data": [
{
"status": "completed",
"id": "abc123…",
"from": {
"chainId": "8453", "displayName": "Base", "symbol": "ETH",
"amount": "0.00051755", "address": "0xpayer",
"tokenAddress": "0x0000000000000000000000000000000000000000",
"txHash": "0x<from tx>", "ts": 1784120925,
"explorer": "https://basescan.org", "family": null, "cluster": null
},
"to": {
"chainId": "421614", "displayName": "Arbitrum Sepolia", "symbol": "ETH",
"amount": "5.00000000", "address": "0xrecipient",
"tokenAddress": "0x0000000000000000000000000000000000000000",
"txHash": "0x<payout tx>", "ts": 1784120990,
"explorer": "https://sepolia.arbiscan.io", "family": null, "cluster": null
}
}
],
"refund": null
}datais one order object per recipient (empty array until the payment is indexed — keep polling).refundisnullunless some/all of the payment is being returned (a payment that could not become an order, or the failed slices of a partial batch) — see refund object.
Paginated recipients: &paginate=1
For large batches, ask for an aggregate summary plus a keyset page of recipients instead of the whole array:
curl 'https://drip.tools/api/order?from_tx_hash=0x<yourFromTx>&paginate=1&limit=100'| Parameter | Description |
|---|---|
limit | Page size, default 100, max 1000. limit=all returns the whole remainder in one response; limit=0 returns the summary only. |
cursor | next_cursor from the previous page. |
{
"summary": {
"recipients": 250,
"statusCounts": { "completed": 240, "processing": 10, "failed": 0 },
"fromTotal": "0.12500000",
"toTotal": "1250.00000000",
"completedTs": null,
"lastPayout": null
},
"data": [ /* order objects, in recipient order */ ],
"pagination": { "has_next_page": true, "next_cursor": "184233" },
"refund": null
}completedTs is filled once nothing is left processing; lastPayout ({ txHash, ts } of the last payout) additionally needs at least one completed order — an all-failed batch has completedTs but lastPayout: null. Until the payment is indexed, summary.recipients is 0 and fromTotal / toTotal are null.
By id: ?id=<order_id>
Returns a single order object, or null (with status 200) if the order isn't indexed yet — keep polling. Order ids are case-sensitive.
curl 'https://drip.tools/api/order?id=abc123…'Passing neither parameter → 400 Provide order id or from_tx_hash.
Order object
| Field | Meaning |
|---|---|
status | processing | completed | failed (pending is reserved, never produced today). |
id | The order id (use with /order?id= and /orders/status). |
from | The payment side: chainId, displayName, symbol, amount (human), address (payer), tokenAddress, txHash, ts, plus explorer / family / cluster for building links. |
to | The payout side: same fields for the recipient; txHash is the payout tx (null until dispensed), ts the completion time. |
Identity forms on the from side follow the chain: an EVM payer is 0x… and txHash is the transaction hash; a Solana payer is base58 and txHash the transaction signature (family: "solana", cluster: "mainnet-beta"); a TON payer is the UQ… address and txHash the treasury-side transaction hash (64 hex characters, family: "ton").
Refund object
| Field | Meaning |
|---|---|
status | refund_pending (recorded, not yet returned) | refunded (return tx sent). |
reason | unknown_quote | unattributed | ambiguous_memo | expired | bad_signature | failed_order — see Refund reasons. |
from | The returned amount + payer + original payment tx. |
refundTxHash | The return transaction, or null. |
refundedAt | Epoch seconds, or null. |
GET /api/orders
A payer's order history, keyset-paginated.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
address | string | yes | The payer identity, or up to 10 identities comma-separated (0x…, Solana base58, TON address). EVM addresses match case-insensitively; Solana and TON identities must be exact (UQ… form for TON). |
view | string | no | batches = one row per payment (multisend collapsed). Otherwise one row per order. |
limit | number | no | 1–100, default 20. |
cursor | string | no | next_cursor from a prior response — the next (older) page. |
since_cursor | string | no | Poll for rows newer than this cursor. |
view=batches also accepts filters (resend them with every page): status (processing|partial|completed|failed|refund), to_chain_id, and q (prefix search over order ids / FROM tx hashes, at least 2 characters).
Response
{
"data": [ /* order or batch rows */ ],
"pagination": {
"has_next_page": true,
"next_cursor": "eyJ0cyI6…",
"latest_item_cursor": "eyJ0cyI6…"
}
}A batch row (view=batches) aggregates a payment:
{
"status": "partial",
"statusCounts": { "completed": 3, "processing": 0, "failed": 1 },
"refund": { "status": "refund_pending", "reason": "failed_order" },
"recipients": 4,
"id": "firstOrderId",
"from": { "chainId": "8453", "displayName": "Base", "symbol": "ETH", "amount": "0.16", "address": "0x<payer>", "txHash": "0x…", "ts": 1784120925 },
"to": { "chainId": "421614", "displayName": "Arbitrum Sepolia", "symbol": "ETH", "amount": "16.0", "address": null, "txHash": null, "ts": null }
}Aggregate status is processing | completed | failed | partial (some paid, some failed). Amounts are decimal strings whose trailing zeros vary (a batch row's from.amount is padded to 18 places, other amounts to 8) — parse them as decimals, never compare them as strings.
Payments that could not become an order (unknown or missing quote, expired) appear in the unfiltered batches view as rows with status: "refund_pending" / "refunded", isRefund: true, reason, recipients: 0, id: null (no order id — look the payment up by its FROM tx hash) and to: null; status=refund lists only those.
Errors: 400 Incorrect address; 400 status must be one of: processing, partial, completed, failed, refund.
POST /api/orders/status
Lightweight bulk status for order ids you already hold.
curl -X POST 'https://drip.tools/api/orders/status' \
-H 'Content-Type: application/json' \
-d '{ "ids": ["orderId1", "orderId2"] }'{
"data": [
{ "status": "completed", "id": "orderId1", "to": { "txHash": "0x<payout>", "ts": 1784120990 } }
]
}Missing ids are simply omitted. Empty ids: [] → { "data": [] }. Non-array ids → 400 ids must be an array and is required. More than 500 ids → 400 too many ids (max 500).
GET /api/orders/load
Dispense-queue depth and drain rate, for estimating how long a payout will take.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
ref | string | no | An order_id or a FROM tx hash (or the TON treasury-side hash) — adds your position in line. |
Response
{
"updatedAt": 1784120100,
"ratePerSec": 25,
"minEtaSec": 10,
"chains": {
"421614": { "depth": 42, "rate": 28.5, "measured": true, "stalled": false, "minEtaSec": null }
},
"ref": {
"chainId": "421614",
"depth": 17,
"statusCounts": { "processing": 2, "completed": 15, "failed": 0 }
}
}chains[toChainId].depth— recipients still awaiting dispense on that TO chain;rate— measured drain rate (recipients/sec). Rough ETA ≈max(minEtaSec, depth / rate). An idle chain is simply absent fromchains.ref.depth— recipients ahead of (and including) your batch on its TO chain; only shrinks as the queue reaches you.refisnullif the ref isn't indexed yet — fall back to the chain-wide depth.
GET /api/orders/stream
A Server-Sent Events feed that fires whenever an order or refund belonging to the given payer identities changes. It is a notification only — re-fetch with GET /api/order or GET /api/orders when it fires.
curl -N 'https://drip.tools/api/orders/stream?address=0xpayer': connected
event: orders-changed
data: {"txs":["0x<from tx>"]}
: pingaddress— same asGET /api/orders: one or more identities, comma-separated.orders-changed—data.txslists the FROM tx hashes whose orders changed. Re-fetch those.- A
: pingcomment every 25 s keeps the connection alive. The stream is not rate-limited; a malformedaddressgets400 Incorrect address, and if it answers503, fall back to polling.