# Unfret > Pay-per-check risk signals for AI shopping agents: is this store or product listing showing signs of scams, > dropshipping of generic goods, fake reviews or dark patterns? Returns evidence and an action, never a "scam" label. ## Endpoint POST https://dev.unfret.net/v1/check with JSON {"url": "...", "page_content": " (optional)", "tier": "quick|deep", "policy": "strict|balanced|lenient"}. Send page_content for marketplaces (Amazon blocks server-side fetches). ## Tiers - quick ($0.01): Quick first-pass filter: rule-based checks of the page itself (discount tricks, dropshipping and review-import apps, countdown timers and pre-ticked add-ons, hidden text aimed at AI, missing legal information, shipping and return terms, shops sharing accounts). No AI read of the page, no reviews, photo, domain age or registry lookups. Use it to screen many pages; it misses what only reading the listing, its reviews or its photos shows, so check what matters with deep. A page without a product and a price can't be assessed by quick (you are refunded). - deep ($0.20): Full check: everything in quick, plus an AI read of the listing, the reviews, the product photo in our image index, the price history, domain age and the EU VAT register. The same check a shopper gets. ## Payment (x402 v2) The first call answers 402 with a PAYMENT-REQUIRED header listing the options: wSOL (wrapped SOL) and USDC. Pay one with the `exact` scheme on Solana (network solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1, i.e. solana-devnet; the same CAIP-2 identifier as `accepts[].network`) and retry with PAYMENT-SIGNATURE. Prices are in US dollars: quick = $0.01, deep = $0.20. USDC (6 decimals) pays exactly that; the wSOL amount (9 decimals; `extra.decimals` in each option) is the dollar price converted at quote time with Pyth's SOL/USD price (wSOL isn't offered while the oracle is stale). A wSOL payment may differ from the quote by up to `extra.quoteToleranceBps` (200 bps); the price moves, so re-fetch the 402 right before paying. Standard wallets and smart wallets (Squads and other program-controlled wallets paying by CPI) both work, as long as the transaction carries one memo and pays the quoted amount into payTo's token account for that mint (all rules: `extra.constraints` in each option). Unfret is the fee payer, so you only pay the price. `extra.constraints.maxPriorityFeeLamports` caps the priority fee you may set; it is in lamports (SOL) on every option, USDC included, because the fee is paid in SOL: a share of the tier's dollar price converted at the SOL/USD quote (`extra.feeCapNote`). A refused payment answers 402 again with `error` (why) and, when the payment's simulation failed, `reason` (e.g. `insufficient_funds`, `source_token_account_missing`, `blockhash_not_found`). A page we can't fetch costs nothing. Retrying with the same PAYMENT-SIGNATURE returns the same answer and is never charged twice. ## Response {"action": "allow|warn|block", "risk_score": 0..1, "confidence": 0..1, "flags": [{"type", "evidence", "weight"}], "policy_applied", "tier", "cached", "checked_at", "model_version", "band"}. "warn" means: ask your human before buying. Under the balanced and lenient policies you are never told to warn about a page a shopper would be shown as "reliable" or "concerns" (the band); choose strict to be warned about those too. ## Errors Every error is JSON with `detail` and `error_code`. | error_code | HTTP | charged | what to do | | --- | --- | --- | --- | | `PAYMENT_REQUIRED` | 402 | no | pay one of `accepts` and retry with PAYMENT-SIGNATURE; `error` says why (`reason`: why a simulation failed) | | `INVALID_PAYMENT` | 400 | no | the PAYMENT-SIGNATURE header couldn't be decoded | | `VALIDATION_ERROR` | 422 | no | fix the request body (see `errors`) | | `PAGE_UNAVAILABLE` | 422 | no | the page couldn't be fetched: send page_content | | `PAGE_BLOCKED` | 422 | no | the page is a bot check: send the real page as page_content | | `RATE_LIMITED` | 429 | no | wait Retry-After seconds | | `PRICE_UNAVAILABLE` | 503 | no | nothing can be priced right now: retry after Retry-After | | `SETTLEMENTS_PAUSED` | 503 | no | too many failed settlements: retry after Retry-After | | `SERVICE_UNAVAILABLE` | 503 | no | payments or analysis are off: retry later | | `SETTLEMENT_FAILED` | 402 | no | the network refused the payment: pay again | | `SETTLEMENT_PENDING` | 402 | maybe | don't pay again; if it lands within 2 min its price is credited | | `SETTLEMENT_MISMATCH` | 402 | yes | it landed differently than simulated: no verdict (alerted) | | `GATEWAY_TIMEOUT` | 504 | refunded | the analysis timed out: the price is credited to the paying wallet | | `INTERNAL_ERROR` | 500 | refunded | the analysis failed: the price is credited to the paying wallet | ## Shared results (shoppers) A shopper's result from POST /v1/analyze carries a share_token. POST /v1/shares/preview, then POST /v1/shares, with {"share_token", "url", "result"} and without Authorization, make the verdict public at /r/ for 90 days (no score, no quotes from the page, nothing about who checked); DELETE /v1/shares/ with X-Delete-Code removes it. Shared pages are noindex. Don't present a shared result as Unfret's own judgement of the shop. ## Machine-readable https://dev.unfret.net/.well-known/x402