# truval.dev LLM Full Reference Canonical docs for AI agents integrating truval.dev email verification via HTTP API or MCP. Primary API base: https://api.truval.dev HTTP OpenAPI: https://api.truval.dev/openapi.json Postman Collection: https://www.postman.com/truval-dev/workspace/truval-public/collection/38452101-a5b24567-8901-2345-6789-012345678901 MCP endpoint: https://mcp.truval.dev/mcp MCP tool schema endpoint: https://mcp.truval.dev/openapi.json ## 1) Authentication Required on all API and MCP requests: - Authorization: Bearer sk_live_... (or sk_test_... in non-prod) - Management API Authorization: Bearer sk_mgmt_... For HTTP API requests: - Content-Type: application/json Get keys at: - https://dash.truval.dev ## 2) HTTP API Contract Endpoint: - POST https://api.truval.dev/v1/email/verify Request JSON schema (sync or async): { "type": "object", "required": ["email"], "properties": { "email": { "type": "string", "description": "Email address to verify" }, "webhook": { "type": "string", "description": "Optional public HTTPS URL. If set, the API returns 202 { job_id, status: pending } immediately and POSTs the full result to this URL when verification completes (SSRF-safe rules apply — see docs)." }, "webhook_secret": { "type": "string", "description": "Optional shared secret for webhook signing. When provided, truval.dev includes X-Truval-Signature: sha256=... on the webhook callback (HMAC-SHA256 over the raw JSON request body)." } }, "additionalProperties": false } Request example (sync — omit webhook): { "email": "user@example.com" } Async example (202 + background callback): { "email": "user@example.com", "webhook": "https://your-agent.com/truval-callback", "webhook_secret": "whsec_..." } Success response JSON schema: { "type": "object", "required": [ "email", "valid", "status", "confidence", "failed_check", "disposable", "role", "free_provider", "catch_all", "smtp_blocked", "mx_found", "mx_host", "suggestion", "latency_ms" ], "properties": { "email": { "type": "string" }, "valid": { "type": "boolean" }, "status": { "type": "string", "enum": ["deliverable", "undeliverable", "unknown", "catch_all", "invalid"] }, "confidence": { "type": "number", "minimum": 0, "maximum": 1 }, "failed_check": { "anyOf": [ { "type": "string", "enum": ["syntax", "disposable", "no_mx", "smtp", "smtp_timeout"] }, { "type": "null" } ] }, "disposable": { "type": "boolean" }, "role": { "type": "boolean" }, "free_provider": { "type": "boolean" }, "catch_all": { "type": "boolean" }, "smtp_blocked": { "type": "boolean" }, "mx_found": { "type": "boolean" }, "mx_host": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "suggestion": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "latency_ms": { "type": "number" } } } Example success response: { "email": "user@example.com", "valid": true, "status": "deliverable", "confidence": 0.97, "failed_check": null, "disposable": false, "role": false, "free_provider": false, "catch_all": false, "smtp_blocked": false, "mx_found": true, "mx_host": "mail.example.com", "suggestion": null, "latency_ms": 187 } ## 2a) Async webhook (optional) If `webhook` is present in the body, the API responds with **202 Accepted** and JSON `{ "job_id": "...", "status": "pending" }` immediately. When verification finishes, the API **POSTs** the full `VerifyEmailResult` (plus `job_id`) to the webhook URL. URL rules: HTTPS only, hostname (not IP), no userinfo, no localhost / `*.local`; callbacks do not follow redirects. See `https://docs.truval.dev/api/email-verify#webhook`. Webhook signing (recommended): - If `webhook_secret` is provided, the callback includes: - `X-Truval-Signature: sha256=` - Compute `expected = "sha256=" + HMAC_SHA256(webhook_secret, raw_request_body)` and compare using a constant-time comparison. ## 2b) npm TypeScript/JavaScript SDK Install: `npm install truval` or `pnpm add truval` The `truval` package wraps the HTTP API for Node and modern runtimes with `fetch`. Import: ```ts import { createClient, createManagementClient, verify } from 'truval' ``` ### createClient(apiKey, options?) Options (all optional): - `baseUrl` — API origin without trailing slash (default `https://api.truval.dev`) - `retryOnRateLimit` — when `true`, on HTTP 429 the client waits until the response JSON `reset_at` (ISO timestamp) **plus a small cushion** (SDK adds **50ms**), then retries (default `false`) - `maxRetries` — max **retry attempts** after a 429 when `retryOnRateLimit` is enabled (default `3`) ### Methods - **`client.verify(email)`** — `POST /v1/email/verify`; returns one `VerifyResult` object (same shape as the JSON schema above). - **`client.verifyAsync(email, webhook)`** — same endpoint with `webhook` set; returns `{ job_id, status: 'pending' }` (HTTP 202); result is delivered to your HTTPS callback when ready. - **`client.verifyBatch(emails)`** — `POST /v1/email/verify/batch` in chunks of **50** addresses. Chunks are sent **sequentially** (one batch request after another), not in parallel, to reduce rate-limit spikes; results are concatenated in **input order**. Example: 200 emails → 4 API calls in series. - **`client.verifyStream(emails)`** — `POST /v1/email/verify/stream`; returns an **async iterable** of `VerifyResult` (NDJSON lines). Emission order is **completion order** and may differ from `emails` order. - **`verify(email, apiKey, options?)`** — convenience for a single check without holding a client; same options as `createClient`. ### Example ```ts import { createClient } from 'truval' const truval = createClient(process.env.TRUVAL_API_KEY!, { retryOnRateLimit: true, maxRetries: 3, }) const single = await truval.verify('user@example.com') const batch = await truval.verifyBatch(['a@b.com', 'c@d.com']) for await (const row of truval.verifyStream(['a@b.com', 'c@d.com'])) { console.log(row.email, row.valid) } ``` HTTP batch and stream endpoints use the same per-email rate limits as documented in section 4. Management client: - `createManagementClient(mgmtKey, options?)` - `mgmt.account.get()` -> `GET /v1/management/account` - `mgmt.keys.list()` -> `GET /v1/management/keys` - `mgmt.keys.create({ label? })` -> `POST /v1/management/keys` - `mgmt.keys.revoke(id)` -> `DELETE /v1/management/keys/{id}` - `mgmt.usage.daily()` -> `GET /v1/management/usage` - `mgmt.usage.summary()` -> `GET /v1/management/usage/summary` - `mgmt.billing.portal({ return_url? })` -> `POST /v1/management/billing/portal` ## 3) Status Semantics - deliverable: SMTP accepted mailbox. - undeliverable: SMTP rejected mailbox. - unknown: mailbox-level verification unavailable — smtp_blocked major providers, timeout/greylist, or ambiguous SMTP on the MX (common org/M365); use mx_found and smtp_blocked. - catch_all: domain accepts all RCPT; mailbox-level certainty unavailable. - invalid: failed syntax/disposable/no_mx checks. Catch-all caution: - For catch-all domains, `valid=true` can still be returned because the server accepts RCPT for arbitrary recipients. - This does not prove that a specific mailbox exists. - Expected confidence for catch-all is lower (`0.65`) to reflect higher downstream bounce risk. Confidence ladder (ordinal summary, not a statistical probability): - `0.97` deliverable · `0.75` unknown + smtp_blocked · `0.65` catch_all · `0.50` unknown + mx_found + not smtp_blocked (SMTP inconclusive) · `0.02` undeliverable · `0.0` invalid path. Major-provider behavior: - For Gmail/Outlook/Yahoo/iCloud-like domains, third-party SMTP probing may be blocked. - Expected response pattern: - valid=false (unknown is not deliverable/catch-all) - smtp_blocked=true - status=unknown - confidence=0.75 - Treat this as expected, not as API failure; use `confidence` and `smtp_blocked`, not `valid` alone. ## 4) Error Contract Common HTTP errors: - 400 Invalid request body - 401 Missing or invalid API key - 402 Hard EUR spend cap would be exceeded (verify / batch / stream) - 429 Per-minute rate limit exceeded, or (free tier) monthly verification quota exceeded - 503 Temporary monthly quota check failure — retry shortly - 404 Unknown route 401 missing key shape: { "error": "missing_api_key", "message": "No Authorization header provided.", "action": "Include your API key as: Authorization: Bearer sk_live_...", "docs": "https://docs.truval.dev/api/email-verify#authentication" } 401 invalid key shape: { "error": "invalid_api_key", "message": "API key not found or revoked.", "action": "Generate a new key at https://dash.truval.dev.", "docs": "https://docs.truval.dev/api/email-verify#authentication" } 400 invalid request shape: { "error": "invalid_request", "message": "Request body must be JSON with an email field.", "action": "Send: {\"email\":\"user@example.com\"}", "docs": "https://docs.truval.dev/api/email-verify#request" } 402 spend cap shape: { "error": "payment_required", "message": "Hard spend cap reached. Upgrade or raise your cap to continue.", "action": "Adjust your hard cap in Billing & Limits, or upgrade your plan.", "docs": "https://docs.truval.dev/api/email-verify#spend-cap", "hard_cap_eur": 50, "current_overage_eur": 52.5 } 429 per-minute rate limit (`message` and `limit` reflect your tier): { "error": "rate_limit_exceeded", "message": "Rate limit of 10 req/min exceeded.", "action": "Wait until reset_at (plus a small cushion) before retrying.", "limit": 10, "window": "1m", "reset_at": "2026-01-01T00:00:00.000Z", "docs": "https://docs.truval.dev/api/email-verify#rate-limits" } 429 free-tier monthly quota (no `window`; `reset_at` is start of next UTC month): { "error": "monthly_quota_exceeded", "message": "Monthly free tier limit of 500 verification units reached for this UTC month.", "action": "Upgrade your plan or wait until the next UTC month.", "limit": 500, "used": 500, "reset_at": "2026-02-01T00:00:00.000Z", "docs": "https://docs.truval.dev/api/email-verify#monthly-quota" } 503 quota check failed (retry): { "error": "quota_check_failed", "message": "Could not verify monthly usage quota. Retry shortly.", "action": "If this persists, contact support.", "docs": "https://docs.truval.dev/api/email-verify#monthly-quota" } 404 shape: { "error": "not_found", "message": "The requested endpoint does not exist.", "action": "Use POST /v1/email/verify, POST /v1/email/verify/batch, POST /v1/email/verify/stream, or inspect https://api.truval.dev/openapi.json.", "docs": "https://docs.truval.dev/api/email-verify#not-found" } Plan rate limits: - free: 10 req/min - builder: 100 req/min - scale: 1000 req/min Pricing: - free: 500 verification units per UTC calendar month (count_usage_for_customer; excludes throttle/monthly-cap-only usage_log rows; see monthly-quota), EUR 0 - builder: 5,000 billable/mo (subscription period), EUR 9 (+ EUR 0.002 overage per billable call) - scale: 100,000 billable/mo (subscription period), EUR 49 (+ EUR 0.0012 overage per billable call) **Free tier** monthly enforcement uses `count_usage_for_customer` (all `usage_log` rows in the UTC month except `failed_check` `rate_limit_exceeded` / `monthly_quota_exceeded`). **Paid tiers — billable verification:** one email counted after layer 1 (syntax). Syntax-only (`failed_check` syntax on HTTP 200) is excluded from billable; disposable, no_mx, smtp*, and success each cost one billable unit. See https://docs.truval.dev/api/email-verify#billing-and-billable-verifications and https://docs.truval.dev/api/email-verify#monthly-quota ## 5) Integration Examples ### 5.1 curl curl https://api.truval.dev/v1/email/verify \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{"email":"user@example.com"}' ### 5.2 TypeScript (fetch) import "dotenv/config"; const API_KEY = process.env.TRUVAL_API_KEY!; async function verifyEmail(email: string) { const res = await fetch("https://api.truval.dev/v1/email/verify", { method: "POST", headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ email }), }); if (!res.ok) { throw new Error(`truval.dev error ${res.status}: ${await res.text()}`); } const data = await res.json(); return data; } const result = await verifyEmail("user@example.com"); console.log(result); ### 5.3 Python (requests) import os import requests API_KEY = os.environ["TRUVAL_API_KEY"] def verify_email(email: str) -> dict: resp = requests.post( "https://api.truval.dev/v1/email/verify", headers={ "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json", }, json={"email": email}, timeout=10, ) resp.raise_for_status() return resp.json() print(verify_email("user@example.com")) ### 5.4 LangChain tool wrapper (Python) import os import requests from langchain.tools import tool API_KEY = os.environ["TRUVAL_API_KEY"] @tool("truval_verify_email") def truval_verify_email(email: str) -> str: """Verify whether an email is deliverable.""" resp = requests.post( "https://api.truval.dev/v1/email/verify", headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}, json={"email": email}, timeout=10, ) resp.raise_for_status() return resp.text ### 5.5 CrewAI tool (Python) import os import requests from crewai.tools import BaseTool class VerifyEmailTool(BaseTool): name: str = "truval_verify_email" description: str = "Verify if an email is valid and deliverable using truval.dev." def _run(self, email: str) -> str: api_key = os.environ["TRUVAL_API_KEY"] resp = requests.post( "https://api.truval.dev/v1/email/verify", headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", }, json={"email": email}, timeout=10, ) resp.raise_for_status() return resp.text ### 5.6 Vercel AI SDK tool (TypeScript) import { tool } from "ai"; import { z } from "zod"; const truvalVerifyEmail = tool({ description: "Verify whether an email is valid and deliverable", inputSchema: z.object({ email: z.string().email(), }), execute: async ({ email }) => { const res = await fetch("https://api.truval.dev/v1/email/verify", { method: "POST", headers: { Authorization: `Bearer ${process.env.TRUVAL_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ email }), }); if (!res.ok) { throw new Error(`truval.dev error ${res.status}: ${await res.text()}`); } return res.json(); }, }); ### 5.7 Management API (HTTP) Endpoint set: - GET /v1/management/account - GET /v1/management/keys - POST /v1/management/keys - DELETE /v1/management/keys/{id} - GET /v1/management/usage - GET /v1/management/usage/summary - POST /v1/management/billing/portal Permission matrix: - `sk_live_...` can call `/v1/email/*` only. - `sk_mgmt_...` can call `/v1/management/*` only. If key type is wrong, API returns: ```json { "error": "wrong_key_type", "message": "Management endpoints require a provisioning key (sk_mgmt_...).", "action": "Create a provisioning key at https://dash.truval.dev", "docs": "https://docs.truval.dev/api/management#authentication" } ``` ### 5.8 Management API (SDK) ```ts import { createClient, createManagementClient } from 'truval' const mgmt = createManagementClient(process.env.TRUVAL_MGMT_KEY!) const created = await mgmt.keys.create({ label: 'worker-1' }) const verifyClient = createClient(created.key) const result = await verifyClient.verify('user@example.com') const usage = await mgmt.usage.summary() if (usage.calls_remaining < 100) { throw new Error('Insufficient calls remaining') } ``` ## 6) MCP Contract MCP transport: - HTTP POST - JSON-RPC 2.0 - endpoint: https://mcp.truval.dev/mcp - requires Authorization: Bearer sk_live_... - management tools require X-Truval-Provisioning-Key: sk_mgmt_... Supported MCP methods: - tools/list - tools/call Exposed tools: - verify_email - create_api_key - list_api_keys - revoke_api_key - get_usage_summary - get_account - get_usage - create_billing_portal - input schema: { "type": "object", "required": ["email"], "properties": { "email": { "type": "string", "description": "The email address to verify" } } } ### 6.1 MCP tools/list example curl -sS -X POST "https://mcp.truval.dev/mcp" \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "jsonrpc":"2.0", "id":1, "method":"tools/list" }' ### 6.2 MCP tools/call example curl -sS -X POST "https://mcp.truval.dev/mcp" \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "jsonrpc":"2.0", "id":2, "method":"tools/call", "params":{ "name":"verify_email", "arguments":{"email":"user@example.com"} } }' Expected `tools/call` result payload: - result.content[0].type = "text" - result.content[0].text = JSON string of verification response MCP error patterns: - Missing Authorization header -> JSON-RPC error code -32001, HTTP 401 - Upstream API failure -> JSON-RPC error code -32002, HTTP mirrors upstream status - Invalid upstream JSON -> JSON-RPC error code -32003, HTTP 502 - Invalid method -> JSON-RPC error code -32601 - Invalid params -> JSON-RPC error code -32602 MCP error example (missing Authorization): { "jsonrpc": "2.0", "id": 2, "error": { "code": -32001, "message": "Unauthorized", "data": { "reason": "Missing Authorization header", "action": "Provide Authorization: Bearer sk_live_... in MCP client headers.", "docs": "https://docs.truval.dev/api/email-verify#authentication" } } } Cursor MCP config: { "mcpServers": { "truval": { "url": "https://mcp.truval.dev/mcp", "headers": { "Authorization": "Bearer ${TRUVAL_API_KEY}", "X-Truval-Provisioning-Key": "sk_mgmt_..." } } } } ## 7) Agent Decision Logic (Recommended) Given result object R, evaluate branches in order and emit a signal: - GREEN_LIGHT → proceed - YELLOW_LIGHT → proceed with caution / ask user to confirm - RED_LIGHT → reject or request alternative address 1) suggestion check (evaluate first, before status): - if R.suggestion != null: → YELLOW_LIGHT: prompt user "Did you mean [R.suggestion]?" before anything else. 2) if R.disposable == true: → RED_LIGHT: reject. Do not retry with the same address. 3) if R.status == "invalid" or R.status == "undeliverable": → RED_LIGHT: reject or request alternative address. 4) if R.status == "catch_all" or R.catch_all == true: → YELLOW_LIGHT: low-certainty (typical confidence ~0.65). Proceed only with user confirmation, double opt-in, or downstream engagement checks. valid=true does NOT prove mailbox existence on catch-all. 5) if R.status == "unknown" and R.smtp_blocked == true and R.confidence >= 0.75: → YELLOW_LIGHT (lean GREEN): domain checks passed; major provider blocks SMTP probing (Gmail, Outlook, Yahoo). Do not treat valid=false as failure. Confidence ~0.75 is expected and acceptable. 6) if R.status == "unknown" and R.mx_found == true and R.smtp_blocked == false: → YELLOW_LIGHT: SMTP inconclusive on real domain (common for org/M365). Typical confidence 0.50. Confirm with user or apply secondary verification. Do NOT auto-reject as invalid. 7) if R.status == "unknown" (other cases, e.g. R.mx_found == false): → RED_LIGHT: treat as unconfirmed; trigger fallback verification flow. 8) if R.valid == true (deliverable or catch-all already handled above): → GREEN_LIGHT: proceed. Typical confidence 0.97 for deliverable. Fallback: if R.confidence >= 0.9: → GREEN_LIGHT if R.confidence >= 0.7: → YELLOW_LIGHT (proceed with low risk) if R.confidence >= 0.5: → YELLOW_LIGHT (confirm with user) if R.confidence < 0.5: → RED_LIGHT (reject or ask for different email) ## 8) Reliability Patterns - Retry: transient network failures, 5xx, and 429 only. - Backoff: exponential with jitter; honor `reset_at` on 429 when present. - Avoid retry loops for deterministic failures (`invalid`, `undeliverable`, failed_check in syntax/disposable/no_mx). - Persist raw response fields to preserve explainability for later decisions. ## 9) Agent Skills Truval provides Universal Agent Directives (Agent Skills) compatible with Claude, ChatGPT, Cursor, LangChain, CrewAI, and any agent framework. - Skill URL: `https://docs.truval.dev/skills/truval-email-verify/SKILL.md` (for Claude.ai projects, copy the page content into the project’s custom instructions) - CLI Installation: `npx skills add truval-dev/truval-skills` - GitHub Repository: `https://github.com/truval-dev/truval-skills` ## 10) Canonical URLs - API: `https://api.truval.dev` - Dashboard: `https://dash.truval.dev` - Docs: `https://docs.truval.dev` - MCP: `https://mcp.truval.dev/mcp` - OpenAPI: `https://api.truval.dev/openapi.json` - MCP Schema: `https://mcp.truval.dev/openapi.json` - Postman: `https://docs.truval.dev/postman/truval.json` ## 11) Public Repositories - SDK: `https://github.com/truval-dev/truval-sdk` - MCP Server: `https://github.com/truval-dev/truval-mcp-server` - Agent Skills: `https://github.com/truval-dev/truval-skills`