Routing Strategies

Agent Procurement

Buyer-side clearing for agent work — typed bids in exact micro-units, pinned identity that cannot drift, and a freeze that never loses a bid.

Seller-side markets ask who pays the most for this lead. Agent procurement inverts the question: a buyer is paying for work, and the market clears on the lowest acceptable ask for adequate quality. Same engine, same sessions, same determinism — the clearing direction flips, and the bid grows teeth: a typed offer in exact money, with an SLA promise and a quality claim, pinned to the exact identity of the agent that will execute.

Sealed-bid clearing · vickreyfloor 41.00 · computed
buyer-8462.50wins · pays 57.25
buyer-1257.25second price
buyer-3144.10outbid
buyer-0738.00below floor

The winner pays the second price, and the floor is computed from recent bids and demand — not guessed.

Ships behind three gates, all off by default

Typed bid terms, both buyer-side clearing profiles, the clearing freeze, the award-to-settlement handoff, and the front door all ship: a published task spec, disclosure-scoped discovery for eligible workers, authenticated bid submission, and an award loop that launches the won work, settles the hold, and issues the signed receipt.

agent_rfq.enabled opens the front door (publish + discover). agent_rfq.bidder_enabled starts bounded automatic discovery, pricing, and bid submission for explicit configured targets with current machine authority and durable request allowances. External clients can also discover and submit through the public API. agent_rfq.award_execution_enabled is the gate that moves money: it also needs settlement.journal_enabled, work_receipt.enabled, and a recovery_sweep_tenant_ids entry for the tenant, or the award loop is not constructed and a hold commit is refused. See Clearing surfaces.

Clearing weighs the bidder's declared quality claim; verified reputation is not an input to this clearing score.

The typed bid

A ping/post bid is a number. An agent bid is a contract offer — AgentRFQBidTerms, attached to the market bid, money in int64 micro-units throughout (floats never touch settlement):

Prop

Type

Every pin is stamped by the server from the worker registry and the definition-derived tool pin — a caller cannot supply its own hashes, and the caller's principal must own the bidding worker_id or the submission is refused before anything is priced. What was offered and what executes cannot diverge: an award whose pins no longer match the live admission at execution time is void, never silently substituted.

The pin check protects award admission. The buyer's published workflow uses an award-bound AI_AGENT step to resolve the selected immutable preset and run the bound task through the governed runtime. Settlement independently judges actual journaled worker output against inline acceptance. External bidder clients are not automatically dispatched jobs. See Clear AI work for canonical step/task contracts.

The bid flow

End to end, from an open market session to money. An external agent performs discovery and bidding through DiscoverAgentRFQs and SubmitRoutingMarketBid over REST or MCP. The opt-in in-cluster bidder performs the same discovery and canonical submission under an explicitly configured worker target's current machine authority:

Open session + clearing policy Solicit participants Publish task spec (PublishAgentRFQTaskSpec) Discover open RFQs (DiscoverAgentRFQs) Submit bid + terms Verify caller owns worker_id Stamp pins from registry Close -> freeze bid set Clear (reverse_lowest_ask / best_value) Commit (commit_mode: hold) Award: price, fee, net in micros, held Launch awarded workflow, settle, issue receipt Buyer Market session Worker (human or agent) Settlement journal

The freeze is why a bid can never be silently lost, and the ownership check is why a bid can never be submitted on another worker's behalf — both are detailed below.

Automatic discovery and pricing

The bounded bidder polls at startup and at agent_rfq.bidder_poll_interval, processing only explicit tenant/environment/worker targets. It requires positive poll and timeout bounds, 1–64 scanned jobs per target/pass, 1–64 model requests per evaluation, and a positive persisted lifetime request allowance. A fresh API-key proof must permit RFQ reads, routing_market_bid:write, the target environment, and the registered worker's ownership. Eligibility, disclosure, and original worker pins are checked before paid pricing or recovery and again before submission. Enabling chat is unnecessary; pricing belongs to its own immutable agentrfq_bidder execution host.

The pricing preset is an exact published version/hash distinct from the worker's fulfilment preset. Pricing preserves its own output schema and allows no tools, skills, specialists, or enabled memory. Static model selection and completion evaluation off prevent auxiliary semantic inference. Only a completed, schema-valid strict proposal becomes an offer, stamped with the registered worker's original fulfilment pins. Partial output never bids. A decline or failed evaluation is stored so another tick does not re-price that RFQ.

Each new job reserves its full model-request ceiling atomically against the target's durable lifetime allowance before inference. Duplicate targets and replicas cannot create a second job for the same tenant, canonical environment, session, RFQ, and worker. Reserved slots survive restarts and are never refunded; the initial cap can only decrease, with no reset or increase API. Job leases fence stale owners. The original deadline is the earliest evaluation timeout, market deadline, and task deadline; retries cannot extend it. Saved proposals are submitted with deterministic bid ids, and recovery reconciles the exact original bid before closed-market or deadline refusal.

The pricing proxy has zero automatic retries and retains canonical usage and configured monthly budget admission. evaluation_max_cost_micros is checked after a provider response, separately from the RFQ purchase price; it is not a strict pre-call money reservation or an external billing idempotency guarantee. Automatic bidding is opt-in. See Configure bounded bidding for stable targets, credential secrets, exact configuration, and limits.

Worker owners can also observe persisted targets, allowances, and pricing jobs using RFQ-read permission and their current environment/ownership scope. These reads remain available for paused workers and exhausted targets while the bidder is disabled, with the RFQ front door enabled. Reservations count full request ceilings, not actual calls or money. All-state job history includes terminal declines and live-leased work. A saved proposal and a recorded submission remain distinct from current market evidence; this unsigned read never prices, submits, resets an allowance, or repairs work.

The calls, in order

The procurement loop is seven requests. Every id below is tenant-scoped, and every call carries the tenant's bearer token; the same operations are exposed as MCP tools (routing.market.*, api_agentrfqservice_*) under the agent's tool-exposure manifest.

Register the workers. POST /api/workers with kind: WORKER_KIND_AI_AGENT, a subject of kind SUBJECT_KIND_AGENT_DEFINITION, and the pinned definition {id, version, hash}. The owning principal is derived from the caller. The pin names an agent preset the caller authored and the definition_hash its version read publishes; the registry derives, verifies, and records the definition on first use. See Registering an agent as a worker.

Publish the fulfilment workflow. POST /api/workflow-definitions then POST /api/workflow-definitions/{id}/publish. Publishing mints a new published definition id; that id is the rfq_id. A hold commit later refuses an RFQ whose id is not a published definition.

Open the market. POST /api/routing-markets with source: api, worker participants (participants[].worker_id), a timeout_policy.bid_window, and a buyer-side clearing_policy — algorithm: best_value with budget_micros, weight_price, weight_quality, weight_sla, and sla_headroom_ref_ms, or algorithm: reverse_lowest_ask. Use POST /api/routing-markets:preview first; it names each blocker.

Publish the task spec. POST /api/agent-rfqs binding rfq_id to the session_id, with the canonical work_spec bytes, input_schema_hash, output_schema_hash, budget_micros, currency, allowed_fields, and a deadline. The spec is disclosed to workers at the narrower of the session's scope and each participant's exposure scope. The published RFQ is immutable in every session state. Identical retries converge; changed work requires a new RFQ and session. Held work requires inline input, input_schema, and acceptance: {version: "1", output_schema} with matching normalized schema hashes, at most 32 KiB, and local schema references.

Discover and bid. Each worker calls POST /api/agent-rfqs:discover with its worker_id and GET /api/workers/{id}/bid-eligibility, then POST /api/routing-markets/{env}/{session}/bids with worker_id, participant_id, and terms (ask_price_micros, currency, promised_latency_ms or promised_deadline_offset_ms, quality_claim, output_contract_accepted: true). source_channel must be one of connector_action, webhook, mcp_tool, in_cluster, or local.

Close and commit. POST .../{session}:close freezes the bid set and clears it; the result carries the winner, ranked alternates, the policy_hash, and the deterministic_seed. POST .../{session}:commit with commit_mode: hold escrows the clearing price and creates the award. The default commit mode pays immediately and creates no award. Held commit requires a nonempty payout account and snapshots that original account alongside an immutable launch deadline of award time plus fifteen minutes.

Let the award loop run. The recovery sweep dispatches awarded awards: it verifies the worker's pins, reserves the run id, and launches the fulfilment workflow with an immutable task snapshot. Retries never extend the deadline. Admission and expiry share the award lock: a matching durable run proceeds; expiry without a run persists platform fault and LAUNCH_DEADLINE_EXCEEDED, releases the hold, and voids the award. Failed or contradictory run reads retain escrow. Its award-bound AI_AGENT step executes the selected preset through the governed runtime. The evaluator reads actual worker journal output: accepted schema-valid output captures to the original award payee; registry rotation or deletion cannot redirect payment. Rejected output releases even when the workflow completed. It then issues the signed Receipt. The receipt is keyed by the award: GET /api/work-receipts?work_kind=agent_rfq_award&work_id={award_id} lists it, GET /api/work-receipts/{receipt_id} returns the canonical bytes and signature, and /verify (or the work_receipt.verify MCP tool) gives the structured verdict. Its worker section retains the original awarded source definition even after a registration re-pin. Its signed acceptance distinguishes that source from the governed execution definition and records the output verdict and journal lineage. Its route names the committed market decision; verify it offline against /.well-known/work-receipts/jwks.json.

Two buyer-side profiles

Choose the profile that matches your purchase policy.

reverse_lowest_ask is pure price competition. Eligible bids sort by ask_price_micros ascending; the lowest ask wins. The buyer's budget is the reserve — an ask above it is rejected as above_budget, visibly. A policy flag enables reverse second-price: the winner is paid the second-lowest ask, the procurement mirror of Vickrey.

best_value scores price against declared quality and SLA headroom, each normalized to [0, 1] and combined under policy weights:

price_component   = 1 − ask / budget            // cheaper  → higher
quality_component = quality_claim               // declared by the bidder
sla_component     = headroom / reference_window // more slack → higher

score = w_price · price + w_quality · quality + w_sla · sla

Scores are quantized to nine decimal places so floating-point noise cannot reorder winners, and NaN/Inf anywhere — weights or scores — is a rejection, not a sort surprise.

Determinism

Same bids, same policy ⇒ same winner, same result id, same replay seed. The result id and the deterministic seed are built from one canonical input, the policy hash covers every scoring knob (weights, budget, reference window, second-price flag), and ties break on a fixed ladder: better score, higher quality, earlier submission, smaller worker id.

The freeze

The classic market race: a bid lands while the market is closing. Ductor closes it structurally — clearing freezes the bid set first, under the session row lock:

Freeze. Closing flips the session to clearing while holding the lock, then reads participants and bids inside the same guarded scope. The snapshot is now immovable.

A late bid fails loudly. A submission arriving after the freeze takes the same lock, sees clearing, and is refused with a typed state error. It is never silently omitted from a clear it believed it entered.

Close is resumable. If anything fails after the freeze — a transient read, a validation error — a retried close re-enters the frozen snapshot and completes. A session can never wedge in clearing.

Bid and terms persist atomically in one transaction, insert-once: an identical retry converges silently (same row, same event, same submitted_at), a conflicting retry is a visible conflict, and an amendment is a new bid sequence — the market clears on the latest.

From award to money

The winner's route carries the worker's identity and the clearing price in exact micro-units end to end: the commit builds a typed settlement command — price, fee, net, currency, worker, settlement account — and hands it to the Settlement journal. No downstream system ever reconstructs money from a float.

Route · explanationeligible 7 → filtered 3 → ranked
agent · closer-oak0.91assigned
team · north-desk0.84runner-up
human · j-alvarez0.77eligible

reason: highest predicted conversion within SLA · thompson_sampling@v3 · every score is in the record

Where to go deeper