Build on DiemSum

Order inference like an agent

Pay-per-use inference over x402 — any Venice model, no API key, no account. Quote a request, sign an on-chain payment that satisfies the quote, and get the completion back. Access is gated by payment, so this same contract works for headless agents and browser wallets alike.

x402 + ERC-7710No buyer API keyOpenAPI 3.1

For agents

Point your agent at skill.md

One file teaches an agent the whole contract: discovery, quote-first pricing, ERC-7710 signing, retrieval, and multi-turn. Drop the URL into your tool config and let the agent read it.

Skill URL

Concepts

How payment works

There is no API key. Every paid completion is a single endpoint, POST /api/v1/inference, called twice. The first call carries no payment header, so DiemSum replies 402 Payment Required with a binding quote and a base64 PAYMENT-REQUIRED envelope. The second call re-sends the same body with a PAYMENT-SIGNATURE header, and the server verifies the payment, calls Venice behind the seller key, settles, and returns the completion.

5-minute quote TTLPaid body must match the quoteNo raw prompt retention — sha256 only

Walkthrough

Quote → pay → retrieve

The whole exchange in four moves. Keep the request body identical between the quote and the paid retry, or the server rejects it with 400 quoted_request_mismatch.

  1. Request a quote (expect 402)

    POST the request you actually want. Exactly one of prompt or messages[] is required; snake_case and camelCase keys are both accepted.

    requesthttp
    POST /api/v1/inference
    Content-Type: application/json
    
    {
      "offerId": "offer_venice-llama-3_3-70b",
      "messages": [
        { "role": "user", "content": "Explain x402 in one sentence." }
      ],
      "maxOutputTokens": 512,
      "buyerAddress": "0xYourBuyerOrPayerAddress"
    }
  2. Read the quote and envelope

    The 402 carries the quote in the body and a base64 PAYMENT-REQUIRED header. The X-DiemSum-Quote-Id and X-DiemSum-Request-Hash headers echo the quote so you can correlate without parsing the body.

    responsehttp
    HTTP/1.1 402 Payment Required
    PAYMENT-REQUIRED: <base64 paymentRequirements>
    X-DiemSum-Quote-Id: quote_8f...
    X-DiemSum-Request-Hash: sha256:1c9a...
    
    {
      "error": "Payment required",
      "code": "payment_required",
      "quote": {
        "quoteId": "quote_8f...",
        "requestHash": "sha256:1c9a...",
        "quotedMaxUsd": 0.0123,
        "expiresAt": "2026-06-08T00:05:00.000Z"
      },
      "paymentRequirements": {
        "x402Version": 2,
        "accepts": [{
          "scheme": "exact",
          "network": "eip155:8453",
          "amount": "12345",
          "asset": "0x8335...2913",
          "payTo": "0xSeller...",
          "maxTimeoutSeconds": 300,
          "extra": { "assetTransferMethod": "erc7710", "quoteId": "quote_8f..." }
        }]
      }
    }
  3. Sign the payment, then pay

    Build an x402 payload that satisfies accepts[0] exactly (same amount, asset, payTo, network), base64-encode it, and send it as PAYMENT-SIGNATURE on a re-send of the identical body.

    paid retryhttp
    POST /api/v1/inference
    Content-Type: application/json
    PAYMENT-SIGNATURE: <base64 x402 payload>
    
    { ...the EXACT same body you quoted in step 1... }
  4. Retrieve the completion

    A 200 returns the completion, a usage receipt, and a nextTurn lineage you keep for follow-ups.

    responsehttp
    HTTP/1.1 200 OK
    
    {
      "quote": { "quoteId": "quote_8f...", "conversationId": "conv_...", "turnId": "turn_..." },
      "inferenceRequest": { "inferenceRequestId": "inf_...", "status": "completed" },
      "receipt": { "receiptId": "rcpt_...", "usage": { "promptTokens": 18, "completionTokens": 24 } },
      "completion": { "content": "x402 lets a server demand on-chain payment for a request..." },
      "nextTurn": {
        "conversationId": "conv_...",
        "parentInferenceRequestId": "inf_...",
        "parentReceiptId": "rcpt_..."
      }
    }

The same flow in JavaScript

quote-then-pay with fetchjs
// One paid completion = two POSTs to the SAME endpoint with the SAME body.
const base = "https://diemsum.com";
const body = {
  offerId: "offer_venice-llama-3_3-70b",
  messages: [{ role: "user", content: "Explain x402 in one sentence." }],
  maxOutputTokens: 512,
  buyerAddress: "0xYourBuyerOrPayerAddress",
};

// 1) Quote — no payment header, so the server answers 402.
const quoteRes = await fetch(`${base}/api/v1/inference`, {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify(body),
});
if (quoteRes.status !== 402) throw new Error(`expected 402, got ${quoteRes.status}`);
const { paymentRequirements } = await quoteRes.json();

// 2) Sign an x402 payload that satisfies accepts[0] exactly (ERC-7710
//    delegation for agents; see docs/examples/direct-x402-erc7710.ts).
const paymentSignature = await signX402(paymentRequirements.accepts[0]);

// 3) Pay — re-send the IDENTICAL body plus PAYMENT-SIGNATURE.
const payRes = await fetch(`${base}/api/v1/inference`, {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "payment-signature": paymentSignature,
  },
  body: JSON.stringify(body), // byte-for-byte identical or you get 400 quoted_request_mismatch
});
const result = await payRes.json();
console.log(result.completion.content);

Reference

Endpoint reference

MethodPathPurpose
GET/api/v1/offersList buyer-callable offers. No auth — pick an offerId.
GET/api/v1/marketOffers joined with the Venice reference price and savingsPct per offer.
POST/api/v1/inferenceCalled twice: no payment header → 402 quote; re-send the identical body with PAYMENT-SIGNATURE200 completion.
GET/api/v1/quotes/{quoteId}Quote + payment envelope. 404 if unknown, 410 if expired.
GET/api/v1/inference/{inferenceRequestId}Status of a paid request. 404 if unknown.

Reference

Error codes

Every error carries a stable code alongside the HTTP status. Branch on the code, not the prose.

StatusCodeMeaning
402payment_requiredQuote issued. Pay and retry the identical body.
400quoted_request_mismatchThe paid body does not reproduce the quoted request.
400transcript_hash_mismatchA supplied transcriptHash does not match the server hash.
400invalid_payment_signatureThe PAYMENT-SIGNATURE header could not be decoded.
402verification_failedThe facilitator rejected the payment during verification.
409settlement_in_progressSettlement for this quote is mid-flight. Retry with the same PAYMENT-SIGNATURE shortly.
409quote_not_payableThe quote was already consumed or canceled.
410quote_expiredQuote older than the 5-minute TTL. Re-quote.
429rate_limitedBack off using the Retry-After header.
502settlement_failedVenice ran but settlement failed: output is withheld and the request is marked withheld. Retry with the same PAYMENT-SIGNATURE to resume settlement and retrieve the stored output — Venice is not re-run. A background reconciler also retries withheld settlements automatically.
502venice_execution_failedVenice failed before output was produced; the reservation is released and nothing settles.
503seller_route_unavailableThe seller route lost its key, approval, or capacity after quoting.
503facilitator_config_missingThe deployment is not payment-capable.
500paid_inference_failedServer-side failure mid-payment. Retry with the same PAYMENT-SIGNATURE.

Two buyer identities

Direct agent vs browser human

Both paths speak the same x402 contract above. They differ only in how the payer authorizes the facilitator to pull funds.

Headless

Direct agent · ERC-7710

Your agent owns an EOA, upgrades or validates it for EIP-7702, and signs a direct ERC-7710 delegation to the quoted facilitator redeemers. No browser, no wallet UI.

  • Signs an x402 payload satisfying accepts[0] exactly.
  • Reference: docs/examples/direct-x402-erc7710.ts
Browser

Browser human · ERC-7715

A MetaMask user grants an ERC-7715 Advanced Permission once; the dapp redelegates it per quote. This is the Order UI path, not the headless agent path.

  • One permission grant covers many quotes within its budget.
  • The browser signs each per-quote redelegation in MetaMask.

Headless agents

Agent permission bootstrap

A long-running buying agent can obtain a scoped, recurring spending mandate from its human owner once, then pay for many inferences autonomously inside that budget — no human approval per call.

The agent opens a request with its own delegate address and a recurring budget. DiemSum returns an approval URL the human opens in a browser; they connect MetaMask and grant an ERC-7715 erc20-token-periodic permission whose delegate is the agent's own address — not a browser session key. The agent polls until the grant lands, stores it locally, and redelegates it per quote for each purchase.

MethodPathPurpose
POST/api/v1/agent-permissions/requestOpen a request with agentAddress and periodAmountUsd. Returns 201 with the request, approvalUrl, and a poll target.
GET/api/v1/agent-permissions/{requestId}Poll for status. Returns request plus grant (null until granted). 404 if unknown.
POST/api/v1/agent-permissions/{requestId}/grantPosted by the permit page after the human signs, carrying the granted permission context. You do not call this directly.
POST/api/v1/agent-permissions/{requestId}/declineThe human declines; the request moves to declined and nothing is granted.
  1. Request the mandate

    POST your delegate agentAddress and a periodAmountUsd (e.g. 100 per day). DiemSum returns a requestId and an approval URL like /permit/{requestId}.

  2. Hand the URL to your human

    They open it, connect MetaMask, and grant the erc20-token-periodic permission. They verify in the MetaMask prompt that the delegate address matches your agent before signing — that verification is the security boundary. Granting needs the MetaMask desktop browser extension; MetaMask Mobile cannot grant ERC-7715 permissions yet, so on a phone the permit page offers a copy-the-link gate and only the decline path.

  3. Poll, then store the grant

    GET the request until status is granted, then save the permission context locally (for example ~/.diemsum/permissions.json, chmod 600).

  4. Redelegate per purchase

    For each inference, sign a child delegation from the stored root permission to the quoted facilitator redeemers with an exact-amount USDC caveat matching the quote, encode child plus root as the payment context, and send it as PAYMENT-SIGNATURE. A 100-USD-per-day root funds a 1-USD call 100 times a day with no new human approval.

The approval and poll URLs are unauthenticated capability URLs keyed by the high-entropy requestId. The real boundary is the human verifying the delegate address inside MetaMask before signing; the granted context is unusable without the agent private key that owns the delegate address. The root grant is scoped by token, budget, period, and expiry; DiemSum adds the per-quote payee and redeemer scope in the child redelegation.

Conversations

Multi-turn

DiemSum keeps no transcript server-side — only sha256: hashes. For a follow-up, re-send the full messages[](including the prior assistant content you kept) plus the lineage from the previous turn's nextTurn:

  • Carry forward conversationId, parentInferenceRequestId, and parentReceiptId.
  • The follow-up earns its own quote, receipt, and turnId under the same conversationId.
  • Then repeat quote → pay → retrieve for the new turn.

Copy, run, ship

Runnable examples

Both run against a local server with the facilitator stubbed: X402_VERIFY_MODE=stub npm run dev.

Node 22 · no build step

docs/examples/agent-quickstart.mjs

End-to-end discover → quote → sign → retrieve. Safe by default: prints the exact follow-up body without paying until you supply a signer key.

TypeScript

docs/examples/direct-x402-erc7710.ts

The ERC-7710 signing core: decode the PAYMENT-REQUIRED envelope, delegate to the facilitator redeemers, and encode the PAYMENT-SIGNATURE.