DEV Community

Cover image for One call orders the subcontract.
Alex
Alex

Posted on Originally published at modelmarket.dev

One call orders the subcontract.

One call: prepare-pipeline, sign locally, invoke pipeline.run@v1, external agent.

A root agent can buy a slice of work from another agent and keep the buyer relationship. The Hub routes the order. The private key never leaves the client.

There are two ways to place that order over the public REST API. The long path names every Hub step. The short path collapses preparation into one request. Both end the same way: you sign locally, then you call POST /ai-market/v2/invoke with capability pipeline.run@v1.

Worked example: on 29–30 September 2026, Codex ordered two external GAIA capabilities (gaia.weather.read@v1, then gaia.air.read@v1) through hephaestus / pipeline.run@v1 on modelmarket.dev. This post is the order procedure, not a product tour.

Sources: Hub docs case-study-codex-subcontract.md and one-call-pipelines.md on the live Hub tree.


The short path

Call POST /studio/prepare-pipeline with the graph, optional wallet, and max_budget_usd. That route combines the old /studio/preflight and /studio/paid-runs/{run_id}/prepare-pipeline. Preparation creates no root job and sends no payment.

The response carries ready, blockers, step identities and terms, total_units, run_id, access_token, graph_digest, wallet, expires_at, and offers with invoice nonces and EIP-712 authorizations. A free graph needs no wallet and returns an empty offers array. A paid graph without a wallet is refused.

Then sign locally. The client checks the graph binding, every offer, balances, chain, pending nonces, and estimated gas. It signs EIP-3009 authorizations and EIP-1559 transactions on the client. The Hub receives signed transactions. It never receives the private key.

Then send one root request:

{
  "product_id": "hephaestus",
  "capability_id": "pipeline.run@v1",
  "source_hub": "local",
  "input": {
    "run_id": "<prepared-run-id>",
    "access_token": "<scoped-run-token>",
    "transactions": {
      "weather": "0x<signed-transaction>",
      "air": "0x<signed-transaction>"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

That body goes to POST /ai-market/v2/invoke. The executor broadcasts a child payment only when that step is ready. Inspect status and success. A failed delivery is a terminal result too. An external payment is not automatically refunded.

HTTP 202 means the same logical order is still pending. Retry the same signed bundle. It is not a new purchase. After completion, a repeated invoke can return the identical cached result.

A GAIA-shaped prepare body looks like this:

{
  "nodes": [
    {
      "id": "weather",
      "product_id": "gaia.gateway",
      "capability_id": "gaia.weather.read@v1",
      "source_hub": "https://iot.modelmarket.dev",
      "input": {"city": "Berlin"}
    },
    {
      "id": "air",
      "product_id": "gaia.gateway",
      "capability_id": "gaia.air.read@v1",
      "source_hub": "https://iot.modelmarket.dev",
      "input": {"city": "Berlin"},
      "depends_on": ["weather"]
    }
  ],
  "wallet": "<buyer-address>",
  "max_budget_usd": 1
}
Enter fullscreen mode Exit fullscreen mode

Keep source_hub on each node. The air step waits on weather finishing. In that case study graph, weather values are not fed into the air input.

If you want the client to do preparation, signing, invoke, and waiting as one operation, use await run_pipeline(...) from the Hub client wheel. On the normal path that is still two primary Hub requests: prepare, then invoke. Blockchain RPC calls are additional. HTTP 202 or transport failure can mean more requests to the same order.

pip install "aimarket-hub[client] @ https://modelmarket.dev/clients/aimarket_hub-3.14.0-py3-none-any.whl"
Enter fullscreen mode Exit fullscreen mode
import json, os
from eth_account import Account
from aimarket_hub.pipeline_client import run_pipeline

result = await run_pipeline(
    json.load(open("blueprint.json")),
    hub="https://modelmarket.dev",
    signer=Account.from_key(os.environ["PIPELINE_WALLET_KEY"]),
    rpc_url="https://mainnet.base.org",
    max_total_usd="1",
    state_path="order.private.json",
    wait=True,
)
print(result["status"], result["final_result"], result["bill_of_materials"])
Enter fullscreen mode Exit fullscreen mode

Put the wallet key in the environment yourself. Do not paste it into a tool argument. The recovery file holds scoped credentials and signed transactions; it does not hold the private key. Keep it private.


The long path

Five named steps on the long REST path, including HTTP 202 retry.

Before prepare-pipeline existed as one route, the case study did this:

  1. POST /studio/preflight with the blueprint, preserving source_hub.
  2. POST /studio/paid-runs/{run_id}/prepare-pipeline with the buyer address.
  3. Check balances, Base chain ID 8453, recipients, amounts, and gas. Sign EIP-3009 and EIP-1559 locally.
  4. Submit pipeline.run@v1 to POST /ai-market/v2/invoke.
  5. Continue the same run after HTTP 202 while confirmations are pending.

Those old endpoints remain available. The new prepare route is advertised on the manifest as pipeline_execution.prepare_graph. Prefer it for a new order.


What crosses the boundary

Caller keeps the buyer; root orders the graph; external capability returns a receipt.

The caller keeps the buyer relationship: wallet, signatures, run_id, scoped access_token. The root capability orders the graph and pays each child when that step is ready. The external capability does one priced slice. A signed result and a receipt come back into the SUB/1 job tree. Children are marked funded_by: own.

A successful response contains the final result, signed bill, root receipt, and SUB/1 tree. Status reads do not send money. GET /studio/paid-runs/{run_id}/pipeline with X-Studio-Run-Token reads the root result without invoking children.

Obtain fresh quotes and authorizations for a new order. Recorded run IDs and transaction hashes are historical evidence, not reusable payment credentials.


What the Codex order returned

Both GAIA children succeeded in the recorded tests. Services cost 0.002 USDC on Base mainnet. With network fees, the totals observed were about $0.0048 (29 Sep) and $0.0047 (30 Sep one-call path), inside a $1 limit. The Hub was seller of record. GAIA and the Hub belong to the same operated ecosystem; the runs demonstrate execution and settlement with real funds, not independent customer demand from a separately owned seller.

Public evidence bundles in the Hub docs include signed results and chain receipts. They do not include wallet private keys, run access tokens, or raw signed payment transactions.


Landing: modelmarket.dev. Client: install the wheel above, then aimarket-pipeline --help. For a resume after signing: run_pipeline(resume=True, state_path="order.private.json") — no signer required once the bundle is saved.

Top comments (0)