Choose a check
| Your task | Endpoint | What it does | USDC |
|---|---|---|---|
| Screen a message or URL for scam indicators | POST /checks/message | Model + deterministic checks; optional LLM review | 0.01 / 0.10 with LLM |
| Screen a screenshot of a conversation or offer | POST /checks/screenshot | Same scam checks, using your transcript plus QR extraction; no OCR | 0.01 / 0.10 with LLM |
| Investigate an offer, claim, or claimed source | POST /checks/reality | LLM research and evidence assessment across text, URLs, and media | 0.50 |
| Retrieve a submitted check | GET /checks/{check_id} | Wallet-authenticated status and result | Free |
Scam: “Does this message show fraud or manipulation indicators?”
Reality: “Is this offer or source legitimate, given this evidence?” A related batch returns one combined assessment. Media analysis is not a forensic authenticity guarantee.
All paths above use the /api/v1 prefix. Prices are per check. LLM means large language model; OCR means extracting text from an image.
Getting started
New to x402? It’s an HTTP payment protocol: the API returns 402 Payment Required with a price, and your client signs a payment and retries. Common Defense uses it to charge per check in USDC on Base.
- Official x402 documentation — protocol concepts and reference.
- Buyer quickstart — build a client that pays for API requests.
- SDKs and examples — upstream implementations.
Use those guides for payment setup, then follow the wallet-signing and request-ID requirements below for this API.
API origin: https://app.commondefense.ai
Requirements: A wallet controlled by a private key (EOA; smart-contract wallets are not supported), USDC on the Base network (eip155:8453), and an x402 v2 client. Use its EVM exact payment scheme and the payment-identifier and Sign-In-With-X (SIWX) extensions. SIWX proves you control the wallet, so you can retrieve your results without paying again.
1. Create a check request
Choose an endpoint and build its request body. For example, to screen a message:
const body = JSON.stringify({
request_id: crypto.randomUUID(),
text: "Send a deposit now to claim your prize.",
mode: "standard"
});
Generate request_id once per check and save this body before sending. It is a UUID used as an idempotency key: retrying with the same ID and body retrieves the original check instead of charging again. A new check gets a new ID; a retry keeps the original.
2. Authorize payment and submit
- POST the saved body to
/api/v1/checks/messagewithout payment. Read the 402PAYMENT-REQUIREDheader; the SDK decodes its payment requirements. - Validate Base (
eip155:8453), the USDC asset, recipient, and price. Sign payment with the x402 SDK. Set its payment-identifier extension’sinfo.idto the same UUID asrequest_id—no second ID is needed. - POST the same bytes with
PAYMENT-SIGNATUREand freshSIGN-IN-WITH-X. Receive 202 + check_id, not a verdict.
3. Retrieve the result
The API returns a check_id, separate from your client-generated request_id. Use it to GET /api/v1/checks/{check_id} with fresh SIWX every few seconds. Read result when status == "complete". Polling is free.
Checks, LLM calls, and paid transcription start only after payment is confirmed. Validation and local media preparation happen before payment. Keep your private key client-side.
Request bodies
These are JSON bodies for your x402 client, not standalone requests. Replace example IDs and base64 placeholders. Unknown fields are rejected.
Message · POST /api/v1/checks/message
{
"request_id": "c321be46-70b3-48da-ab59-c964b5292075",
"text": "Send a deposit now to claim your prize.",
"mode": "llm"
}
text is required. Omit mode for the 0.01 USDC standard check. Standard checks make no hosted LLM call; an inconclusive result may require review.
Screenshot · POST /api/v1/checks/screenshot
{
"request_id": "84cbd78a-9db5-4ef1-85b2-3857363ca45c",
"image_base64": "<base64 image bytes>",
"transcript": "Send a deposit now to claim your prize.",
"text": "Optional context",
"mode": "standard"
}
Image and caller-supplied transcript (the text visible in the screenshot) are required. Encode the image bytes as base64 without a data:image/...;base64, prefix. Same mode options as message checks.
Reality · POST /api/v1/checks/reality
{
"request_id": "c662389e-2957-4a37-9699-ad9cfa6e50fc",
"text": "Is this offer from the organization it claims to represent?",
"attachments": [
{
"file_name": "offer.pdf",
"mime_type": "application/pdf",
"data_base64": "<base64 file bytes>"
}
]
}
Provide text, attachments, or both. Put research URLs in text; attachments contain bytes, not download URLs. Supports images, PDFs, audio/video, and related batches. No mode field. 0.50 USDC for the whole request.
Sign each request
Use the x402 SDK’s SIWX helpers to create and encode the wallet proof in SIGN-IN-WITH-X. On Base, this uses Sign-In With Ethereum (SIWE) and EIP-191 signatures. Use the same wallet that pays for the check. SIWX is an optional x402 extension that this API requires for paid submissions, recovery, and result retrieval.
| Proof field | Value |
|---|---|
domain / uri | API host / exact HTTPS origin + path |
version / type | "1" / "eip191" |
chainId | "eip155:8453" (Base) |
issuedAt | Current UTC timestamp; valid within five minutes |
nonce | Fresh alphanumeric value, 8–64 characters; single use |
requestId | Lowercase SHA-256 below; not the JSON UUID |
import hashlib
digest = hashlib.sha256(
method.upper().encode() + b"\n" + path.encode() + b"\n" + body_bytes
).hexdigest()
The SIWX requestId above is a hash of this HTTP request, not your check’s UUID. This method/body binding is specific to our API. Use the full path including /api/v1, an empty body for GET, and no query string. Sign the bytes you send.
Keep the check’s UUID; refresh the proof. Every retry and poll needs a new signature with a fresh nonce (a single-use random value) and timestamp.
Results and recovery
settling (confirming payment) → pending (queued) → processing (running) → complete
GET returns 200 even while processing. Inspect status; the completed assessment is in result. The envelope also includes check_id, error, transaction, amount_usdc, and result_expired.
| Outcome | Client action |
|---|---|
complete | Read result; if result_expired: true, it has been removed |
payment_failed | Authorization expired without settlement; a new purchase is allowed |
refund_required | Contact support with check ID + transaction hash; refund is manual, not already sent |
Timeout / prolonged settling | Recover the existing request; do not create another purchase |
Recovery: resend the same POST body and UUID with fresh SIWX, without a new payment signature. An existing check returns 200 if complete, otherwise 202. Never change the UUID just because a response was lost.
Input limits
| Input | Limit |
|---|---|
| Text / screenshot transcript | 50,000 characters each |
| Screenshot | 10 MiB decoded; 14 MiB request body |
| Reality attachments | 5 files; 25 MiB combined decoded by default; 32 MiB request body |
| Reality prepared evidence | 20 image parts; 50 pages/PDF; 100,000 combined characters |
| Audio/video | 10 minutes combined decoded audio |
| Wallet concurrency / rate | 2 active checks; 30 purchase admissions/min; 120 polls/min |
Requests are validated before payment; these size limits do not guarantee acceptance. Additional ingress and media-preparation limits apply.
Errors
| HTTP | Action |
|---|---|
| 401 | Supply a fresh signature from the purchasing wallet |
| 404 | For GET, verify the check ID and wallet; for POST, the paid API may not be enabled |
| 402 | Read payment challenge; if PAYMENT-REQUIRED is absent, recover the existing request |
| 409 | Fresh nonce for retries; unchanged body/UUID for recovery; changed content needs a new check |
| 413 | Reduce the request size |
| 422 | Check the response detail for invalid or unsupported input |
| 429 | Back off before retrying; preserve the request ID and use a fresh signature |
| 503 | Service unavailable; preserve the request ID for recovery |
Retention
Inputs are cleared on completion or failure. All results, including safe verdicts, expire 30 days after completion; cleanup sets result: null and result_expired: true on completed checks. Payment/replay metadata is retained indefinitely. Privacy policy.
