Routing Strategies

Capacity marketplace

A secondary market where recipients trade spare capacity — listings, an order book, trades, price suggestions, and per-pool market config.

The capacity marketplace lets Workers (modelled today as recipients) buy and sell spare capacity units among themselves. A worker with idle headroom lists it; a worker that is saturated buys units to raise its own cap. It is the over-subscription economics plane of Ductor — a market for capacity itself, settled per pool under a configurable fee and price band. It is a second clearing surface alongside the work markets: the same settlement discipline, applied to the right to handle more work rather than to a unit of work.

Not the same thing as Markets

Markets is auction routing — recipients compete for a unit of work being routed right now (claim races, ping/post, first- and second-price auctions). The unit traded there is an opportunity.

The capacity marketplace trades capacity — the right to handle more work later. No routing decision is being made; recipients are rebalancing their caps by transacting units between each other. Different plane, different unit, don't conflate them.

Seller lists spare units Order book per pool Buyer purchases units Trade: units + fee settled Buyer cap up, seller cap down

Opt-in per pool

The marketplace is disabled by defaultMarketConfig.Enabled starts false for every pool. A pool does not participate until you enable it and set its price band via market config. The service ships as a contrib module (modules/services/capacity) wired to the marketplace store.

Operations

Ten endpoints under /api/capacity, in four groups.

GroupOperations
ListingsGET · POST /listings · GET · PATCH · DELETE /listings/{listing_id}
Order book & pricingGET /pools/{pool_id}/orderbook · GET /pools/{pool_id}/suggest-price
TradesGET /trades
Market configGET · PUT /pools/{pool_id}/config

Listings

A listing is spare capacity offered for sale: a seller posts units at a price_per_unit, optionally bounded by min_purchase/max_purchase, routable_types, and an expiry. Listings move through active → filled | expired | cancelled.

OperationMethod & path
List active listingsGET /api/capacity/listings
Create listingPOST /api/capacity/listings
Get one listingGET /api/capacity/listings/{listing_id}
Update listingPATCH /api/capacity/listings/{listing_id}
Cancel listingDELETE /api/capacity/listings/{listing_id}

GET /api/capacity/listings filters by pool_id and paginates (pagination.page_size up to 1000, pagination.page_token), newest first. PATCH is a partial update — units, price, purchase bounds, expiry, and metadata — and DELETE cancels a listing without touching already-executed trades.

curl -X POST -H "X-Tenant-ID: $DUCTOR_TENANT_ID" \
  -H "Content-Type: application/json" \
  -d '{"pool_id":"'$POOL_ID'","seller_id":"'$SELLER_ID'","units":50,"price_per_unit":2.5,"currency":"USD","min_purchase":5}' \
  http://localhost:8080/api/capacity/listings

Order book & pricing

GET /api/capacity/pools/{pool_id}/orderbook returns the pool's live market state — every active listing, total_units_available, and the average, minimum, and maximum price per unit, plus recent trades.

GET /api/capacity/pools/{pool_id}/suggest-price returns platform pricing guidance for a pool: a suggested bid and ask, the observed market price, the spread, available supply, and a demand signal — computed from current market conditions.

Trades

A trade is a completed capacity transfer. It records the parties (seller_id, buyer_id), units, price_per_unit, total_price, the platform_fee and seller_credit, and the buyer's and seller's cap before and after — so the economic effect on each side is fully auditable.

GET /api/capacity/trades lists completed trades for a recipient_id (with a limit, default 50).

Trades are listed, not posted here

There is no public endpoint that executes a trade. GET /api/capacity/trades only reads completed trades. Trade execution runs inside the marketplace engine (ExecuteTrade in modules/services/capacity/domain/engine.go), which validates and deducts units atomically to avoid over-selling a listing. The REST surface exposes listings, the order book, pricing, config, and this read-only trade history.

Market config

GET/PUT /api/capacity/pools/{pool_id}/config reads and writes the pool's marketplace configuration — the fee structure, price band, and trading limits that govern every listing and trade in the pool.

Prop

Type

PUT rejects an inconsistent band (e.g. min_fee > max_fee) with 422.