AnjomanBeta
7 min readUpdated October 27, 2026

Choosing an Inference Path

Telegraph answers questions over several different rails. They are not variations on one API — they differ in who picks the miner, how you pay, whether the answer comes back in the same call, and what you need to hold before you start. Picking the wrong one usually isn't discovered until the payment step fails.

Start here, then follow the link for the one you want.

The short answer

You are...UsePage
A script or agent that wants an answer nowHTTP ask (auto-routed)Engine Inference
Calling often enough that per-request settlement hurtsEscrow on the same HTTP endpointsPaying from Escrow
Sure which miner you wantHTTP ask directEngine Inference
An LLM agent with an MCP clientMCP serverMCP Server
Wanting a continuous feed, not one answerWebSocket subscribeWebSocket Signals
A smart contract targeting a capabilityERC-8183 jobOn-Chain Jobs
A smart contract that prefers one known minerERC-8183 job with createJobForMinerOn-Chain Jobs
Just reading what the network already producedDaemon feed (free, no inference)Daemon Signal Feed

The comparison

HTTP askHTTP ask/{id}WebSocket askWebSocket subscribeERC-8183 job
Who picks the minerEngine's LLM routerYouEngine's LLM routern/a, pushed to youProtocol, by intent (or you, with createJobForMiner)
You needUSDC + x402 client, or funded escrowUSDC + x402 client, or funded escrowWallet + ≥ 1 escrowWallet + ≥1 escrowUSDC in escrow, or approve the Diamond and createJobAndPay
How you payPer call, x402, or from escrowPer call, x402, or from escrow$0.01 per call, from escrowPer delivered signal, from escrowjobBasePrice from escrow
Answer arrivesIn the response, in the Intent's shapeIn the response (raw, or shaped for the canonical form)As stream eventsPushed, on the Daemon's cycleCallback + on-chain state
SynchronousYesYesYesNoNo
Fresh inferenceYesYesYesYes (Daemon-generated)Yes
ConcurrencyMany in flightMany in flightPer connectionOne subscription per walletMany in flight

Two things that surprise people:

  • WebSocket ask and ask_direct are billed, just not via x402. Each call draws a flat $0.01 from your on-chain escrow, settled at the next epoch close rather than paid up front. It does not count against a subscription's spend_limit_usdc — that cap is only for pushed signals. Clearing the $1.00 connect minimum once does not buy you unlimited calls.
  • The Daemon feed is not inference. It is a read of answers the network already produced on its own schedule. You cannot ask it your own question.

The minimum that works, per path

Everything below is the shortest complete call for that rail. Each links to the page with the full contract.

HTTP ask — auto-routed

The default. You describe what you want; the Engine classifies it and picks a miner.

js
import { wrapFetchWithPayment } from "@x402/fetch";
import { createSigner } from "@x402/evm";

const fetchWithPayment = wrapFetchWithPayment(fetch, createSigner(process.env.EVM_PRIVATE_KEY));

const res = await fetchWithPayment("https://devnode.telegraphprotocol.com/engine/v1/ask", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ query: "What is the current price of Bitcoin?" }),
});

The library handles the 402 challenge, signs, and retries. Doing it by hand is covered in Paying with x402 — read it before you try, because a malformed payment comes back as a bare 402, indistinguishable from not having paid at all.

→ Engine Inference · Paying with x402

HTTP ask — direct to a miner

You supply the miner ID, the endpoint and the payload. No routing, no reasoning or intent in the response.

bash
# 1. find a miner and a valid endpoint (free)
curl 'https://devnode.telegraphprotocol.com/api/miners?intent=WEATHER_CHECK_TEMPERATURE'

# 2. call it — same x402 flow as above
POST /engine/v1/ask/18
{ "method": "GET", "endpoint": "/predict", "payload": { "lat": 25.2, "lon": 55.3 } }

Miner IDs are not stable over time. Always take them from /api/miners, never from an example.

Unlike the auto-routed path, this one halts if the node predicts the call will fail — a 422 you are not charged for. See Pre-request validation.

→ Engine Inference

MCP server

The zero-crypto-code path. A local Node process holds the key, pays, and exposes every miner as a tool to your agent runtime.

json
{
  "mcpServers": {
    "telegraph": {
      "command": "node",
      "args": ["/path/to/Telegraph-MCP/dist/index.js"],
      "env": {
        "TELEGRAPH_NODE_URL": "https://devnode.telegraphprotocol.com",
        "TELEGRAPH_ENGINE_URL": "https://devnode.telegraphprotocol.com/engine",
        "TELEGRAPH_DAEMON_URL": "https://devnode.telegraphprotocol.com/daemon",
        "TELEGRAPH_EVM_PRIVATE_KEY": "0xyour_burner_key"
      }
    }
  }
}

Node.js 20+ is required — on Node 18 every paid call fails with Crypto API not available.

→ MCP Server

WebSocket

One connection, three uses: on-demand ask, a live subscription, or both. Everything except ping and list_subnets needs the wallet handshake first.

wss://devnode.telegraphprotocol.com/engine/ws?wallet_address=0xYourAddress
→ {"action": "auth_wallet"}
← wallet_challenge
→ {"action": "wallet_verify", "signature": "0x..."}   (personal_sign the message field)
← wallet_verified, then connected

→ {"action": "ask", "query": "..."}                    live inference
→ {"action": "subscribe", "intents": ["WEATHER_CHECK_TEMPERATURE"], "spend_limit_usdc": 500000}

You must have ≥ 1.00 USDC in escrow before the handshake, or the connection is closed. Funding it is three transactions.

→ WebSocket Signals

ERC-8183 job

Your contract pays (from escrow, or in the same call with createJobAndPay), names an intent and typed params, and gets the intent's fixed, processed answer delivered to a callback. Priced at jobBasePrice, not the miner's floor.

bash
cast send $DIAMOND \
  "createJobCapped(bytes32,(address[],uint256[],string[],bool[]),address,uint256)(uint256)" \
  "(cast keccak 'WEATHERFORECASTLAHORE')" '([],[24],["Lahore"],[])'CALLBACK 2000000 \
  --rpc-url RPC --private-keyKEY

Read the intent's on-chain layout from GET /engine/v1/intents/{intent} (onchain) first. The intent in the command above is a placeholder. If no miner can answer, the validator quorum fails the job, you are refunded, and your callback gets success = false.

→ On-Chain Jobs (ERC-8183)

On-chain miner request (removed)

The old gas-only rail (outboundSubnetMessage) was removed with SubnetFacet. Use a job. To prefer one miner, use createJobForMiner.

→ On-Chain Miner Requests (how to move over)

Before any of them: discovery is free

None of these endpoints cost anything, and every path is easier if you call them first:

EndpointWhat it tells you
GET /api/minersEvery registered miner — id, slug, endpoints, schemas, intents, floor price. Filter with ?intent=, ?status=, &limit=.
GET /api/miners/{registrationId}One registration, including rejected ones the catalogue omits.
GET /engine/v1/intentsThe canonical intent set, each with a description and a live miner count.
GET /engine/v1/intents/{intent}/minersWho competes for an intent, before you spend anything.
GET /miner-dispatcher/openapi.jsonMachine-readable OpenAPI spec of every miner endpoint.
GET /engine/v1/signal/{hash}Look up any past call by the signal_hash it returned.

An intent being canonical does not mean anyone serves it. Check miner_count on /engine/v1/intents before building against one.

Verifying afterwards

Every paid call returns a signal_hash. It commits to the request and the response together, and you can re-derive it yourself:

bash
curl https://devnode.telegraphprotocol.com/engine/v1/signal/0x7a44569d...

The response carries the signal, the result behind it, and the payload the hash was computed over. Keep the hash — it is the only handle you have on a call after the fact.

Was this helpful?