AdManager on the ad chain,
paid out to bridgers who pay the order token on the other chain. These endpoints let anyone browse
ads and let a maker prepare every change to their own. See Liquidity ads
for the concept.
Endpoints
Who may act on an ad
Every write on an ad passes the same check:- You must have a signed-in wallet of the ad chain’s kind (
EVMorSTELLAR). Otherwise the relayer answers403withNo STELLAR wallet linked — connect and link the matching wallet to continue(orEVM). - If the ad’s creator is that wallet, you may act (
creatorKind: "WALLET"). - Otherwise the creator must be an account you linked (a Stellar agent account, or an EVM custody account, see Accounts), and the chain must say right now that its owner is your signed-in wallet. The owner is read from the chain on every write.
GET /v1/ads/mine uses a lighter rule: your stored wallets and links, with no chain read.
Agent-account makers
A maker can run an ad through an agent account instead of a wallet (see Agents). ThencreatorKind is STELLAR_AGENT_ACCOUNT or EVM_CUSTODY_ACCOUNT, creator is the account and
authorizer is its owner, your signed-in wallet. The wallet calls the account’s own functions rather
than the AdManager directly: create_ad, fund_ad and call on Stellar, createAdFunded,
fundAdFunded and execute on EVM. Calls that move money pay from the wallet into the escrow in the
same transaction. The account never holds funds.
Ad statuses
The read endpoints show
EXHAUSTED for any ad that is not INACTIVE or CLOSED and has
availableAmount 0. The status filter, and the trade path, use the stored status.
A fund, withdraw or close on an ACTIVE ad pauses it at once, so no trade starts against a pool
that is about to change. When the change confirms, the ad goes back to its earlier status (or to
EXHAUSTED after a withdraw that leaves nothing free, or CLOSED after a close). If the change never
lands, the relayer restores the earlier status. A create that never lands ends as CLOSED.
What the relayer returns, and what the wallet sends
The relayer signs nothing for an ad. Each write returns the contract call’s arguments, shaped for the ad chain. Your wallet builds and sends the transaction, and the relayer checks the chain afterwards.adId is a UUID the relayer picks at create. It is both the relayer’s id and the on-chain ad id.
The settlement signer
Every ad names a settlement signer: the account whose BLS key co-signs settlements for the maker (see Settlement keys). It defaults to the creator. An agent account holds no key, so an ad created from one must name a signer. The signer must hold a usable key on the ad chain’s registry when you create the ad, or theAdManager refuses it on chain. To re-point it
later, the maker calls setSettlementSigner(adId, identity) (set_settlement_signer on Stellar)
directly; there is no relayer route for it.
Browse ads
GET /v1/ads lists ads, ordered by id. Pass nextCursor as cursor for the next page.
routeStatusdefaults toACTIVEwhen you browse, so ads on retired routes are hidden. With a creator filter it defaults toALL, so a maker still sees an ad on a retired route to withdraw from and close it.- With no
status, inactive and closed ads are listed too. A marketplace should passstatus=ACTIVEand hide rows whoseavailableAmountis0.
My ads
GET /v1/ads/mine takes the same query and returns the same shape, limited to ads whose creator is
one of your wallets or linked accounts. Any creator filter you send is replaced. This is where you see
your own INACTIVE and CLOSED ads, and ads on retired routes (send routeStatus=ACTIVE to hide
them). With no wallet and no link you get { "data": [], "nextCursor": null }.
One ad
GET /v1/ads/:id returns one ad in the same shape as a list row. An ad on a retired route still
answers, with routeStatus: "RETIRED": its maker can fund, withdraw and close it, but it takes no new
trades.
Create an ad
POST /v1/ads/create checks the route, the amounts, the creator and the settlement signer, writes the
ad as INACTIVE and returns the createAd call.
creatorDstAddress is a payout destination, not an identity, so it need not be a wallet you linked.
Send minAmount and maxAmount together or not at all: minAmount alone is refused.
Fund and withdraw
POST /v1/ads/:id/fund and POST /v1/ads/:id/withdraw read the ad on chain, record the pending
change, pause an ACTIVE ad and return the call.
{ "pendingConfirmation": true, "chainKind": "EVM" }: call
confirm, then send the request again.
Fetch the pending call again
POST /v1/ads/:id/action-data re-serves the arguments of a pending create or fund, for a
wallet popup that was closed. It takes no body and makes no new pending record. A pending withdraw or
close is not re-served: wait for it to clear and start again.
Confirm a landed transaction
POST /v1/ads/:id/confirm checks the chain against the pending change. The relayer does not look up
your transaction: txHash is optional and not read, so {} is a valid body.
AdManager’s own event for this ad and amount appears after the quote;
on EVM it must be EVM_CONFIRMATIONS blocks deep. A close has landed when the ad is no longer open. On
confirm the relayer copies the chain’s balance into poolAmount and applies the recorded status.
If a create landed after the relayer gave up on it and closed the row, confirm still finds it on chain
and restores the ad to ACTIVE.
A change that is never confirmed is not lost. The relayer checks stale pending changes on chain and
applies or clears them itself.
Update limits, metadata or pause
PATCH /v1/ads/:id/update changes relayer-side settings. There is no transaction and the change takes
effect at once. The on-chain ad has no minimum, maximum or pause; these settings shape what the relayer
offers bridgers.
status change is refused on an INACTIVE or
CLOSED ad. On an EXHAUSTED ad the relayer reads the chain and allows it only when funds are free
there. The relayer does not check minAmount ≤ maxAmount here, so check it in your form, and offer
pause and resume only on ACTIVE or PAUSED ads with no pending change.
Close an ad
POST /v1/ads/:id/close returns the closeAd call, which pays the whole on-chain balance to to.
Close is refused while any trade holds liquidity on the ad, locked on chain or reserved.
{ "pendingConfirmation": true, … } when an earlier change
landed unconfirmed, and it shares their pending-change and chain refusals.
Liquidity held by trades
When a bridger creates a trade, the relayer holds that amount of the ad.availableAmount drops by it, a withdraw cannot take it, and close is refused. The hold ends when
the trade settles or is cancelled, or when an unfunded reservation expires after 30 minutes. Because
the relayer copies the chain’s balance after each settlement, poolAmount can change without any
action from you: a completed trade lowers it.