Endpoints
The flow
If the order deadline passes before settlement, the trade ends through cancels.
Who counts as a party
Every per-trade route admits only the trade’s parties: the bridger (by signed-in wallet), the maker (the ad’s creator, directly or through a linked account the chain says you own, or the order’s settlement signer), or an agent whose installed policy names the ad for that settlement signer. Anyone else gets403. Each route then adds its own role: only the maker locks, and each party writes
only its own half.
Trade statuses
A trade never moves backwards: once
CLAIMED or DISPUTED, it only goes on to COMPLETED or
CANCELLED. A deposit that lands late on an expired or replaced reservation brings it back to
ACTIVE.
List trades
GET /v1/trades/all lists only your own trades: where you are the bridger, the ad creator or the
settlement signer, or where an agent binding of yours names the ad. Filters narrow that set; a filter
naming someone else’s address returns nothing of theirs.
limit. Keep following nextCursor until it is null.
Trades your agent may act on
GET /v1/trades/assigned is the discovery route for an agent runtime. It takes your signed-in wallets
as agent keys, finds the ads their installed policies name, and returns the trades on those ads that
the live policy lets the agent open. It takes the same query as /all but ignores the address
filters, and returns the same shape. With no installed policy you get
{ "data": [], "nextCursor": null }. A maker trading from their own wallet uses /all instead. See
Agents.
One trade
GET /v1/trades/:id returns the trade in the list shape, plus:
pendingLockAttempt:truewhen a lock was quoted or a reservation hold exists.- Your own receive address, and only yours:
bridgerDstAddressif you are the bridger (where you are paid on the ad chain),adCreatorDstAddressif you are the maker (where you are paid on the order chain). This is the only route that adds them.
The timeline
Every trade carriestimeline: what has happened so far, oldest first. A step the chain recorded has
source: "chain", the block’s close time, the chain and the transaction. A step only the relayer saw
has source: "relayer" and the relayer’s clock. A step that has not happened is absent, and each step
appears at most once. Ties are shown in table order.
Create a trade
POST /v1/trades/create reserves the slice of the ad you want to take and returns the exact order to
send to the OrderPortal.
routeId is accepted but not read; the route always comes from the ad.
bridgerDstAddress need not be a wallet you linked, but it is your identity for disputes on the ad
chain: if you cannot sign from it, you cannot file or answer a dispute on this order (the cancel path
still refunds you). It may not be the zero address, the ad’s creator or its settlement signer.
The relayer checks the ad is ACTIVE with no pending change, scales your amount to ad-token decimals
and checks the ad’s limits, checks that the maker’s settlement signer has a usable key on both chains,
and checks free liquidity on chain. It then sets the deadline (see
Deadlines and clocks) and writes the trade as INACTIVE with a
hold on the ad.
OrderPortal.createOrder(orderParams) on chainId (approve amount first for
an ERC-20; send exactly amount as value for a native token), and you call
confirm.
The order
Every order is the same 19 fields, in this order, matching the on-chain order hash:orderChainToken,
adChainToken, amount, bridger, orderChainId, orderPortal, orderRecipient, adChainId,
adManager, adId, adCreator, adRecipient, salt, orderDecimals, adDecimals, deadline,
adSettlementSigner, lpFeeBps, protocolFeeBps.
- Addresses are
bytes32hex; an EVM address is left-padded with zeros. saltis the trade id read as a 128-bit number, in decimal.deadlineis unix seconds; after it the co-signed payout is refused.adSettlementSigneris read from the ad on chain when the trade is created.lpFeeBpsandprotocolFeeBpsare the ad chain’s fee split at reservation (see Fees); every order built for the trade carries the same values.
OrderPortal drops orderChainId and orderPortal (its own), and
the AdManager drops adChainId and adManager and calls orderPortal srcOrderPortal. The relayer
already sends the right set for each call. See Order hashing.
Reservations
- A reservation holds the liquidity for 30 minutes. If the deposit has not landed by then, the
trade becomes
CANCELLEDwithcancelReason: "expired-reservation"and the hold is released. A deposit that lands later still revives it on confirm. - One pending reservation per bridger per ad: creating again replaces the earlier one.
- You can hold only so many open reservations (reserved, deposit not landed, not expired) at once. A reservation being replaced does not count.
The
429 body carries limit. The route also has a strict per-IP rate limit.
Fetch the order again
POST /v1/trades/:id/create-data rebuilds the same createOrder call for a trade still INACTIVE,
for when the wallet call failed or the page reloaded. It takes no body, makes no new reservation and
does not extend the 30 minutes. The response is the same as create, byte for byte.
Deposit status
GET /v1/trades/:id/deposits reports how far each leg has got in the relayer’s record of the chain.
Poll it after the lock is confirmed. When settlementGrade is true, /params serves the roots and
co-signing can start.
Order parameters for signing
GET /v1/trades/:id/params returns the order plus everything both parties sign in the
co-signature.
- Roots. The first call after both legs are settlement grade pins the two roots and widths on
the trade, so both parties sign the same pair even if more deposits land in between. A reorg that
replaces a pinned leaf unpins them and clears stored co-signatures; read
/paramsagain and re-sign. - Maker key.
makerKeyCommitmentnames the maker key. Once the order is locked it is the key inpinnedSlot, and it is absent if that slot holds no qualifying key. - Nullifiers.
adLegNullifierandorderLegNullifierappear once both legs have a half on record. Until then a co-signature is refused with409 nullifiers-not-ready. secretCustody. Per leg:parties(the owning party sent its half),relayer(the half was made under the earlier scheme, for older trades) orpending(none yet). The ad leg is the bridger’s half, the order leg the maker’s. See Trade secrets.
/deposits.
Get the maker’s lock
POST /v1/trades/:id/lock returns the lockForOrder call for the ad chain. Only the maker, or an
agent the maker’s policy names, may call it. The body carries the maker’s half for the order leg.
ACTIVE and not locked, and that the hold
matches the order amount. It then picks the slot the lock pins: the settlement signer’s ad-chain slot
holding the newest key live on both trade chains that outlives the order. Then it stores the half,
records the pending lock and returns the call.
lockForOrder(orderParams, signerSlot) from the maker’s wallet or account, then call
confirm. The AdManager refuses the lock once the deadline is too close
(Escrow__DeadlineTooSoon).
If a lock you were given is already on chain but not confirmed, the answer is
{ "pendingConfirmation": true, "chainKind": "STELLAR" }: call confirm, do not lock again. A quoted
lock is held for 10 minutes; after that a new request quotes a fresh one.
Send your half of the trade secret
POST /v1/trades/:id/half stores one party’s half of the per-trade secret. Each payout proves
knowledge of one half, and both parties co-sign each leg’s nullifier (the hash of that half). Each
party derives its own half from a root only it holds, so the same half comes back on every device.
See Trade secrets.
The bridger sends the ad leg’s half after create and before the deposit. The maker normally sends the
order leg’s half with the lock, and may use this route instead.
GET /v1/keys/half-encryption, after checking
that key’s signature against the key pinned in your build. Use sealHalf from
@proofbridge/bls-encodings. The envelope binds the half to this order and leg, so it cannot be
replayed elsewhere. Before sending, open your own envelope locally and check that the half hashes to
the nullifier you send.
created: false, even with a
fresh ciphertext after a key rotation. A trade whose half never arrives cannot be co-signed and ends
through the cancel path (cancelReason: "half-missing").
Confirm a deposit or lock
POST /v1/trades/:id/confirm reports that the trade’s pending createOrder or lockForOrder landed.
The chain decides, not the body: the relayer reads the escrow’s order status at the chain head.
- On chain: the trade moves to
ACTIVE(deposit) orLOCKED(lock) and you get200. The maker is notified of a deposit and the bridger of a lock. - Not on chain yet,
txHashlooks like a hash, still inside the window:202with{ "success": false, "pending": true, "retry": true }. Call again. The window is 10 minutes from the lock quote, or 30 minutes from create for a deposit. - Not on chain and the window has passed, or no hash-shaped
txHash:400. A deposit that lands later still confirms, and the relayer also confirms late deposits on its own.