Selling Agents the Whole Job, Not the Model Call

An agent with a wallet can now buy six things from us over x402: a video clip, a voiceover, a music track, an image, a captioned clip cut from a YouTube link, and a finished narrated short from a topic. The last two are jobs no model gateway can resell. Here is the layer under all six, and what broke on the way.

Alex Daro
Alex Daro
Selling Agents the Whole Job, Not the Model Call

An agent with a wallet can now buy six things from us over x402, each at its own endpoint with its own listing on the x402 Bazaar:

EndpointWhat the payment buysPrice
/api/x402/videoA clip from a prompt, optionally from a first frame, on eight models$0.42 to $4.90
/api/x402/speechAn ElevenLabs voiceover, 13 voices, up to 3,000 characters$0.42 per 1,000 characters
/api/x402/musicA Google Lyria 3 track: a 30-second clip or a full song$0.06 or $0.12
/api/x402/imageAn image on Nano Banana, GPT Image 2, Seedream, FLUX.2 or Recraftfrom $0.02
/api/x402/clipA YouTube video's best 30 to 60 seconds, captioned and reframed on the speakerabout $0.12 for a 12-minute source
/api/x402/shortA finished 30-second narrated vertical short from a topic$0.87 as stills, $3.22 animated

There is still no account, no API key and no signup anywhere in the flow. The payment is the credential. The first post covered how the video endpoint works: wallet-keyed accounts, pricing from the request body, claim tickets, and two bugs that failed open. This one is about going from one product to six, and about the last two rows, which are the reason for the rest.

Why sell the job

We had one listing and it sold. It was also one of the cheapest on the Bazaar for what it did, and agents still bought video from other sellers far more often. Price was not the gap: a five-second clip cost $0.42 from us and $0.40 from the two sellers agents bought video from most. The difference was that those sellers listed 65 to 84 endpoints each. An agent already paying one of them for text or images adds its video endpoint on the same integration, and the biggest of them saw its video buyers come back for about eight calls each. At the time, none of ours had come back.

The obvious move is to list more model calls, and we did some of that. But a model call is the one thing every gateway can resell. Anyone with a provider key can put Veo or Nano Banana behind a 402. What a gateway cannot sell is the work around the model:

  • Clip this link. Download the video, transcribe it, find the 30 to 60 seconds worth sharing, reframe a landscape interview onto the person talking, burn in captions.
  • Make a short about this. Write a script, split it into scenes, render each scene, narrate it, lay a music bed under the narration, caption it, add callouts.

Those are pipelines we already run every day for our own channels. Selling them to agents meant putting a payment in front of a graph that already worked, not building a new product. And because each endpoint is its own Bazaar listing, and agents search the Bazaar by intent, six endpoints are six places to be found instead of one.

Six products, one contract

The video endpoint was built as a one-off. Copying it five times would have meant six copies of the parts that are easy to get wrong: the settlement order, the claim ticket, the free retry, the refusal before payment. So the first job was to pull those out into one shared layer, and describe each product as a small declaration on top of it:

  • how to read a request body, and what to refuse before any money moves
  • what the request costs
  • which published pipeline renders it, and which of its settings the buyer's choices set
  • how to report the result, and what the listing says

A route factory turns that declaration into the paid POST, the status GET and the retry. The video endpoint moved onto it with its tests unchanged, which was the check that the layer was the right shape.

Every product renders on a published pipeline on the same engine our human customers use. What the buyer picked (the voice, the model, the shape, the length) arrives as a per-run setting, and a run may only change the settings its pipeline marks as changeable per run. The single-model pipelines fail closed: the model their saved graph names is one no provider serves, so a run that somehow arrives without the buyer's choices is refused by the provider for free rather than rendering something nobody bought.

Where a product renders depends on how long it takes. Speech, music and images finish in seconds, so they render on the same serverless function that took the payment, after the response has been committed. Clips and shorts need ffmpeg and a few minutes, so they go to the worker queue. If the queue refuses a job, the run is closed as failed at once, so the buyer gets a retry link instead of a run that never starts.

Priced from what it cost

Every price is what the render costs us at the provider, times the same markup our human customers pay. The render is the same work either way, so there is no reason to charge an agent a different multiple.

For the single-model products the cost is the provider's published rate. For the two pipelines we measured production runs, and the numbers set the price:

  • Clip: $0.02 plus half a cent per minute of source, times the markup, rounded up to the cent. That rate is about twice what a run typically costs, so a source whose transcript has to be retried is still covered. A 12-minute NASA interview was clipped in 97 seconds for $0.029 of provider cost, against a $0.12 price. An hour-long source costs $0.45.
  • Short: the stills version measured $0.37 to $0.55 a run and sells for $0.87. The animated version measured $1.98 and sells for $3.22.

The animated short is where the internal estimate was wrong. Our credit estimator prices every Veo scene as if it carried generated audio. The scenes in a short are rendered silent, under the narrator, so the estimate came out well above what the runs actually cost, and a price built on it would have overcharged every buyer. We priced from the measured runs instead, and a test holds each price at least ten percent above the most expensive production run, after the markup comes off.

The quote has to be the check

The first post laid down a rule: the price function must be a pure function of the request body, because it runs twice. Once on the unpaid request to build the 402 challenge, and again on the paid retry, where a payment that does not match the re-derived price is refused.

A clip breaks that rule by design. Its price depends on how long the source video is, and the body only holds a link. So the price function has to look the video up before it can answer the 402.

The review before launch found what that does to the rule. The price function and the check that runs before settlement each looked the video up on their own. If YouTube failed to answer the first lookup and answered the second, the quote fell back to the minimum, one minute and four cents, while the check saw a real two-hour video and let it through. The facilitator holds the payment to the price the request was quoted, so a buyer could have paid $0.04 for a two-hour source.

The fix is to have one lookup per request. It is keyed by the parsed request body and held as a promise, so the price (for both payment networks, quoted at once) and the check before settlement read the same answer. A request whose lookup failed is quoted the minimum and then refused by the check, which sees the same failure, before the payment settles. Nobody pays it.

The rest of what a clip refuses happens there too, before any money moves: a private, live or upcoming video, anything under ten seconds or over two hours, and any link that is not YouTube. The worker only ever sees a watch URL we build from the video id, and its download is capped at the minutes that were paid for.

Looking up YouTube from a datacenter

The lookup worked on a laptop. From the serverless function, YouTube answered every request with its "confirm you're not a bot" wall. When the endpoint first went live, every clip request was refused before settlement: nobody was charged, and nobody could buy one either.

Our worker already downloads from YouTube through residential proxies, so the lookup now goes out through the same ones. Node's fetch has no proxy support without another dependency, so the tunnel is a small CONNECT over node:http and node:tls, and each proxy gets several attempts: two of YouTube's player clients, then the watch page itself. A proxy that met only bot walls sits out for 30 minutes, and one that could not be reached sits out for five.

The subtle part was deciding what counts as an answer. An early version treated the bot wall as YouTube saying the video could not be played, and cached that as a refusal, so a public video would have been turned away as private until the cache expired. Now a bot wall or a "try again later" never becomes a cached refusal, and a refusal is cached only when every proxy was actually reached. Uncached lookups are also rate-limited, so the free 402 path cannot be used to hammer YouTube through us.

A retry resumes instead of starting over

When a run fails, the buyer pays only for the steps that ran, at the normal rate. The rest of the payment stays on the wallet's balance, and the status response carries a retry URL that runs again on that balance without a second payment. Each run can be retried once.

For speech, music and images, a retry simply renders again, and the buyer may reword the request. The review found the catch: buy the cheapest render, then retry with the longest text. A retry now can never cost more than the purchase it replaces.

For clips and shorts, a retry resumes the failed run. The steps that finished are reused rather than run and billed again, so a short that failed at the captions keeps its script, scenes, voice and music. The first stills short we rendered in production failed earlier than that: the writer marked one scene in a way the script parser did not expect, and the step that pulled that scene out of the script threw. Resuming it re-ran the writer and everything after it, because a step that failed on bad input needs that input made again, and the short finished in three minutes. The parser, which our templates share, now accepts the variations the writer actually produces.

Two details keep a resume honest. The retry checks that the balance covers what the resume will actually run, which is usually just the failed step and occasionally more, and never more than the full price. And a clip's resume always picks its moment again, because replaying a bad pick would make the same bad clip twice.

Staying listed

A Bazaar listing appears after the first paid call to its endpoint, and drops out of the index after 30 days without one. We learned the second half when our first listing, for credit top-ups, aged out and nothing told us. With six listings, remembering is not a plan, so a daily job checks every one and emails us before one lapses, and the example repo has a keepalive script that makes one paid call on the cheapest item each endpoint sells, about $1.49 for all six. A call paid on Solana renews a listing just as a call paid on Base does.

The pipelines behind the endpoints are now locked, too. Each one's published graph is part of a paid API, so an edit is a breaking change for every buyer on their next call. The editor, chat and our MCP server all refuse to edit, publish, roll back or delete them, and only the script that owns a pipeline can change it.

Try it

Ask for a clip without paying and the 402 quotes the exact price for that link:

curl -X POST https://www.trezalabs.com/api/x402/clip \
  -H "Content-Type: application/json" \
  -d '{"url": "https://www.youtube.com/watch?v=KtTXyBJ2N30"}'

Or a short, as stills:

curl -X POST https://www.trezalabs.com/api/x402/short \
  -H "Content-Type: application/json" \
  -d '{"topic": "how octopuses taste the world with their arms", "style": "stills"}'

A GET on any of the six endpoints returns its full menu. The example repo buys any of them from a funded wallet on Base or Solana with npm run buy:clip, buy:short, buy:speech, buy:music or buy:image, and polls for the result when it takes longer than the paid call. One setting to copy from it: the x402 client caps any single payment at $1 by default, which quietly rejects an animated short, so the example raises the cap to $5. The docs have every endpoint's fields, and the demo page has a purchase you can check on-chain.