API documentation

Base URL: https://api.inboxpolicy.com · Auth: x-api-key: ip_live_... header, or pay per call with x402.

Updated 2026-07-12 · llms.txt

Quickstart: make one paid request in five minutes

Prerequisites: bring an EVM wallet funded with USDC on Base and an x402 v2 client. No account or API key is required. The optional X-InboxPolicy-Payer header asks for that wallet's rolling 30-day usage rate; the server binds the short-lived quote to the same wallet that signs. The client preserves the quote extension, signs it, retries with PAYMENT-SIGNATURE, and returns the paid decision.

import { x402Client } from "@x402/core/client";
import { encodePaymentSignatureHeader } from "@x402/core/http";
import { toClientEvmSigner } from "@x402/evm";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { createPublicClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";

const account = privateKeyToAccount(process.env.WALLET_KEY);
const publicClient = createPublicClient({ chain: base, transport: http() });
const x402 = new x402Client();
registerExactEvmScheme(x402, {
  signer: toClientEvmSigner(account, publicClient),
  networks: ["eip155:8453"]
});

const request = {
  method: "POST",
  headers: { "content-type": "application/json", "X-InboxPolicy-Payer": account.address },
  body: JSON.stringify({ email: "[email protected]" })
};
let res = await fetch("https://api.inboxpolicy.com/v1/decide", request);
if (res.status === 402) {
  const payment = await x402.createPaymentPayload(await res.json());
  res = await fetch("https://api.inboxpolicy.com/v1/decide", {
    ...request,
    headers: { ...request.headers, "PAYMENT-SIGNATURE": encodePaymentSignatureHeader(payment) }
  });
}
console.log(await res.json()); // { action: "send", confidence: 95, ... }

Wallet usage rates: the standard single-call rate is $0.01. A wallet receives 20% off the current single or batch base quote after 1,000 settled units in the prior 30 days and 40% off after 10,000. Omit X-InboxPolicy-Payer to use the standard quote.

Prefer an API key? Buy a prepaid credit pack (card checkout) — your key arrives by email. With a key, send x-api-key instead of paying per call:

curl -X POST https://api.inboxpolicy.com/v1/decide \
  -H "x-api-key: ip_live_..." \
  -H "content-type: application/json" \
  -d '{"email": "someone@example.com"}'

POST /v1/decide

Decision-shaped verification: for a schema-valid request body, answers with one action — send, send_with_caution, review, retry_later, or avoid — plus reason, confidence, signals, and cost. A syntactically invalid email string in a schema-valid body returns avoid at no charge; schema-invalid bodies can fail validation. Unknown addresses resolve to review; catch-all addresses resolve to review by default or send_with_caution under aggressive strictness — see how to handle catch-all email addresses, or browse the send-decision benchmark for 40 worked input-to-decision scenarios.

{
  "request_id": "vrf_...",
  "action": "send",
  "reason": "Verified mailbox with acceptable risk.",
  "confidence": 95,
  "status": "valid",
  "signals": { "catch_all": false, "disposable": false, "free": false, "role_account": false },
  "from_cache": false,
  "cost": { "credits": 1, "billable": true }
}

POST /v1/verify

Full verification detail: status, deliverability and reachability flags, catch-all/disposable/role signals, MX context, confidence, recommended action, and normalized signals. Malformed input returns 422. Same request body as /v1/decide: {"email", "strictness?", "force_refresh?"} with strictness one of aggressive | default | strict.

Batches

POST /v1/batches/verify

Async verification for up to 50,000 emails: {"emails": [...], "strictness?", "webhook_url?"}. Returns 202 with a batch_id, worst-case credit estimate, and a webhook_secret when a webhook URL was given (completion webhooks are signed x-inboxpolicy-signature: HMAC-SHA256(secret, body)). Duplicate emails are deduplicated internally and billed once.

Batches via x402 (keyless)

The same endpoint also accepts x402 payment — no account or key needed. The x402 path caps at 5,000 emails per call (the API-key path keeps its 50,000 cap). Without a PAYMENT-SIGNATURE header the API replies 402 with a PAYMENT-REQUIRED quote for emails × the per-email bulk rate, in USDC; a v2 x402 client reads the quote, signs, and retries automatically.

curl -i -X POST https://api.inboxpolicy.com/v1/batches/verify \
  -H "content-type: application/json" \
  -d '{"emails": ["a@x.com", "b@y.com"], "strictness": "default"}'
# 402 Payment Required — quotes total = emails x per-email bulk rate, in USDC

Once payment settles, the same call returns 202:

{
  "batch_id": "bat_...",
  "status": "queued",
  "total_items": 2,
  "amount_usd": 0.02,
  "access_token": "btok_..."
}

Poll keylessly with that token: GET /v1/batches/:batchId and GET /v1/batches/:batchId/items with header x-batch-access-token: btok_... — same response shapes as the API-key path. webhook_url works the same way. For bigger lists, chunk client-side and submit multiple calls; each payment covers exactly the emails submitted in that call, and duplicates within a call are charged as submitted.

GET /v1/batches/:batchId

Status and summary counts: total_items, processed_items, valid/invalid/unknown/error counts, timestamps.

GET /v1/batches/:batchId/items

Per-email results, paginated: ?status=queued|processing|completed|failed&limit=100&offset=0.

GET /v1/batches

Your recent batch jobs, newest first.

Usage & account

GET /v1/usage — remaining credits, month-to-date spend and request counts. GET /v1/requests — recent verification activity. POST /v1/estimate — credit estimate before an operation: {"operation": "verify_email|verify_batch|send_decision", "count": n}.

Billing rules

Idempotency

For authenticated API-key calls, send an idempotency-key header on /v1/verify or /v1/decide. Keyless x402 bypasses this wrapper; after indeterminate settlement, do not submit a second payment. Exactly one concurrent request executes; others replay the stored response or receive 409 while it is in flight. Reusing a key with a different payload returns 409. A crashed owner releases the key after ~5 minutes.

MCP server (agents)

Remote MCP over Streamable HTTP. Tools: decide_send, verify_email, estimate_credits, create_verify_batch, get_batch_status, get_batch_items, get_usage.

{
  "mcpServers": {
    "inboxpolicy": {
      "url": "https://mcp.inboxpolicy.com",
      "headers": { "x-api-key": "ip_live_..." }
    }
  }
}

x402 pay-per-call

No account needed: keyless requests to /v1/decide or /v1/verify return 402 with an x402 v2 PAYMENT-REQUIRED challenge (standard rate $0.01 per call, USDC on Base). Send X-InboxPolicy-Payer to receive a wallet-bound rolling-usage quote: 20% off after 1,000 settled units in 30 days and 40% off after 10,000. Retry with a PAYMENT-SIGNATURE header; the settlement receipt returns in PAYMENT-RESPONSE. Credit packs provide fixed card pack pricing ($3.16–$5.00 per 1,000); model your own list with the cost calculator. Batches accept x402 too — one payment per call, up to 5,000 emails, with the earned percentage applied to the current per-email base quote.

Errors & limits

CodeMeaning
400Validation failure (body includes an issues array)
401Missing or invalid API key
402Insufficient credits, or x402 payment required (keyless)
404Unknown batch ID
409Idempotency conflict or in-flight duplicate
422Email failed syntax validation (verify only)
429Rate limited (default 10 req/s per key)