Contract version 3

Human judgment your agent can call.

Use Verifi through MCP or HTTP. Send the work, receive a callback when the human answer is ready, then unlock the result through a separate second payment. Its price is fixed by when the human answered.

0.10 EURGate 1, the admission, places the request in the human queue.
Human reviewReceive a callback when the answer is ready or failed. Polling remains a recovery fallback.
2.90 or 1.45 EUROne unlock releases the verdict: 2.90 EUR if the human answered within 60 minutes, 1.45 EUR if later, within 24 hours.
Every verification is paid. Human time is never free, so every request pays the admission and one unlock. Free without a human: the docs, MCP discovery, the status endpoint, and the 402 response that lets an agent test the connection and read every price before paying. There is no cancellation. If no human answers within 24 hours, the wallet's next admission is free.
Core flow

One request, three gates, one human answer.

  1. Submit intent, claim, and the requester wallet.
  2. If gate 1 requests payment, read the terms in extensions["service-windows"], choose USDC or EURC from accepts, sign the admission, and repeat the same action.
  3. Store verify_id. Prefer callback_url for ready or failed events.
  4. If no callback is available, or delivery fails, poll while status is processing, waiting at least retry_after_seconds.
  5. If status is failed, stop and check the entry credit.
  6. If status is ready, check service_window, then call the separate unlock action.
  7. The unlock requests exactly one payment: the SLA or grace price, in the asset of the admission. Sign it and repeat the unlock.
  8. Use the answer only when status is completed.
Human work can take minutes or hours. A callback removes the need for an active polling loop. Always persist the ID so the agent can recover with get_verify or GET /verify/{verify_id}. A request can wait up to 24 hours for its human, so do not impose a short fixed client deadline.
Every price before the first payment

Three gates, two windows.

Prices are set in euros and paid on Base mainnet in USDC or EURC. The first 402 carries every price of the chain, so the agent knows the full cost before it pays anything.

GateWhenPrice (EUR basis)
1. AdmissionPOST /verify0.10
2. SLA unlockThe human answered within 60 minutes of admission2.90
3. Grace unlockThe human answered later, within 24 hours of admission1.45

The request enters the human queue only after the admission settlement has been recorded. Both windows start at that moment, admitted_at. The unlock price is fixed by the time of the answer alone, ready_at. A boundary instant belongs to the earlier, higher-priced window. When the agent polls or unlocks does not change the price.

The asset of gate 1 binds the chain: the unlock is paid in the same asset on the same network. EURC amounts are the euro prices. USDC amounts are the euro prices converted at the ECB euro reference rate and rounded up to the next cent at the moment of the quote. The conversion is disclosed in the terms and never changes after admission. With 1 EUR = 1.17 USD, a USDC chain costs 0.12, then 3.40 or 1.70.

Rates: ECB euro foreign exchange reference rates, published for information purposes only. Source: European Central Bank.

There is no cancellation. Paying the admission is a commitment: a ready result unlocks at the SLA price or at the grace price. A ready result stays unlockable at its fixed price. If no human answers within 24 hours, the chain fails and the wallet's next admission is free.

The terms in the first 402

POST /verify without a payment answers 402. The PAYMENT-REQUIRED header carries one exact option per asset, USDC first and EURC second, and the terms in the service-windows extension:

{
  "x402Version": 2,
  "accepts": [
    { "scheme": "exact", "network": "eip155:8453",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "amount": "120000",
      "extra": { "name": "USD Coin", "version": "2", "terms_id": "st_..." } },
    { "scheme": "exact", "network": "eip155:8453",
      "asset": "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42", "amount": "100000",
      "extra": { "name": "EURC", "version": "2", "terms_id": "st_..." } }
  ],
  "extensions": {
    "service-windows": {
      "info": {
        "version": 1,
        "terms_id": "st_...",
        "terms_valid_until": "2026-10-01T09:10:00.000Z",
        "windows": { "sla": { "within_seconds": 3600 }, "grace": { "within_seconds": 86400 } },
        "price_basis": { "currency": "EUR", "admission": "0.10", "sla": "2.90", "grace": "1.45" },
        "prices": [
          { "network": "eip155:8453", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
            "admission": "120000", "sla": "3400000", "grace": "1700000",
            "conversion": { "from": "EUR", "rate": "1.17", "source": "ECB euro reference rate",
                            "as_of": "2026-09-30", "rounding": "up_to_cent" } },
          { "network": "eip155:8453", "asset": "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42",
            "admission": "100000", "sla": "2900000", "grace": "1450000" }
        ],
        "asset_binding": "admission_asset",
        "expiry": { "admission_credit": "next_admission" },
        "cancellation": "none",
        "status_url_template": "https://verifi.cloud/verify/{work_id}",
        "unlock_url_template": "https://verifi.cloud/verify-unlock?id={work_id}"
      }
    }
  }
}

Amounts are strings in atomic units, 6 decimals for both assets. A quote can be paid until terms_valid_until, about ten minutes. A payment signed against an expired quote is not settled: the answer is a fresh 402 with fresh terms, and the agent signs again.

Recommended for agents

Agents work through MCP.

Connect to https://verifi.cloud/mcp. The same tools handle every paid review, and discovery plus the price check cost nothing.

No API key, signup, account, or whitelist. Pass the requester's Base-compatible wallet address. It signs the x402 payments without exposing its private key.
verify_claimSubmit the request, optionally register a callback, and pass gate 1.
get_verifyPoll the request without payment.
unlock_verifyPay the SLA or grace unlock and receive the answer.
verifi_infoRead the service rules and the current terms.

For a paid gate, the tool returns the standard x402 requirement understood by x402-aware MCP clients, with the terms in extensions:

{
  "x402Version": 2,
  "accepts": [
    { "scheme": "exact", "amount": "120000", "network": "eip155:8453", "extra": { "name": "USD Coin" } },
    { "scheme": "exact", "amount": "100000", "network": "eip155:8453", "extra": { "name": "EURC" } }
  ],
  "extensions": { "service-windows": { "info": { "terms_id": "st_..." } } },
  "resource": { "url": "mcp://tool/verify_claim" }
}

The MCP payment client signs the selected requirement with the requester wallet and repeats the same tool call using x402/payment metadata. A successful call returns x402/payment-response with the settlement receipt. Generic MCP clients can use the optional payment_signature argument instead.

A payment request looks like an error, by spec. The x402 MCP transport requires the requirement to arrive with isError: true, in both structuredContent and content[0].text. It means the tool was called without payment, not that the tool failed. Before treating isError as a failure, check whether structuredContent carries x402Version: that is what separates "asked to pay" from "genuinely broken". The resource.url in that result is always the public address, never an internal one.
Never send a private key. Verifi receives only the signed, single-use x402 authorization. The admission and the unlock require separate signatures tied to the same verify_id.
Gate 1

Submit a request

HTTP endpoint: POST https://verifi.cloud/verify

FieldTypeMeaning
intentstring, max 2000What the agent is trying to do
claimstring, max 4000What the human should verify or answer
agent_id0x wallet addressRequester wallet and quota identity
callback_urlHTTPS URL, optionalReady or failed notification
curl -X POST https://verifi.cloud/verify \
  -H "Content-Type: application/json" \
  -d '{
    "intent": "refund decision",
    "claim": "Read the policy and write which exception applies to order 482.",
    "agent_id": "0xYourWalletAddress",
    "callback_url": "https://your-agent.example/verifi-events"
  }'

An admitted request returns HTTP 202 with status: processing, a durable verify_id (also returned as work_id), contract_version: 3, retry_after_seconds, and the terms bound to the chain:

{
  "verify_id": "a1b2c3d4-0000-4000-8000-000000000000",
  "work_id": "a1b2c3d4-0000-4000-8000-000000000000",
  "contract_version": 3,
  "status": "processing",
  "next_action": "poll",
  "retry_after_seconds": 15,
  "admitted_at": null,
  "sla_deadline": null,
  "grace_deadline": null,
  "terms": {
    "terms_id": "st_...",
    "network": "eip155:8453",
    "asset": "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42",
    "asset_symbol": "EURC",
    "admission": "100000", "sla": "2900000", "grace": "1450000",
    "windows": { "sla": { "within_seconds": 3600 }, "grace": { "within_seconds": 86400 } }
  },
  "funding": {
    "entry_source": "x402",
    "entry_amount_atomic": "100000",
    "entry_charged_atomic": "100000",
    "unlock_amount_atomic": "2900000",
    "entry_charged_usdc": null
  }
}

The admission payment settles while the response is sent, so on the 202 of a paid admission admitted_at and both deadlines are still null. They appear on the first poll. An admission paid with a credit has them at once. The *_usdc funding fields are null on an EURC chain. The atomic amount fields are always present. Until the human answers, unlock_amount_atomic shows the SLA amount, the most the unlock can cost.

Entry sourceAdmissionUnlock
x402PaidPaid, SLA or grace price
failure_creditFreePaid, SLA or grace price
initial_freeFreeFree (chains from before contract 3 only)
Recommended for unattended agents

Let Verifi tell your agent when to continue

Pass an HTTPS callback_url with verify_claim or POST /verify. Verifi sends verify.ready when the unlock can be called, or verify.failed when the chain must stop. A ready callback carries ready_at, service_window, and the unlock price.

{
  "event": "verify.ready",
  "verify_id": "a1b2c3d4-0000-4000-8000-000000000000",
  "work_id": "a1b2c3d4-0000-4000-8000-000000000000",
  "contract_version": 3,
  "status": "ready",
  "next_action": "unlock",
  "unlock_url": "https://verifi.cloud/verify-unlock?id=a1b2c3d4-0000-4000-8000-000000000000",
  "ready_at": "2026-10-01T09:41:12.000Z",
  "service_window": {
    "applied": "sla",
    "sla_deadline": "2026-10-01T10:00:00.100Z",
    "grace_deadline": "2026-10-02T09:00:00.100Z",
    "network": "eip155:8453",
    "asset": "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42",
    "unlock_amount": "2900000"
  }
}
No active polling loop is required. Store verify_id before yielding. Verifi makes up to three delivery attempts. Treat the callback as a wake-up signal, then continue with unlock. If delivery fails, recover with get_verify or GET /verify/{verify_id}. The callback never includes the locked human answer.
No charge

Poll the same request

Use MCP get_verify or HTTP GET /verify/{verify_id} when no callback is available or as recovery after a delivery failure. Polling never costs money.

StatusMeaningNext action
processingAdmission is settling or a human is workingWait for callback, or poll to recover
readyThe answer exists but is lockedCall unlock
failedNo answer can be unlockedStop
completedThe answer is visibleUse the result

A processing response carries admitted_at, sla_deadline, and grace_deadline, so the agent can plan its polling. expires_at equals the last deadline. A ready response contains no verdict, but it states exactly what the unlock costs and why:

{
  "verify_id": "a1b2c3d4-0000-4000-8000-000000000000",
  "work_id": "a1b2c3d4-0000-4000-8000-000000000000",
  "contract_version": 3,
  "status": "ready",
  "verdict": null,
  "explanation": null,
  "admitted_at": "2026-10-01T09:00:00.100Z",
  "ready_at": "2026-10-01T13:12:40.000Z",
  "service_window": {
    "applied": "grace",
    "sla_deadline": "2026-10-01T10:00:00.100Z",
    "grace_deadline": "2026-10-02T09:00:00.100Z",
    "network": "eip155:8453",
    "asset": "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42",
    "unlock_amount": "1450000"
  },
  "next_action": "unlock",
  "unlock_url": "https://verifi.cloud/verify-unlock?id=a1b2c3d4-0000-4000-8000-000000000000",
  "unlock": {
    "method": "POST",
    "price_usdc": null,
    "payment_required": true,
    "asset_symbol": "EURC",
    "amount_atomic": "1450000",
    "amount_decimal": "1.45",
    "applied_window": "grace"
  }
}

On a USDC chain unlock.price_usdc is the decimal USDC price, for example "3.40".

Gate 2 or gate 3

Unlock the human answer

Use MCP unlock_verify or HTTP POST /verify-unlock?id={verify_id} only after status becomes ready. Before that the endpoint answers 409 and asks for no payment.

Without ?id= naming a chain, or with an unknown id, the endpoint answers 402 with the unlock price band: every accepted asset at both the SLA and the grace price. That 402 only describes the prices. A payment made against it is refused with 400 or 404 and never settled.

The unlock returns exactly one exact requirement: the asset the admission was paid in, at service_window.unlock_amount. Its extensions["service-windows"].info repeats terms_id, applied, admitted_at, and ready_at, so the agent can check the arithmetic against the terms it paid for at admission. Refuse to sign if the asset differs or the amount is higher than the bound amount for the applied window. An admission credit never covers this gate.

{
  "verify_id": "a1b2c3d4-0000-4000-8000-000000000000",
  "status": "completed",
  "verdict": "refined",
  "explanation": "Use the corrected delivery date: 22 July.",
  "response": "Use the corrected delivery date: 22 July.",
  "next_action": "done",
  "funding": {
    "entry_charged_atomic": "100000",
    "unlock_window": "grace",
    "unlock_charged_atomic": "1450000",
    "total_charged_atomic": "1550000"
  }
}

A completed chain returns its result again without a further charge.

The human verdict is true, false, or refined. A refined free-text answer is returned in explanation and response.

Payment

x402 on Base

All gates use x402 v2, scheme exact, on Base mainnet eip155:8453, with USDC (0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) or EURC (0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42). Both tokens support EIP-3009, and the EIP-712 domain name and version are in each requirement's extra. The buyer needs USDC or EURC but no ETH.

HTTP returns a base64 PAYMENT-REQUIRED header and accepts the signed authorization in PAYMENT-SIGNATURE. MCP returns a standard PaymentRequired result and accepts the signed payload through x402/payment metadata. Successful settlement returns PAYMENT-RESPONSE over HTTP or x402/payment-response through MCP.

The admission and unlock payments are separate EIP-3009 authorizations and separate on-chain settlements connected to the same verify_id.

Automatic HTTP payment with @x402/fetch

The x402 client reads the 402 requirement, creates the EIP-3009 authorization with the requester's wallet, and retries the same request automatically. Without a custom selector it pays with the first option, USDC. Install @x402/core, @x402/fetch, @x402/evm, and viem:

npm install @x402/core @x402/fetch @x402/evm viem
import { wrapFetchWithPayment } from "@x402/fetch";
import { x402Client } from "@x402/core/client";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";

const signer = privateKeyToAccount(
  process.env.EVM_PRIVATE_KEY as `0x${string}`
);
// To pay in EURC instead, pass a selector:
// new x402Client((_v, accepts) => accepts.find((a) => a.extra?.name === "EURC") ?? accepts[0])
const client = new x402Client();
client.register("eip155:*", new ExactEvmScheme(signer));
const paidFetch = wrapFetchWithPayment(fetch, client);

const response = await paidFetch("https://verifi.cloud/verify", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({
    intent: "Review this customer update",
    claim: "Check the promise and write a safer version.",
    agent_id: signer.address,
    callback_url: "https://your-agent.example/verifi-events"
  })
});

const verify = await response.json();

Use the same paidFetch for POST /verify-unlock?id={verify_id}. It handles the separate unlock requirement, in the asset of the admission, when the callback reports ready. See the official x402 buyer quickstart.

Fair failure handling

Failure and entry credit

A chain nobody answers within 24 hours of admission fails with reason human_timeout:

{
  "verify_id": "a1b2c3d4-0000-4000-8000-000000000000",
  "status": "failed",
  "next_action": "stop",
  "failure": {
    "reason": "human_timeout",
    "entry_credit_granted": true,
    "entry_credit": "next_admission",
    "entry_credit_value_usdc": null
  }
}

To spend a credit, the wallet signs the ordinary 402 for gate 1. Verifi checks that the signature comes from agent_id, uses the credit, and never submits the payment, so no funds move. A signature from any other wallet is settled as a normal payment, so nobody can spend another wallet's credit.

The credit is not cash. It pays only the admission of the same wallet's next request, which is then bound to the same asset as the expired chain. The unlock of that next request still costs the SLA or grace price when its answer is ready. entry_credit_value_usdc is the admission price on a USDC chain and null on an EURC chain.

Reliability

Callbacks, errors, and audit

An optional callback sends verify.ready or verify.failed. It replaces active polling during normal operation, never exposes a locked answer, and leaves polling available as the recovery path if delivery fails.

HTTP statusMeaning
400Invalid input or wallet address
402This gate requires an x402 payment, or the paid quote expired: sign the fresh terms
409The lifecycle action is not currently valid, for example unlock before ready, or the paid terms could not be bound
429The wallet already has an active review
503The human queue is full, no quote can be issued right now, or paid routes are unavailable. Retry after the advertised delay

A refused request is never charged. The active-request check runs before payment. Capacity and request validation run after it, because x402 cancels settlement for any 4xx or 5xx answer, so an unpaid caller always sees a real 402 first. The human queue has a hard cap: a full queue answers 503 after the payment gate, and nothing is charged. PostgreSQL records the wallet, request, every quote and ECB rate used, the bound terms, the applied window, charged amounts in the bound asset, both transaction hashes, free uses, credits, lifecycle timestamps, and the append-only audit trail.