Developer docs / API v1
Keep the request.
Gain the proof.
https://verifyroute.tech/api/v1.Two lines to switch.
Verify Route speaks the chat-completions request shape that most AI SDKs already send. Point the client at the Verify Route base URL, use a vr-live-… key, and the rest of your code keeps working: messages, streaming, tools, JSON output and usage fields.
Your wallet is your account. Connect it on the dashboard, fund one USDG balance on Robinhood Chain, and create as many keys as you need. No email and no password.
curl https://verifyroute.tech/api/v1/chat/completions \
-H "Authorization: Bearer $VERIFYROUTE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/llama-3.3-70b-instruct",
"messages": [{ "role": "user", "content": "Name three prime numbers." }]
}'import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://verifyroute.tech/api/v1",
apiKey: process.env.VERIFYROUTE_API_KEY, // "vr-live-…"
});
const res = await client.chat.completions.create({
model: "qwen/qwen3-32b",
messages: [{ role: "user", content: "Summarize this in one line." }],
});
console.log(res.choices[0].message.content);
console.log(res.usage); // tokens and cost in USDG
console.log(res.receipt); // signed record of the callTell the router what matters.
Without preferences, the router sends a call to the healthiest verified provider for that model at the best price. The provider object lets you narrow or reorder that choice:
ordertries named providers first, in your order.sortranks the pool byprice,latencyorthroughput.allow_fallbackslets the next qualifying provider answer if the first one fails.data_collection: "deny"removes providers that declare they keep or train on prompts.max_pricecaps the USD per million tokens you accept, per direction.
{
"model": "deepseek/deepseek-r1",
"messages": [{ "role": "user", "content": "…" }],
"provider": {
"order": ["quarry-compute", "tidewater-labs"],
"sort": "price",
"allow_fallbacks": true,
"data_collection": "deny",
"max_price": { "prompt": 1.0, "completion": 2.0 }
}
}Decide how much proof a call needs.
Verification is the part of routing that most gateways leave out. Every provider on Verify Route is measured on three things, and a request can demand a minimum for each:
- Bond. A provider locks 10,000 USDG on Robinhood Chain before it serves live traffic. A proven failure can be slashed after a 72-hour dispute window.
- Canaries. Automated test prompts run against every served model around the clock. They check the answers, the weights fingerprint, the quantization and the empty-reply rate.
- Receipts. The provider's answer is only accepted if the router can sign a receipt for it.
Set a floor with verify in the body or the X-VR-Verify header. If no provider meets it, the request is refused with a clear error instead of being served by a weaker one, and nothing is charged.
{
"model": "meta-llama/llama-3.3-70b-instruct",
"messages": [{ "role": "user", "content": "…" }],
"verify": {
"bond_min": 10000,
"canary_min": 0.99,
"require_receipt": true
}
}Three lanes, one floor each.
A lane sets the minimum privacy a request accepts. The router never serves a request below the lane it asked for.
| Lane | Who can serve it | What you get |
|---|---|---|
| standard | Any bonded provider | TLS to the router and on to the provider, under the provider's declared data policy. |
| attested | Hosts with a fresh TEE quote | The prompt is only processed inside an enclave whose quote the router verified minutes ago. Quote hash in the receipt. |
| blind | Attested hosts, paid with blind credits | Payment cannot be tied back to a wallet. Designed in VEIL; not served yet. |
Pick a lane with a model suffix (qwen/qwen3-32b:attested), the X-VR-Lane header, or as a default on the key. The full privacy design is in VEIL.
A receipt for every answer.
Each completed call returns a receipt: which model and provider answered, on which lane, the token counts, the cost in USDG, SHA-256 hashes of the request and the response, and the attestation hash when there is one. The router signs it with Ed25519, and every hour it anchors a Merkle root of the receipts it issued on Robinhood Chain.
A receipt holds hashes, never text. Anyone holding the original request can confirm it matches; anyone else learns nothing about its content. Fetch a receipt later with GET /api/v1/receipts/{id}, and check it in your browser on the verify page.
{
"id": "rcpt_01J9Q4X2M8",
"version": 1,
"generation": "gen-2027114",
"model": "meta-llama/llama-3.3-70b-instruct",
"provider": "quarry-compute",
"lane": "attested",
"usage": { "prompt_tokens": 142, "completion_tokens": 88 },
"cost_usdg": "0.00004112",
"request_sha256": "9f2c…41ab",
"response_sha256": "0d7e…c3f9",
"attestation_sha256": "4be0…9d7f",
"bond": { "amount_usdg": 10000, "status": "active" },
"canary": { "pass_rate_24h": 1.0 },
"issued_at": "2026-09-30T09:00:31Z",
"key_id": "7f21ac03",
"sig": "ed25519:9Qx4b…Tz0="
}One key per workload.
Keys are created by a connected wallet. Each carries its own policy: a spend limit and reset period, rate limits, an allowlist of models, a minimum verification level and a default lane. The key is shown once; the router only stores its hash.
curl https://verifyroute.tech/api/v1/keys \
-H "Authorization: Bearer $VERIFYROUTE_SESSION" \
-d '{
"name": "research-agent",
"limit": 300,
"limit_reset": "monthly",
"allowed_models": ["qwen/qwen3-32b", "deepseek/deepseek-r1"],
"verify": { "bond_min": 10000 },
"lane": "standard"
}'One USDG balance.
Every key draws on the balance of the wallet that owns it. Deposits are USDG on Robinhood Chain (0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168, 6 decimals) and are credited once the transfer is final. Prices are quoted per million tokens and charged per call, with no subscription.
Unused balance can be withdrawn back to the same wallet. Your current on-chain USDG shows in the wallet menu as soon as you connect.
Pay per call, no account.
An agent does not need a key. A request without one receives 402 Payment Required with a price in USDG, a pay-to address and an expiry. The agent pays from its own wallet and retries with the proof. The receipt then names the payment transaction.
# 1. Call without a key: the router answers with a price.
POST /api/v1/chat/completions -> 402 Payment Required
{ "amount_usdg": "0.00021", "pay_to": "0x…", "chain_id": 4663, "expires_in": 120 }
# 2. Pay that amount in USDG from any wallet on Robinhood Chain.
# 3. Retry the same request with the proof.
POST /api/v1/chat/completions
X-Payment: <tx hash or signed authorization> -> 200 OK + receiptWhat comes back.
| Header | Meaning |
|---|---|
| X-VR-Receipt-Id | Id of the signed receipt for this call. |
| X-VR-Provider | Provider that served the call. |
| X-VR-Lane | Lane the call was served on. |
| X-VR-Verify | Checks that passed: bond, canary, attestation. |
| X-VR-Cost | Cost of the call in USDG. |
On a stream the same fields arrive in the final event, together with the receipt, so a client never has to make a second request to see what it paid.
SDKs you already have.
Any OpenAI-compatible client works unchanged. A dedicated @verifyroute/client package is in design: it will verify receipts locally and refuse to send a request if a provider's attestation does not check out.
from openai import OpenAI
client = OpenAI(
base_url="https://verifyroute.tech/api/v1",
api_key=os.environ["VERIFYROUTE_API_KEY"],
)
res = client.chat.completions.create(
model="mistralai/mistral-small",
messages=[{"role": "user", "content": "Hello"}],
)Show your verification.
Providers and model builders can embed a badge that reads the public registry from the visitor's browser. It shows Verified only while the checks pass, and falls back to a plain status when they do not.
Planned
The badge script and image are part of the registry stage and are not served yet.<script src="https://verifyroute.tech/badge.js"
data-endpoint="<provider id or model id>"
data-theme="light" async></script>
<!-- No scripts allowed? Use the image variant. -->
<img src="https://verifyroute.tech/badge/<provider id>.svg" alt="Verification status" />Errors that explain themselves.
| Status | Code | When |
|---|---|---|
| 400 | invalid_request | The body is not a valid chat request. |
| 401 | invalid_key | Missing, unknown or revoked key. |
| 402 | payment_required | No key and no payment proof; the body carries a price. |
| 402 | insufficient_balance | The wallet's USDG balance does not cover the call. |
| 403 | model_not_allowed | The key's allowlist excludes this model. |
| 409 | verification_unmet | No provider meets the requested bond or canary floor. |
| 429 | rate_limited | The key's rpm or tpm limit was reached. |
| 503 | no_attested_endpoint | The attested lane has no host with a fresh quote right now. |
A refused request is never charged, and it is never quietly sent to a provider below the level you asked for.
Endpoint map.
| Endpoint | Purpose |
|---|---|
| POST /chat/completions | Chat and completion calls, streaming or not. |
| GET /models | Catalog with prices, context and supported features. |
| POST /keys | Create a key with its policy. |
| GET /keys | List keys for the connected wallet. |
| GET /receipts/{id} | Fetch a signed receipt. |
| GET /providers/{id} | Bond, canary and attestation record for a provider. |
| GET /.well-known/vr-receipt-keys.json | Public keys used to sign receipts. |
The machine-readable description is at /openapi.json.
Limits and honest caveats.
- An attestation shows what code is running in an enclave. It does not prove what a provider does outside it.
- Canaries catch swapped or degraded models statistically; they are evidence, not a guarantee for each single call.
- A bond limits what a provider can lose, not what a user can lose. Disputes are decided on recorded evidence.
- Receipts prove what the router saw and signed. Their anchors on Robinhood Chain prove when.
Questions or corrections: @verifyroute on X.