# VerifiedInbox — full documentation All public VerifiedInbox pages in one file. Index: https://verifiedinbox.ai/llms.txt --- # Get started with VerifiedInbox (guide for AI assistants) > Everything an AI assistant needs to take a company's email domain from a first check to its logo, and Gmail's blue checkmark, shown in the inbox, using the VerifiedInbox MCP server or REST API. You are probably reading this because a person asked you something like: *"Add a verified VMC mark to my email domain using verifiedinbox.ai/get-started.md"*. This page is written for you, the AI assistant. It contains everything you need to complete the job with the person, step by step, across as many conversations as it takes. ## The 60-second version 1. **Connect** to the MCP server at `https://api.verifiedinbox.ai/mcp` (no credentials needed to start). If you can't use MCP, use the REST API at `https://api.verifiedinbox.ai/v1` (see [REST API fallback](#rest-api-fallback)). 2. **Ask for the domain** if you don't know it (the part after the `@` in the person's work email, e.g. `acme.com`). Shared providers like gmail.com cannot be used. 3. Call `check_domain`, then `start_application`. **Save the `application_id` and `guest_token`** it returns, and tell the person to keep them (or create a resume link) so you can continue later. 4. Walk through the free steps: `set_email_usage` + `recheck_email_security` → `submit_logo` → `get_logo_status` → `get_trademark_status` → `get_eligibility` → `get_estimate`. **Steps only move forward when completed:** once a step's `status` is `ready`, confirm with the person and call `complete_step`. 5. Tell the person, in plain English, which outcome they qualify for: **logo + blue checkmark (VMC)**, **logo only (CMC)**, or **not currently eligible** (and what to do about it). 6. When they are ready to pay, they need an account. Create a checkout link with `create_checkout`; **only the person can pay**. 7. After payment: business details → logo approval (by the person) → DNS records (added by the person) → certificate → go live, completing each step with `complete_step`. Always follow the `next_actions` returned by every tool. ## What VerifiedInbox does VerifiedInbox gets a company's logo shown next to the emails it sends in Gmail and other inboxes that support the BIMI standard, and, with a registered trademark, Gmail's **blue verified checkmark**. It is a one-stop shop: it runs the checks, prepares the logo file, checks the US trademark register, verifies the business, orders the certificate from the certificate issuer (DigiCert), gives copy-paste DNS records, hosts the logo and certificate, monitors the setup and renews the certificate every year. The people you are helping are usually marketing, sales or company leaders, not engineers. Use plain English with them. Technical terms are fine with you, the agent, and in "advanced" details. ## Possible outcomes ### VMC (Verified Mark Certificate) | Field | Value | |---|---| | **What the person gets** | Logo **and** Gmail's blue checkmark | | **Requirements** | Registered trademark for the logo (USPTO in the first release), own domain, DMARC at enforcement | | **Price** | $1,499 per domain per year + issuer fee | ### CMC (Common Mark Certificate) | Field | Value | |---|---| | **What the person gets** | Logo only, **no checkmark** | | **Requirements** | Logo used publicly for 12+ months (no trademark needed), own domain, DMARC at enforcement | | **Price** | $899 per domain per year + issuer fee | ### Not currently eligible | Field | Value | |---|---| | **What the person gets** | Clear list of what's missing, evidence, a date when they may qualify, and a reminder | | **Price** | Free | For a CMC customer, success means **the logo is displayed**. Never promise a checkmark to a CMC customer. ### Separate outcomes (never collapse them into one "verified") An application tracks several independent outcomes. Report each one separately: | Outcome field | Meaning | Values | |---|---|---| | `eligibility` | Which route the domain qualifies for | `unknown`, `assessing`, `vmc_eligible`, `cmc_eligible`, `not_currently_eligible` | | `payment` | Plan payment | `unpaid`, `checkout_open`, `paid`, `refund_pending`, `refunded`, `canceled` | | `business` | Company verification | `not_started`, `verifying`, `verified`, `needs_info`, `rejected` | | `ownership` | Domain control (DNS TXT record) | `pending`, `verified`, `lost` | | `logo` | Logo file | `missing`, `processing`, `ready_for_review`, `approved`, `failed` | | `trademark` | USPTO match | `not_started`, `searching`, `match_found`, `no_match`, `needs_review`, `manually_verified`, `error` | | `certificate` | **Certificate issued** | `not_ordered`, `waiting_prerequisites`, `submitted`, `issuer_validation`, `awaiting_customer`, `issued`, `expiring`, `renewed`, `expired`, `rejected`, `revoked` | | `bimi` | **BIMI correctly published and reachable** | `not_published`, `invalid`, `published` | | `inbox_display` | **Display observed in a real mailbox** | `not_observed`, `observed` | | `dmarc_rollout` | Safe DMARC rollout stage | `missing`, `monitoring`, `quarantine_partial`, `quarantine`, `reject` | "Eligibility assessed", "certificate issued", "BIMI published" and "display observed" are four different milestones. Gmail decides when to show logos and checkmarks; even with everything correct, **display is never guaranteed**. ## Connect to the MCP server - **Endpoint:** `https://api.verifiedinbox.ai/mcp` - **Transport:** MCP Streamable HTTP (JSON responses). Protocol versions `2025-11-25`, `2025-06-18`, `2025-03-26`. - Every MCP tool wraps one or more REST API operations (listed below) and uses the same server logic. Tool arguments are generated from the OpenAPI schema, so MCP and REST never disagree. ### Three ways to connect | Mode | How | What you can do | |---|---|---| | **Anonymous** | Connect with no credentials | All free tools. `start_application` returns a `guest_token`; pass it as the `guest_token` argument on later calls (or send the `VI-Guest-Token` header). | | **OAuth (sign in)** | Clients that support MCP OAuth sign the person in automatically the first time an account tool is called (the server answers HTTP 401 with `WWW-Authenticate` resource metadata). | Everything the person's role allows. | | **API key** | Header `Authorization: Bearer vi_live_…` (real) or `vi_test_…` (test mode). The person creates keys in Dashboard → Settings → API keys. | Scoped to one organization and one environment (live or test). | Never ask the person to paste an API key into the chat unless your client has no safer way to configure headers; prefer OAuth. ### Client setup **Claude (claude.ai and Claude Desktop):** Settings → Connectors → *Add custom connector* → name `VerifiedInbox`, URL `https://api.verifiedinbox.ai/mcp`. Then enable it in the conversation. **Claude Desktop (config file, if custom connectors aren't available):** ```json { "mcpServers": { "verifiedinbox": { "command": "npx", "args": ["-y", "mcp-remote", "https://api.verifiedinbox.ai/mcp"] } } } ``` **Claude Code:** ```bash claude mcp add --transport http verifiedinbox https://api.verifiedinbox.ai/mcp # with an API key (test mode shown): claude mcp add --transport http verifiedinbox https://api.verifiedinbox.ai/mcp \ --header "Authorization: Bearer vi_test_…" ``` **Cursor** (`~/.cursor/mcp.json` or `.cursor/mcp.json` in a project): ```json { "mcpServers": { "verifiedinbox": { "url": "https://api.verifiedinbox.ai/mcp" } } } ``` With an API key: ```json { "mcpServers": { "verifiedinbox": { "url": "https://api.verifiedinbox.ai/mcp", "headers": { "Authorization": "Bearer vi_test_…" } } } } ``` **ChatGPT:** add a custom connector (Settings → Apps & Connectors; on some plans this requires developer mode) with the URL `https://api.verifiedinbox.ai/mcp`. If the person's plan doesn't allow custom connectors, use the [REST API](#rest-api-fallback) if you can make HTTP requests, or guide them through the website at `https://verifiedinbox.ai/verify/new?domain=`. **VS Code / other MCP clients:** add an HTTP MCP server with the URL above, e.g. `.vscode/mcp.json`: ```json { "servers": { "verifiedinbox": { "type": "http", "url": "https://api.verifiedinbox.ai/mcp" } } } ``` If you cannot add the server yourself, tell the person exactly which of the steps above to follow, then continue once the tools are available. ### MCP resources and prompts - Resource `verifiedinbox://get-started` — this guide. - Resource `verifiedinbox://glossary` — plain-English definitions of every term. - Prompt `verify_my_domain(domain)` — starts the guided flow. ## The process, step by step The website shows the same process as an 11-step wizard. Steps 1–5 are **free and anonymous**; steps 6–11 need an account and payment. **Only the person (or you, on their behalf) moves the wizard forward.** Background work changes outcomes but never completes a step. Every step in `steps` lists its `requirements` (required ones must be met; others are recommended). When a step's `status` is `ready`, call `complete_step` with the `application_id` and the step slug; a `409 step_not_ready` lists what's still missing. Statuses: `complete` (done), `attention` (done earlier, but something it relied on broke: fix it and call `complete_step` again), `ready`, `action_required`, `in_progress`, `blocked` (the current step) and `locked` (later steps). | # & step (`slug`) | Phase & access | What happens | |---|---|---| | 1. `domain` | Check (free) | Domain accepted; blocked-provider and registration check | | 2. `email-security` | Check (free) | SPF, DKIM and DMARC results with exact fixes; DMARC must be enforced (filter all or block all) | | 3. `logo` | Check (free) | Logo uploaded or discovered; compliant version prepared; Gmail preview | | 4. `trademark` | Check (free) | USPTO search (or a registration number is verified) | | 5. `eligibility` | Check (free) | VMC, CMC or not currently eligible, with evidence, next steps and an estimate | | 6. `checkout` | Setup (paid) | Sign in and pay through Stripe's hosted checkout | | 7. `business` | Setup (paid) | Legal company details and the applicant | | 8. `logo-approval` | Setup (paid) | The person approves the exact final logo file | | 9. `dns` | Setup (paid) | Ownership and logo (BIMI) records; DMARC from step 2 shown as done | | 10. `certificate` | Certificate (paid) | Final re-check, order, issuer review (a few business days), issuance | | 11. `go-live` | Live (paid) | Logo published, test email, display confirmed; monitoring and renewals on | ### Free steps (anonymous) **Step 0 — explain (optional).** `get_started` returns the process, steps and pricing. Use it if the person asks "how does this work?". **Step 1 — check the domain.** `check_domain` with `{ "domain": "acme.com" }` (emails and URLs are accepted too). It returns a `verdict` (`ok`, `blocked_provider`, `invalid`, `not_registered`), the mailbox provider, whether DMARC and BIMI exist, and a plain-English `summary`. If the verdict is `blocked_provider`, explain why (see [Shared email providers](#shared-email-providers-cannot-be-used)) and ask for their company domain. If `not_registered`, tell them the domain must be registered first (with a registrar such as Cloudflare or GoDaddy). **Step 2 — start (or resume) the application.** `start_application` with `{ "domain": "acme.com" }` returns the `application_id`, a `guest_token` (only the first time), the wizard `steps`, `next_actions` and a `web_url`. It is idempotent: calling it again for the same domain returns the existing application. Background work (DNS scan, website logo discovery, web-archive history) starts immediately. **Step 3 — email security.** Ask the person how email is used on the domain and record it with `set_email_usage` (`none`: new domain or no email yet; `low`: little email; `active`: email every day). The answer decides the recommended path: `none`/`low` publish the blocking DMARC record straight away; `active` follows the staged rollout, though the person may skip ahead (explain the risk). After they change DNS, call `recheck_email_security`: it returns SPF, DKIM and DMARC status, `findings` with ready-made `fix` records, and `dmarc_rollout` with every stage's exact record, what to monitor and what happens to email. If no DKIM key was found, ask for their selector and call `verify_dkim_selectors`. Summarize the 1–3 things that matter. The step needs DMARC enforced (plus SPF and DKIM unless no email is sent); see [Safe DMARC rollout](#safe-dmarc-rollout). `check_email_security` runs the same inspection for any domain without an application. **Step 4 — logo.** `submit_logo` with the `application_id` and one of: SVG text (`svg`), a public URL (`url`), or `use_discovered: true` to use the logo found on their website. Max 512 KB. The original file is kept byte-for-byte; a compliant SVG Tiny P/S copy is produced. Then call `get_logo_status`: it returns the compliance analysis, the list of changes made during conversion (flagging any that change the look), a visual comparison score, and preview URLs. Share the preview with the person. If the logo can't be used (raster only, too complex), explain and ask for a better file (an SVG from their designer or brand guidelines). **Step 5 — trademark.** Submitting the logo starts a USPTO search automatically. `get_trademark_status` returns `match_found`, `no_match`, `needs_review` or `searching`, plus candidates. If the person knows their registration or serial number, call `search_trademark` with it to verify that exact record; you can also pass the owner name or mark text to refine the search. Searches are rate-limited for anonymous users; don't loop. **Step 6 — eligibility.** `get_eligibility` returns `vmc_eligible`, `cmc_eligible` or `not_currently_eligible`, the requirements for each route (met or not, with evidence), `next_steps`, and `estimated_eligible_on` when a date is known. Explain the result in one or two sentences. **Step 7 — estimate.** `get_estimate` with `product` (`vmc` or `cmc`) returns line items (our plan + the issuer's fee, marked as estimated), the total per year and the refund policy. Always show the total before suggesting payment. **If not currently eligible:** offer `watch_eligibility` (we email them when they qualify) and the paid trademark filing service (email `trademark@verifiedinbox.ai`). See [Trademark logic](#trademark-logic). ### Paid steps (account required) **Sign in and claim.** Account tools need the person to be signed in: - If your client supports MCP OAuth, call the account tool (for example `get_account`); the client will open a sign-in window. Then call `claim_application` with the `application_id` and the `guest_token` to move the anonymous draft into their account (atomic; if their organization already has an application for the domain, the more advanced one is kept and you're told which). - Otherwise call `create_resume_link` and give the person the link. They open it, sign in on the website, and the draft is claimed automatically. You can continue later with an API key or OAuth. **Checkout.** `create_checkout` with `{ "application_id", "product": "vmc" | "cmc" }` returns a Stripe `checkout_url` (expires). Give the link to the person; **you cannot pay, and you must never ask for card details**. After they pay, `get_application` shows `payment: "paid"` (confirmation comes from Stripe, usually within seconds). **Business details.** `submit_business_details` with the legal company name, jurisdiction (e.g. `us_de`), registration number, address, and the applicant (name, work email, title, phone), plus `authority_attested: true` only if the person confirms they are authorized to act for the company. Then `get_business_status`. If it returns `needs_info`, ask the person exactly what's requested. **Logo approval (the person decides).** Show the person the original and the converted logo side by side (URLs from `get_logo_status`). Only after they explicitly say it looks right, call `approve_logo` with the `converted_sha256` of the version they reviewed. **DNS records (the person, or their IT, adds them).** `get_dns_records` returns every record to add, each with `host`, `type`, `value`, `purpose` and live `status` (`ok`, `missing`, `mismatch`, `warning`): - Domain ownership: `TXT` at `_verifiedinbox.` - DMARC: `TXT` at `_dmarc.` (at the current safe rollout stage) - BIMI: `TXT` at `default._bimi.` pointing to the hosted logo and certificate - SPF/DKIM advice when needed Present them as a short copy-paste list and say where to paste them (their DNS host, which is detected and returned). After they've added them, call `verify_dns`: it checks the domain's **authoritative nameservers** directly, so correct changes show up within seconds. Never claim you changed DNS yourself; in this release DNS is never changed on the person's behalf. **DKIM and a real test email (optional but recommended).** `verify_dkim_selectors` checks selectors the person supplies. `create_test_message` returns an address; ask the person to send a normal email from their domain to it, then call `get_test_message_result` to see SPF, DKIM and DMARC alignment of that real message. **Certificate.** Ordering happens automatically once the DNS step is completed. Right before the order is sent, a **final re-check** verifies everything the issuer will look at, live (domain, email protection, approved logo, company, trademark, DNS records); it repeats daily while the issuer reviews. Anything that fails holds the order and marks its step `attention`: help the person fix it, then call `complete_step` for that step again. `run_final_check` re-runs it on demand. `get_certificate` returns the status, `final_check`, `customer_actions` (for example "approve the email from DigiCert" or a verification call) and, once issued, the expiry date. Issuer review usually takes a few business days. Relay customer actions clearly and promptly. **Go live.** When the certificate is issued, the BIMI record points to the hosted files. `get_application` shows `bimi: "published"` when everything is reachable and matches. Ask the person to send an email to a Gmail address and look at it; when they confirm they see the logo (and checkmark for VMC), call `confirm_inbox_display` with `{ "mailbox": "gmail", "observed": true }`. Monitoring and renewal start automatically. **After going live.** `get_account` lists every organization, application, status and pending action. `list_orders` shows payments. `renew_certificate` renews early, `reissue_certificate` gets a new certificate (e.g. after a logo change). `revoke_certificate` permanently cancels it and requires `confirm: "REVOKE "`; only do this after the person explicitly asks and confirms. Free tools that work on an application accept `guest_token`; account tools accept `environment` (`live` or `test`). The table below lists every argument. ### Complete tool reference Every MCP tool, what it does, its arguments (`*` = required), whether it needs an account, and the REST operation(s) it wraps. Generated from the OpenAPI schema and the server's tool list. ### `get_started` Explains the whole process (free checks, paid setup, certificate, go-live), pricing, what the user must do themselves, and how to continue later. Call this first if you're new to VerifiedInbox. | Field | Value | |---|---| | **Arguments** | None | | **Access** | Free | | **API operations** | `GET /v1/meta` | ### `check_domain` Free, instant check of an email domain: can it get a verified logo? Detects shared providers that can't be used (gmail.com, outlook.com…), unregistered domains, the mailbox provider and a DMARC/BIMI summary. Accepts a domain, URL or email address. | Field | Value | |---|---| | **Arguments** | `domain`* | | **Access** | Free | | **API operations** | `POST /v1/checks/domain` | ### `check_email_security` Free full inspection of SPF, DKIM, DMARC and BIMI with plain-English findings and exact fixes. Uses the domain's authoritative nameservers (sees changes within seconds). | Field | Value | |---|---| | **Arguments** | `domain`*, `dkim_selectors` | | **Access** | Free | | **API operations** | `POST /v1/checks/email-auth` | ### `start_application` Starts the verification for a domain, or returns the existing one. Anonymous callers receive a guest_token — tell the user to keep it (with the application_id) to continue in a later conversation. Background checks start immediately. Returns the full state and next_actions. | Field | Value | |---|---| | **Arguments** | `domain`*, `environment`, `guest_token` | | **Access** | Free | | **API operations** | `POST /v1/applications` | ### `list_applications` Lists the verifications visible to the caller (guest drafts for a guest_token, or the account's domains). | Field | Value | |---|---| | **Arguments** | `environment`, `guest_token` | | **Access** | Free | | **API operations** | `GET /v1/applications` | ### `get_application` Full current state: separate outcomes (eligibility, payment, business, ownership, logo, trademark, certificate, BIMI, inbox display), the 11 wizard steps with status, running background work, and next_actions (what to do next and which tool to use). Call this whenever you need to know where things stand. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token` | | **Access** | Free | | **API operations** | `GET /v1/applications/{application_id}`, `GET /v1/applications/{application_id}/next-steps` | ### `complete_step` Moves the verification to the next step once the current step's required items are met (each step lists them in `requirements`; status `ready` means it can be completed). Background work never completes steps; this does, on the user's behalf. Also re-confirms an earlier step marked `attention` after it was fixed. Confirm with the user before completing a step they haven't reviewed. | Field | Value | |---|---| | **Arguments** | `application_id`*, `step`*, `guest_token` | | **Access** | Free | | **API operations** | `POST /v1/applications/{application_id}/steps/{step}/complete` | ### `set_email_usage` Ask the user how email is used on their domain and record the answer: none (new domain, no email yet), low (only their mailbox provider, e.g. Google Workspace, sends email) or active (several tools send email: newsletters, billing, support…). none/low can block fake emails right away; active should follow the staged rollout, though the user may skip ahead. The email security step needs DMARC published with a report address (watch mode is enough); filtering or blocking is required by the DNS step. Returns the email security report with the recommended DMARC record. | Field | Value | |---|---| | **Arguments** | `application_id`*, `email_usage`*, `guest_token` | | **Access** | Free | | **API operations** | `PUT /v1/applications/{application_id}/email-usage` | ### `set_dmarc_report_address` Sets the email address for daily DMARC reports, used in every suggested DMARC record (rua=). Ask the user for a mailbox they read (or a DMARC reporting service); by default reports go to dmarc@, which must exist. Pass null to go back to the default. The user still publishes the record. | Field | Value | |---|---| | **Arguments** | `application_id`*, `email`*, `guest_token` | | **Access** | Free | | **API operations** | `PUT /v1/applications/{application_id}/dmarc-reports` | ### `recheck_email_security` Re-checks SPF, DKIM and DMARC for the application right now (after the user published records) and returns the report plus the DMARC rollout plan: every stage's exact record, what to monitor and what happens to email at that stage. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token` | | **Access** | Free | | **API operations** | `POST /v1/applications/{application_id}/email-security/check` | ### `submit_logo` Provide the company logo: SVG text (preferred), a URL to an SVG, or use_discovered=true to use the logo found on the company website. The original is kept untouched; an inbox-ready SVG Tiny P/S version is produced automatically and a US trademark search starts. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token`, `svg`, `url`, `use_discovered` | | **Access** | Free | | **API operations** | `PUT /v1/applications/{application_id}/logo` | ### `get_logo_status` Logo processing status, compliance issues, the list of changes made for the inbox version (lossy ones flagged), visual difference, and preview URLs. Also lists logos discovered on the website. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token` | | **Access** | Free | | **API operations** | `GET /v1/applications/{application_id}/logo`, `GET /v1/applications/{application_id}/logo-candidates` | ### `search_trademark` Starts (or re-runs) the USPTO trademark search, or verifies a specific US serial/registration number the user provides. A registered trademark matching the logo unlocks the blue checkmark (VMC). Rate-limited for anonymous users. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token`, `mark_text`, `owner_name`, `serial_or_registration_number` | | **Access** | Free | | **API operations** | `POST /v1/applications/{application_id}/trademark/search`, `POST /v1/applications/{application_id}/trademark/claim` | ### `get_trademark_status` Trademark search result: matched registration (with similarity to the logo), candidates, and what it means. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token` | | **Access** | Free | | **API operations** | `GET /v1/applications/{application_id}/trademark` | ### `get_eligibility` Outcome: vmc_eligible (logo + blue checkmark), cmc_eligible (logo only, no checkmark), not_currently_eligible (with evidence, estimated date and next steps such as the trademark filing service at trademark@getverifiedinbox.com), or assessing. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token` | | **Access** | Free | | **API operations** | `GET /v1/applications/{application_id}/eligibility` | ### `get_estimate` Itemized yearly price before payment: plan (CMC $899 or VMC $1,499 per domain) plus the certificate issuer's fee (pass-through, no markup, ~$750–$1,750). Full refund if issuance is rejected. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token`, `product` | | **Access** | Free | | **API operations** | `GET /v1/applications/{application_id}/estimate` | ### `watch_eligibility` Emails the user when the domain becomes eligible (we re-check daily). Use for not_currently_eligible outcomes. | Field | Value | |---|---| | **Arguments** | `application_id`*, `email`*, `guest_token` | | **Access** | Free | | **API operations** | `POST /v1/applications/{application_id}/eligibility/watch` | ### `create_resume_link` Creates a one-time link (valid 24h) that opens this verification in the web app — useful for visual steps like reviewing the logo or copying DNS records. Optionally emails it. | Field | Value | |---|---| | **Arguments** | `application_id`*, `email`, `guest_token` | | **Access** | Free | | **API operations** | `POST /v1/applications/{application_id}/resume-links` | ### `explain_term` Plain-English explanation of a term such as BIMI, VMC, CMC, DMARC, SPF, DKIM, selector, alignment, SVG Tiny P/S. | Field | Value | |---|---| | **Arguments** | `term`* | | **Access** | Free | | **API operations** | `GET /v1/glossary/{term}` | ### `contact_support` Sends a message to the VerifiedInbox team (a human replies by email within one business day). Include the user's email. | Field | Value | |---|---| | **Arguments** | `message`*, `application_id`, `email`, `guest_token`, `kind`, `name` | | **Access** | Free | | **API operations** | `POST /v1/support` | ### `send_feedback` Sends feedback or a feature request to the VerifiedInbox team. | Field | Value | |---|---| | **Arguments** | `message`*, `email`, `guest_token`, `kind` | | **Access** | Free | | **API operations** | `POST /v1/support` | ### `get_account` Organizations, environment (live/test), every domain with its status and pending actions, and recent orders. | Field | Value | |---|---| | **Arguments** | `environment` | | **Access** | Account | | **API operations** | `GET /v1/me`, `GET /v1/applications`, `GET /v1/orders` | ### `claim_application` Atomically moves a guest draft (application_id + guest_token) into the signed-in account. If the account already has the domain, keeps the more advanced one and reports which. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token`*, `environment` | | **Access** | Account (`applications:write`) | | **API operations** | `POST /v1/applications/{application_id}/claim` | ### `create_checkout` Creates a Stripe checkout link for the chosen plan. Only a human can pay: give the link to the user, and confirm the price first (see get_estimate). | Field | Value | |---|---| | **Arguments** | `application_id`*, `product`*, `environment`, `idempotency_key` | | **Access** | Account (`billing:write`) | | **API operations** | `POST /v1/applications/{application_id}/checkout` | ### `submit_business_details` Legal company details + the applicant (starts business registration verification). Requires payment. jurisdiction is the US state code like us_de. | Field | Value | |---|---| | **Arguments** | `address`*, `applicant`*, `application_id`*, `jurisdiction`*, `legal_name`*, `authority_attested`, `environment`, `registration_number` | | **Access** | Account (`applications:write`) | | **API operations** | `PUT /v1/applications/{application_id}/business` | ### `get_business_status` Business verification status, matched registry record and applicant authority. | Field | Value | |---|---| | **Arguments** | `application_id`*, `environment` | | **Access** | Account | | **API operations** | `GET /v1/applications/{application_id}/business` | ### `approve_logo` Approves the exact inbox-ready logo the user reviewed (pass converted_sha256 from get_logo_status). Ask the user to look at the preview first (create_resume_link helps). | Field | Value | |---|---| | **Arguments** | `application_id`*, `converted_sha256`*, `environment` | | **Access** | Account (`applications:write`) | | **API operations** | `POST /v1/applications/{application_id}/logo/approve` | ### `get_dns_records` Every DNS record the user must add (ownership TXT, DMARC at the right rollout stage, BIMI) with copy-paste values and live status from the authoritative nameservers. The user adds them at their DNS provider. | Field | Value | |---|---| | **Arguments** | `application_id`*, `environment` | | **Access** | Account | | **API operations** | `GET /v1/applications/{application_id}/dns` | ### `verify_dns` Re-checks the DNS records right now (and watches closely for the next minutes). | Field | Value | |---|---| | **Arguments** | `application_id`*, `environment` | | **Access** | Account | | **API operations** | `POST /v1/applications/{application_id}/dns/verify` | ### `verify_dkim_selectors` Checks DKIM selectors the user provides. Needed only when the common selectors we try automatically found no key; the user's email provider shows the selector in its DKIM settings. | Field | Value | |---|---| | **Arguments** | `application_id`*, `selectors`*, `guest_token` | | **Access** | Free | | **API operations** | `POST /v1/applications/{application_id}/dkim-selectors` | ### `create_test_message` Returns an address the user sends a real email to; we then report SPF/DKIM/DMARC alignment on that actual message. Free. Also finds the DKIM key when the common selectors didn't: an aligned signature on the test email is added to the email security report. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token` | | **Access** | Free | | **API operations** | `POST /v1/applications/{application_id}/test-messages` | ### `get_test_message_result` Result of the test email: SPF, DKIM, DMARC alignment and findings. | Field | Value | |---|---| | **Arguments** | `application_id`*, `test_message_id`*, `guest_token` | | **Access** | Free | | **API operations** | `GET /v1/applications/{application_id}/test-messages/{test_message_id}` | ### `get_certificate` Certificate status timeline, prerequisites (with the step that fixes each), the latest final check, actions the issuer needs from the user, validity and renewal dates. | Field | Value | |---|---| | **Arguments** | `application_id`*, `environment` | | **Access** | Account | | **API operations** | `GET /v1/applications/{application_id}/certificate` | ### `run_final_check` Re-verifies live everything the certificate issuer checks (domain, email protection, logo, company, trademark, DNS records). Runs automatically before the order is submitted and daily during the issuer's review; anything that fails marks its step `attention` and holds the order. | Field | Value | |---|---| | **Arguments** | `application_id`*, `environment` | | **Access** | Account | | **API operations** | `POST /v1/applications/{application_id}/certificate/check` | ### `order_certificate` Submits the certificate order (normally automatic once prerequisites are met). Safe to call repeatedly. | Field | Value | |---|---| | **Arguments** | `application_id`*, `environment`, `idempotency_key` | | **Access** | Account (`certificates:write`) | | **API operations** | `POST /v1/applications/{application_id}/certificate/order` | ### `reissue_certificate` Requests a reissue (e.g. after a logo change). Re-checks domain ownership first. | Field | Value | |---|---| | **Arguments** | `application_id`*, `reason`*, `environment`, `idempotency_key` | | **Access** | Account (`certificates:write`) | | **API operations** | `POST /v1/applications/{application_id}/certificate/reissue` | ### `renew_certificate` Starts the renewal early (renewal is otherwise automatic 45 days before expiry). | Field | Value | |---|---| | **Arguments** | `application_id`*, `environment`, `idempotency_key` | | **Access** | Account (`certificates:write`) | | **API operations** | `POST /v1/applications/{application_id}/certificate/renew` | ### `revoke_certificate` PERMANENTLY revokes the certificate; the logo disappears from inboxes. Only with explicit user confirmation: confirm must be exactly "REVOKE ". | Field | Value | |---|---| | **Arguments** | `application_id`*, `confirm`*, `reason`*, `environment`, `idempotency_key` | | **Access** | Account (`certificates:write`) | | **API operations** | `POST /v1/applications/{application_id}/certificate/revoke` | ### `confirm_inbox_display` Records that the user saw their logo (and checkmark for VMC) in a mailbox such as Gmail — the final 'display observed' outcome. | Field | Value | |---|---| | **Arguments** | `application_id`*, `environment`, `mailbox`, `observed` | | **Access** | Account (`applications:write`) | | **API operations** | `POST /v1/applications/{application_id}/inbox-display` | ### `list_orders` Orders and payments for the account. | Field | Value | |---|---| | **Arguments** | `environment` | | **Access** | Account | | **API operations** | `GET /v1/orders`, `GET /v1/orders/{order_id}` | \* required. ## Continuing across conversations All state lives on the server. Nothing resets when a conversation ends. - **Save** the `application_id` and, while anonymous, the `guest_token` (valid 30 days, extended on use, 90 days maximum). Tell the person to keep them, or store them in your memory/notes if your client allows. - **New conversation:** call `list_applications` or `get_application` with the `guest_token`, then read `current_step` and `next_actions` and continue. - **Switch to the browser:** `create_resume_link` returns a one-time link valid for 24 hours. It opens the exact step in the web app. - **After sign-in:** once claimed, the person's account (OAuth or API key) is enough; the guest token is no longer needed. - **Edits are safe:** responses include a `version`. If you send `If-Match` and get `409 version_conflict`, re-read the application and try again. Omitting `If-Match` means "last write wins". ## What only the person can do You can do almost everything, but some actions are deliberately human-only. Guide the person through each: | Action | Why | How to guide them | |---|---|---| | **Pay** | Agents cannot spend money | Show the estimate, then share the Stripe checkout link from `create_checkout` | | **Add DNS records** | Only they (or their IT) can change the domain | Give the exact host/type/value list and where to paste it; then run `verify_dns` | | **Approve the final logo** | It's their brand | Show original vs. converted; call `approve_logo` only after an explicit yes | | **Confirm authority** | Legal attestation | Ask whether they may act for the company before `authority_attested: true` | | **Respond to the issuer** | DigiCert may email or call them | Relay `customer_actions` from `get_certificate` | | **Confirm display** | We can't see their inbox | Ask them to check Gmail, then call `confirm_inbox_display` | ## Shared email providers cannot be used Addresses at shared providers such as gmail.com, outlook.com, hotmail.com, yahoo.com, icloud.com, proton.me or aol.com cannot get a logo or checkmark. These domains belong to the provider and are shared by millions of people; the logo shown for them is the provider's own. The person needs their **own domain** (like `yourcompany.com`). The API returns `blocked_provider` (HTTP 422) with a plain-English reason; the full list is at `GET https://api.verifiedinbox.ai/v1/providers/blocked`. ## Trademark logic - **VMC requires a registered trademark** for the logo. The first release checks the **US Patent and Trademark Office (USPTO) only**. Other offices are not yet supported; a human at VerifiedInbox can review special cases (contact support). - We match the logo against USPTO records automatically (AI vision with a perceptual-hash fallback); a person at VerifiedInbox can verify a match manually when it's ambiguous (`needs_review`). - **No registered trademark?** - If the logo has been in **public use for 12+ months** (we check web archive history), offer a **CMC**: logo, no checkmark. - Offer the **trademark filing service**: the person emails `trademark@verifiedinbox.ai`. Registration usually takes several months; they can start with a CMC meanwhile, and move to a VMC later. - **Neither route yet** (new company, new logo, no trademark): the outcome is `not_currently_eligible`. Explain what's missing and when they may qualify (`estimated_eligible_on`), offer `watch_eligibility` (email reminder), and the trademark service. ## Safe DMARC rollout Logos only show when the domain's DMARC policy is at enforcement (`p=quarantine` or `p=reject`, 100% of mail). How fast to get there depends on how email is used on the domain (`set_email_usage`): - **No email yet (`none`)** or **little email (`low`):** publish the blocking record (`p=reject`) straight away. There is little or nothing to break; if a real email ever bounces, the reports show which service needs setting up. `dmarc_rollout.path` is `direct` and `recommended_record` is the blocking record. - **Email every day (`active`):** tightening too fast can send the company's own newsletters or invoices to spam if some sending service isn't set up yet. The staged plan (`dmarc_rollout.plan`, each stage with its exact record, what to monitor and what happens to email): 1. **Watch:** `p=none` with reporting (`rua=`), suggested 14 days, while every sending service is set up with SPF/DKIM. 2. **Filter some:** `p=quarantine; pct=25`, suggested 7 days. 3. **Filter all:** `p=quarantine` (100%). This meets the requirement. 4. **Block all (recommended):** `p=reject`. The waiting times are suggestions, never enforced: the person may skip ahead. If they want to, explain the risk plainly (real emails from a service that isn't set up yet could go to spam or bounce) and give them the record of the stage they choose. ## Pricing - **Free:** domain check, full email security report, logo preparation and preview, USPTO trademark lookup, eligibility result and price estimate. - **CMC plan:** $899 per domain per year. **VMC plan:** $1,499 per domain per year. Includes business verification, logo preparation, certificate ordering, hosting of the logo and certificate, monitoring, automatic renewals and reminders, and support. - **Certificate issuer fee:** charged by the issuer (DigiCert), passed through **without markup**, collected at the same checkout as a **separate line item**. About **$750–$1,750 per year**; current estimates: VMC ~$1,350, CMC ~$950. Example totals: VMC ≈ $2,849/year, CMC ≈ $1,849/year. - The **estimated total is always shown before payment** (`get_estimate`). - **Full refund** if the certificate issuer rejects the application. - Prices are per domain. Payment is by Stripe's hosted checkout. ## Test mode (sandbox) Signed-in users can switch between **live** and **test** environments (like Stripe). In test mode nothing is charged (Stripe test mode), certificates come from a simulated issuer, and data is kept separate. Use `vi_test_…` API keys or pass `environment: "test"` to account tools. Use test mode when the person wants to see the whole flow before paying, or when you are building an integration. ## REST API fallback Use this if MCP isn't available but you can make HTTP requests. Every tool above has an equivalent REST operation. - **OpenAPI 3.1 document (complete contract):** `https://verifiedinbox.ai/openapi.json` (also `https://api.verifiedinbox.ai/openapi.json`). Load it if you can; each operation lists who may call it (`x-access`), whether it needs an `Idempotency-Key` (`x-idempotency`) and the matching MCP tool (`x-mcp-tools`). - **Base URL:** `https://api.verifiedinbox.ai` (API paths start with `/v1`). JSON bodies, UTF-8. Money is in **cents** (USD). - **Auth headers:** none for public endpoints; `VI-Guest-Token: vi_guest_…` (or `Authorization: Bearer vi_guest_…`) to continue an anonymous application; `Authorization: Bearer vi_live_…` / `vi_test_…` for an account. A guest token can be combined with an account credential (send it in `VI-Guest-Token`) to claim. - **Idempotency:** send `Idempotency-Key: ` where an operation's `x-idempotency` says so: required for checkout and certificate operations, optional when starting an application. - Human-readable reference: [API docs](https://verifiedinbox.ai/docs/reference/api/v1.md). ### Meta Service metadata, health and this contract. | Endpoint | Summary | Access & MCP | |---|---|---| | `GET /health` | Liveness check | Public — no credentials needed | | `GET /openapi.json` | This OpenAPI document | Public — no credentials needed | | `GET /ready` | Readiness check | Public — no credentials needed | | `GET /v1/meta` | Pricing, steps and live/mocked integrations | Public — no credentials needed
MCP: `get_started` | ### Checks Free, repeatable public checks of any domain. | Endpoint | Summary | Access & MCP | |---|---|---| | `POST /v1/checks/domain` | Quick domain eligibility check | Public — no credentials needed
MCP: `check_domain` | | `POST /v1/checks/email-auth` | Full SPF/DKIM/DMARC/BIMI inspection | Public — no credentials needed
MCP: `check_email_security` | | `GET /v1/providers/blocked` | Shared mailbox domains that can't be used | Public — no credentials needed | ### Glossary Plain-English definitions of every term we use. | Endpoint | Summary | Access & MCP | |---|---|---| | `GET /v1/glossary` | All glossary terms | Public — no credentials needed | | `GET /v1/glossary/{term}` | One glossary term | Public — no credentials needed
MCP: `explain_term` | ### Guest sessions Anonymous credentials and resume links. | Endpoint | Summary | Access & MCP | |---|---|---| | `POST /v1/guest-sessions` | Create a guest credential | Public — no credentials needed | | `DELETE /v1/guest-sessions/current` | Revoke the current guest session | Free — guest or account | | `POST /v1/resume` | Exchange a one-time resume token | Public — no credentials needed | ### Applications The server-side state of one domain's journey. | Endpoint | Summary | Access & MCP | |---|---|---| | `GET /v1/applications` | List my verifications | Free — guest or account
MCP: `list_applications`, `get_account` | | `POST /v1/applications` | Start (or resume) a verification | Public — no credentials needed
MCP: `start_application` | | `GET /v1/applications/{application_id}` | Get a verification | Free — guest or account
MCP: `get_application` | | `PATCH /v1/applications/{application_id}` | Choose product / contact email | Free — guest or account | | `POST /v1/applications/{application_id}/archive` | Archive (unpaid only) | Free — guest or account | | `POST /v1/applications/{application_id}/claim` | Move a guest draft into the account | Account required
MCP: `claim_application` | | `GET /v1/applications/{application_id}/events` | Timeline of the verification | Free — guest or account | | `GET /v1/applications/{application_id}/next-steps` | Current step and next actions | Free — guest or account
MCP: `get_application` | | `POST /v1/applications/{application_id}/refresh` | Re-run checks now | Free — guest or account | | `POST /v1/applications/{application_id}/resume-links` | One-time link to continue in a browser | Free — guest or account
MCP: `create_resume_link` | | `POST /v1/applications/{application_id}/steps/{step}/complete` | Complete a wizard step and move on | Free — guest or account
MCP: `complete_step` | ### Email security SPF, DKIM, DMARC and BIMI for an application. | Endpoint | Summary | Access & MCP | |---|---|---| | `POST /v1/applications/{application_id}/dkim-selectors` | Verify DKIM selectors you supply | Free — guest or account
MCP: `verify_dkim_selectors` | | `PUT /v1/applications/{application_id}/dmarc-reports` | Where daily DMARC reports go | Free — guest or account
MCP: `set_dmarc_report_address` | | `GET /v1/applications/{application_id}/email-security` | Latest email-security report + DMARC rollout plan | Free — guest or account | | `POST /v1/applications/{application_id}/email-security/check` | Re-check email security now | Free — guest or account
MCP: `recheck_email_security` | | `PUT /v1/applications/{application_id}/email-usage` | How email is used on the domain | Free — guest or account
MCP: `set_email_usage` | ### Logo Logo intake, SVG Tiny P/S conversion and approval. | Endpoint | Summary | Access & MCP | |---|---|---| | `GET /v1/applications/{application_id}/logo` | Logo status, analysis and changes | Free — guest or account
MCP: `get_logo_status` | | `PUT /v1/applications/{application_id}/logo` | Submit the logo | Free — guest or account
MCP: `submit_logo` | | `GET /v1/applications/{application_id}/logo-candidates` | Logos found on the website | Free — guest or account
MCP: `get_logo_status` | | `POST /v1/applications/{application_id}/logo/approve` | Approve the reviewed inbox-ready logo | Paid plan required
MCP: `approve_logo` | | `GET /v1/applications/{application_id}/logo/converted.svg` | Inbox-ready SVG Tiny P/S file | Free — guest or account | | `GET /v1/applications/{application_id}/logo/original.svg` | Original logo file | Free — guest or account | | `GET /v1/applications/{application_id}/logo/preview.png` | Rendered PNG preview | Free — guest or account | ### Trademark USPTO trademark search and verification. | Endpoint | Summary | Access & MCP | |---|---|---| | `GET /v1/applications/{application_id}/trademark` | Trademark result | Free — guest or account
MCP: `get_trademark_status` | | `POST /v1/applications/{application_id}/trademark/claim` | Verify a specific USPTO number | Free — guest or account
MCP: `search_trademark` | | `POST /v1/applications/{application_id}/trademark/search` | (Re)start the USPTO search | Free — guest or account
MCP: `search_trademark` | ### Eligibility VMC / CMC / not-yet-eligible outcome and price estimate. | Endpoint | Summary | Access & MCP | |---|---|---| | `GET /v1/applications/{application_id}/eligibility` | VMC / CMC / not-yet-eligible with evidence | Free — guest or account
MCP: `get_eligibility` | | `POST /v1/applications/{application_id}/eligibility/watch` | Email me when this domain qualifies | Free — guest or account
MCP: `watch_eligibility` | | `GET /v1/applications/{application_id}/estimate` | Itemized yearly price | Free — guest or account
MCP: `get_estimate` | ### Checkout Stripe hosted checkout (agents create links; people pay). | Endpoint | Summary | Access & MCP | |---|---|---| | `POST /v1/applications/{application_id}/checkout` | Create a Stripe checkout link | Account required
MCP: `create_checkout` | ### Business Legal company verification. | Endpoint | Summary | Access & MCP | |---|---|---| | `GET /v1/applications/{application_id}/business` | Company verification status | Free — guest or account
MCP: `get_business_status` | | `PUT /v1/applications/{application_id}/business` | Submit legal company details | Paid plan required
MCP: `submit_business_details` | ### DNS The DNS records to add, with live status. | Endpoint | Summary | Access & MCP | |---|---|---| | `GET /v1/applications/{application_id}/dns` | Every DNS record to add, with live status | Paid plan required
MCP: `get_dns_records` | | `POST /v1/applications/{application_id}/dns/verify` | Re-check DNS now | Paid plan required
MCP: `verify_dns` | ### Test messages Authentication results on a real email. | Endpoint | Summary | Access & MCP | |---|---|---| | `POST /v1/applications/{application_id}/test-messages` | Get an address to send a real test email to | Free — guest or account
MCP: `create_test_message` | | `GET /v1/applications/{application_id}/test-messages/{test_message_id}` | Authentication results of the test email | Free — guest or account
MCP: `get_test_message_result` | ### Certificate Certificate order, status, reissue, renewal and revocation. | Endpoint | Summary | Access & MCP | |---|---|---| | `GET /v1/applications/{application_id}/certificate` | Certificate status, prerequisites, final check and history | Free — guest or account
MCP: `get_certificate` | | `POST /v1/applications/{application_id}/certificate/check` | Re-check everything the issuer will verify | Paid plan required
MCP: `run_final_check` | | `POST /v1/applications/{application_id}/certificate/order` | Submit the order (normally automatic) | Paid plan required
MCP: `order_certificate` | | `POST /v1/applications/{application_id}/certificate/reissue` | Reissue the certificate | Paid plan required
MCP: `reissue_certificate` | | `POST /v1/applications/{application_id}/certificate/renew` | Renew early | Paid plan required
MCP: `renew_certificate` | | `POST /v1/applications/{application_id}/certificate/revoke` | Revoke permanently | Paid plan required
MCP: `revoke_certificate` | ### Go live BIMI publication and display confirmation. | Endpoint | Summary | Access & MCP | |---|---|---| | `GET /v1/applications/{application_id}/go-live` | BIMI publication, test email, display, monitoring, renewal | Free — guest or account | | `POST /v1/applications/{application_id}/inbox-display` | Record that the logo was seen in a mailbox | Paid plan required
MCP: `confirm_inbox_display` | ### Account Profile, organizations, environment, API keys, orders. | Endpoint | Summary | Access & MCP | |---|---|---| | `GET /v1/api-keys` | API keys | Account required | | `POST /v1/api-keys` | Create an API key (web session) | Account required | | `DELETE /v1/api-keys/{api_key_id}` | Revoke an API key | Account required | | `POST /v1/billing/portal` | Stripe billing portal link | Account required | | `GET /v1/me` | Who am I (works anonymously too) | Public — no credentials needed
MCP: `get_account` | | `POST /v1/me/environment` | Switch live/test mode (web session) | Account required | | `POST /v1/me/organization` | Switch organization (web session) | Account required | | `GET /v1/notifications` | Notification log | Account required | | `GET /v1/orders` | Orders and payments | Account required
MCP: `get_account`, `list_orders` | | `GET /v1/orders/{order_id}` | One order | Account required
MCP: `list_orders` | | `GET /v1/organizations` | My organizations | Account required | | `POST /v1/organizations` | Create an organization | Account required | | `GET /v1/organizations/{organization_id}` | One organization | Account required | | `PATCH /v1/organizations/{organization_id}` | Rename an organization | Account required | | `GET /v1/organizations/{organization_id}/members` | Organization members | Account required | ### Support Contact a human, feedback and feature requests. | Endpoint | Summary | Access & MCP | |---|---|---| | `POST /v1/support` | Contact a human, feedback, feature request, partnership | Public — no credentials needed
MCP: `contact_support`, `send_feedback` | ### Hosting Public logo and certificate files referenced by BIMI records. | Endpoint | Summary | Access & MCP | |---|---|---| | `GET /bimi/test/{domain}/{file}` | Hosted BIMI assets (test mode) | Public — no credentials needed | | `GET /bimi/{domain}/{file}` | Hosted BIMI logo / certificate | Public — no credentials needed | ### MCP Model Context Protocol endpoint and OAuth discovery. | Endpoint | Summary | Access & MCP | |---|---|---| | `GET /.well-known/oauth-protected-resource` | OAuth protected-resource metadata (RFC 9728) | Public — no credentials needed | | `GET /.well-known/oauth-protected-resource/mcp` | OAuth protected-resource metadata for /mcp | Public — no credentials needed | | `POST /mcp` | MCP endpoint (Streamable HTTP, JSON responses) | Public — no credentials needed | ### Test mode Sandbox helpers (no real payments). | Endpoint | Summary | Access & MCP | |---|---|---| | `POST /v1/dev/mock-checkout/{order_id}/complete` | Simulate payment of a mock checkout | Account required | Example: ```bash # 1. Quick check (no credentials) curl -s https://api.verifiedinbox.ai/v1/checks/domain \ -H 'Content-Type: application/json' -d '{"domain":"acme.com"}' # 2. Start an application; save application.id and guest_token from the response curl -s https://api.verifiedinbox.ai/v1/applications \ -H 'Content-Type: application/json' -H 'Idempotency-Key: start-acme-1' \ -d '{"domain":"acme.com"}' # 3. Continue later with the guest token curl -s https://api.verifiedinbox.ai/v1/applications/APPLICATION_ID \ -H 'VI-Guest-Token: vi_guest_…' ``` ## Errors and limits Errors look like `{ "error": { "code", "message", "details"?, "next_action"? } }`. The `message` is plain English and safe to show the person. MCP tool failures return `isError: true` with the same message and `next_actions`. | Code (HTTP) | Meaning | What to do | |---|---|---| | `unauthenticated` (401) | This needs an account | Sign in via OAuth, use an API key, or give the person a resume link | | `payment_required` (402) | This step is paid | Show the estimate and create a checkout link | | `insufficient_scope` / `forbidden` (403) | The key lacks a scope | Ask the person for a key with the needed scope (`applications:read`, `applications:write`, `billing:write`, `certificates:write`, `support:write`) | | `not_found` (404) | Wrong id, or no access | Check the id and credentials (we never reveal whether someone else's application exists) | | `version_conflict` (409) | Someone else changed it | Re-read the application and retry | | `blocked_provider` (422) | Shared email domain | Ask for their own company domain | | `rate_limited` (429) | Too many requests | Wait `Retry-After` / `retry_after_seconds`, then retry once | | `upstream_unavailable` (503) | A provider is temporarily down | Retry later; long-running work continues in the background | Anonymous usage is rate-limited per IP and per guest, and expensive work (trademark searches, AI logo comparison) has a daily budget. Poll gently: when background work is running, check again after a minute or when the person comes back, not in a tight loop. ## Help, support and feedback - `contact_support` — message a human (use `kind: "contact_human"` when the person asks for a person, `support` for help with a step). Include the domain and `application_id`. A human replies by email, usually within one business day. - `send_feedback` — feedback or a feature request. - `explain_term` — plain-English definition of any term (DMARC, BIMI, VMC…). - Email: `support@verifiedinbox.ai` · trademark filing: `trademark@verifiedinbox.ai` · web: [verifiedinbox.ai/support](https://verifiedinbox.ai/support.md). Everything can be done self-service; humans are there if the person wants one. ## How to behave - **Plain language.** Say "fake-email protection" before "DMARC p=quarantine". Offer technical detail if they ask. - **Be honest about outcomes.** Never claim the checkmark or logo display is guaranteed; Gmail decides. Never promise a checkmark to someone on the CMC route. - **Never ask for passwords**, card numbers, DNS-provider logins or one-time codes. Payment happens only on Stripe's page; DNS changes happen in their DNS provider. - **Confirm before consequential actions:** creating a checkout (show the total first), attesting authority, approving the logo, and especially revoking a certificate. - **Use `next_actions`.** Every response tells you what's next, whether it needs sign-in or payment, and which tool to call. - **Keep continuity.** Save the `application_id` and `guest_token`; at the start of a new conversation, look up the existing application instead of starting over. - **Respect the person's time.** Batch what they need to do (e.g. all DNS records at once) and explain how long waits usually take. - If anything is unclear or stuck, use `contact_support`, and tell the person a human will follow up by email. --- # VerifiedInbox for AI agents and assistants > What VerifiedInbox is for, what an AI assistant can do with it, where the boundaries are, and how to connect over MCP or the REST API. **In one sentence:** VerifiedInbox lets an AI assistant take a company's email domain all the way to its logo, and Gmail's blue verified checkmark, shown next to the company's emails, while the person only steps in to pay, edit DNS records and approve their logo. If you are an agent about to do the work, read the complete guide: [get-started.md](https://verifiedinbox.ai/get-started.md) (also [as a web page](https://verifiedinbox.ai/get-started.md)). It contains every step, tool name, error and rule. ## Why this exists When a company sends email, most inboxes show a plain letter next to its name. With the right setup (a standard called [BIMI](https://verifiedinbox.ai/docs/glossary.md#bimi) plus a mark certificate), Gmail and other inboxes show the company's real logo instead, and with a [Verified Mark Certificate](https://verifiedinbox.ai/docs/glossary.md#vmc), Gmail adds a blue checkmark. Getting there normally takes weeks of back-and-forth: email security settings, a special logo file format, trademark evidence, business verification, a certificate order, DNS records, hosting, and a renewal every year. VerifiedInbox does all of it as a single service, and it was built **AI-first**: every operation in the web app is also an API call and an MCP tool, the server keeps all state, and every response tells the caller what to do next. ## Who you are helping Mostly non-technical people: marketing managers, sales leaders and founders who want their brand recognized in the inbox and want customers to trust their emails. Talk to them in plain English. Many will paste a single sentence into their assistant, for example: > Add a verified VMC mark to my email domain using verifiedinbox.ai/get-started.md ## What you can do | Capability | Without an account | With an account | |---|---|---| | Check a domain (blocked providers, registration, mailbox provider, DMARC/BIMI summary) | ✓ | ✓ | | Full SPF, DKIM, DMARC and BIMI inspection with plain-English fixes | ✓ | ✓ | | Start and resume an application; read its full state and next steps | ✓ | ✓ | | Submit a logo; get compliance analysis, conversion changes and previews | ✓ | ✓ | | USPTO trademark search or verification of a registration number | ✓ (rate-limited) | ✓ | | Eligibility (VMC, CMC, not currently eligible) and a price estimate | ✓ | ✓ | | Get notified when the domain becomes eligible | ✓ | ✓ | | Hand off to the browser with a one-time resume link | ✓ | ✓ | | Contact a human, send feedback or a feature request | ✓ | ✓ | | Claim drafts into an organization; see all domains and orders | | ✓ | | Create a checkout link (the person pays) | | ✓ | | Submit business details; read verification status | | ✓ | | Approve the final logo (after the person says yes) | | ✓ | | Get DNS records and verify them against authoritative nameservers | | ✓ | | Send-a-test-email alignment check | | ✓ | | Certificate status, renew early, reissue, revoke (with confirmation) | | ✓ | | Record that the logo was seen in Gmail | | ✓ | Signed-in users can use **test mode** (sandbox): the whole flow without charges or real certificates. ## Boundaries (by design) - **Agents cannot pay.** You can create a Stripe checkout link; only the person can complete it. - **Agents do not change DNS.** You give exact records; the person or their IT adds them. We verify within seconds. - **The person approves the logo.** Approval is tied to the exact file they reviewed. - **Revocation needs an explicit confirmation string** (`REVOKE `) and the `certificates:write` scope. - **No access by guessing.** Knowing a domain or an application id grants nothing; inaccessible resources return 404. - **Shared email domains** (gmail.com, outlook.com, icloud.com, …) cannot be used; the person needs their own domain. ## Connect - **MCP server:** `https://api.verifiedinbox.ai/mcp` (Streamable HTTP). Connect anonymously to start; sign in with OAuth (WorkOS) or use an API key (`vi_live_…` / `vi_test_…`) for account tools. Setup for Claude, ChatGPT, Cursor and Claude Code: [get-started.md → Connect](https://verifiedinbox.ai/get-started.md#connect-to-the-mcp-server). - **REST API:** `https://api.verifiedinbox.ai/v1` — [API reference](https://verifiedinbox.ai/docs/reference/api/v1.md). - **OpenAPI 3.1 schema:** [`https://verifiedinbox.ai/openapi.json`](https://verifiedinbox.ai/openapi.json) (also at `https://api.verifiedinbox.ai/openapi.json`). The complete REST contract; the MCP tools' arguments are generated from it. Each operation says who may call it (`x-access`), whether it needs an `Idempotency-Key` (`x-idempotency`) and which MCP tool does the same thing (`x-mcp-tools`). - **MCP reference:** [all tools](https://verifiedinbox.ai/docs/mcp.md). - **Markdown everywhere:** every page on this site has a markdown version at the same URL plus `.md` (for example [/pricing.md](https://verifiedinbox.ai/pricing.md)), and requests with `Accept: text/markdown` get markdown too. Index: [/llms.txt](https://verifiedinbox.ai/llms.txt) · everything in one file: [/llms-full.txt](https://verifiedinbox.ai/llms-full.txt). ## The outcomes to report Keep these separate when you talk to the person; they are tracked separately by the API: 1. **Eligibility assessed** — VMC (logo + checkmark), CMC (logo only) or not currently eligible. 2. **Certificate issued** — by the certificate issuer (DigiCert). 3. **BIMI published** — the DNS record and hosted files are live and correct. 4. **Display observed** — the person saw the logo (and checkmark, for VMC) in a real Gmail inbox. ## Pricing, in brief Free checker. CMC $899 and VMC $1,499 per domain per year, including business verification, logo preparation, certificate ordering, hosting, monitoring, automatic renewals and support. The certificate issuer's own fee (about $750–$1,750 per year) is separate, passed through without markup and shown before payment. Full refund if the issuer rejects the application. Details: [pricing](https://verifiedinbox.ai/pricing.md). ## Guidelines for assistants 1. Use plain language; offer technical detail only when asked. 2. Never say the checkmark or logo display is guaranteed; Gmail decides what to show. 3. Never ask for passwords, card numbers or DNS-provider logins. 4. Confirm with the person before creating a checkout, attesting authority, approving a logo or revoking a certificate. 5. Save the `application_id` and `guest_token` so you can continue in a later conversation. 6. Follow `next_actions` in every response. 7. If the person wants a human, use `contact_support` with `kind: "contact_human"`. --- # VerifiedInbox — your logo and blue checkmark in your customers' inboxes > Enter your company domain. We check it for free, then handle the certificate, the setup and every renewal, so your logo, and Gmail's blue checkmark, show up next to your emails. Most inboxes show a plain grey letter next to your company's name. With VerifiedInbox, Gmail shows **your logo**, and with a registered trademark, **Gmail's blue verified checkmark**. Other inboxes that support the same standard (BIMI), such as Apple Mail and Yahoo Mail, can show your logo too. **Start:** enter your domain at [verifiedinbox.ai](https://verifiedinbox.ai/index.md) — free, no account needed. Or ask your AI assistant: *"Add a verified VMC mark to my email domain using verifiedinbox.ai/get-started.md"*. ## Why it matters - **Be recognized.** Your logo stands out in a list of grey letters, so people spot your emails at a glance. - **Be trusted.** The blue checkmark tells readers Gmail has verified that the email really comes from you. - **Be harder to fake.** Getting there requires strong anti-impersonation settings on your domain, which makes it harder for scammers to send email in your name. ## How it works 1. **Check your domain — free.** We look at your email settings and explain, in plain English, what's ready and what needs attention. 2. **Add your logo — free.** Upload it or use the one on your website. We prepare the special file format inboxes need, keep your original untouched, and check the US trademark office. 3. **See if you qualify — free.** You'll know whether you can get the logo with the blue checkmark, the logo only, or what's missing, and see the full price before paying. 4. **We set it up and keep it live.** When you're ready, pay once a year. We verify your business, order the certificate, give you copy-paste settings, host everything and renew it every year. ## We take care of everything Certificates expire about every 13 months. If one lapses, your logo quietly disappears. We make sure that doesn't happen: - **Automatic renewals** before your certificate expires. - **Reminders** 60, 30, 14, 7 and 1 day before anything is due. - **Hosting** of your logo and certificate on fast, secure servers. - **Monitoring** of your settings, with an alert if something breaks. - **Business and logo checks** prepared for the certificate issuer. - **Real people** if you want one. ## Checkmark or logo only? - **Logo + blue checkmark (VMC)** — needs a registered trademark for your logo (we check the US Patent and Trademark Office). $1,499 per domain per year, plus the issuer's fee. - **Logo only (CMC)** — no trademark needed; your logo must have been in public use for at least 12 months. $899 per domain per year, plus the issuer's fee. - **Neither yet?** New logo and no trademark? We tell you exactly what's missing, notify you when you qualify, and can help you file a trademark: email [trademark@verifiedinbox.ai](mailto:trademark@verifiedinbox.ai). ## Use it from your AI assistant Copy this prompt into Claude, ChatGPT or Cursor: > Add a verified VMC mark to my email domain using verifiedinbox.ai/get-started.md Your assistant connects to our MCP server (`https://api.verifiedinbox.ai/mcp`), runs the checks, prepares your logo and tracks progress across conversations. You only step in to pay, add the settings to your domain and approve your logo. Details for assistants: [For AI](https://verifiedinbox.ai/ai.md). ## Pricing | | Price | |---|---| | Checker | Free | | Logo (CMC) | $899 per domain per year | | Logo + blue checkmark (VMC) | $1,499 per domain per year | | Certificate issuer fee (DigiCert) | About $750–$1,750 per year, separate, no markup | You see the estimated total before you pay, and get a full refund if the issuer rejects your application. Details: [pricing](https://verifiedinbox.ai/pricing.md). ## Frequently asked questions **Do I need to be technical?** No. We explain every step in plain English and give you exact settings to copy and paste. You can share the setup page with whoever manages your domain, or talk to a human. **What do I need for the blue checkmark?** Your own company domain, a registered trademark for your logo, and a few email security settings we walk you through. The free check tells you which you already have. **My logo isn't trademarked. Can I still show it?** Usually yes: with a CMC, if your logo has been public for at least 12 months. Gmail shows the logo without the checkmark. We can also help you file a trademark. **How long does it take?** The free checks take about a minute. After payment, the certificate issuer verifies your business and logo, which usually takes from a few days to a few weeks. **Is the checkmark guaranteed?** No one can guarantee it; Gmail decides what to show. We make sure everything on your side is set up correctly and stays that way. If the issuer rejects your application, you get a full refund. **What happens when the certificate expires?** It won't lapse while your plan is active: we remind you early and renew automatically. **Can I try it without paying?** Yes. All checks are free and need no account. Signed-in users also get a test mode that runs every step without charges or real certificates. **Why can't I use my @gmail.com address?** Shared addresses like gmail.com, outlook.com or icloud.com belong to the provider, so the logo shown is theirs. You need your own domain. **Which inboxes show my logo?** Gmail shows your logo, plus the blue checkmark with a VMC. Other inboxes that support BIMI, including Apple Mail and Yahoo Mail, can show your logo too; each provider sets its own rules. ## Talk to a human Everything can be done self-service; humans are here if you want one: [contact us](https://verifiedinbox.ai/support?kind=contact_human) or email [support@verifiedinbox.ai](mailto:support@verifiedinbox.ai). --- # Pricing > Free checks. One yearly price per domain when you're ready. The certificate issuer's fee is separate, passed through without markup, and shown before you pay. ## Plans (per domain, per year) ### Checker: Free No account needed. Find out exactly where you stand. - Clear domain and email security check - Logo preparation and Gmail preview - US trademark lookup - Eligibility result with next steps - Full price estimate ### Logo only: $899 per domain per year Certificate: Common Mark Certificate (CMC). Your logo in the inbox, without the checkmark. Plus the certificate issuer's fee, estimated at $950 per year. - No trademark needed - Logo in public use for 12+ months - Everything listed under “included” below ### Logo + blue checkmark: $1,499 per domain per year Certificate: Verified Mark Certificate (VMC). Your logo and Gmail's blue verified checkmark. Plus the certificate issuer's fee, estimated at $1,350 per year. - Registered trademark for your logo (USPTO) - The strongest trust signal in Gmail - Everything listed under “included” below ## Included in both paid plans - **Business verification.** We confirm your company and your authority, as the issuer requires. - **Logo preparation.** Converted to the inbox format, compared visually, your original kept. - **Certificate ordering.** We prepare and submit the order and handle the issuer's questions. - **Hosting.** Your logo and certificate, served fast and securely for every inbox. - **Monitoring.** We keep checking your settings and alert you if anything breaks. - **Renewals & reminders.** Automatic renewal, with reminders 60, 30, 14, 7 and 1 day ahead. - **Support.** Real people by email whenever you want one. Per domain, per year. Most companies need one. ## The certificate issuer's fee is separate Your certificate is issued by an independent certificate issuer, **DigiCert**, which verifies your business and logo and charges its own yearly fee. - Collected by us at the same checkout, as a separate line - No markup: passed through at cost - About $750–$1,750 per year, depending on the certificate - The estimated total is shown before you pay ## Estimated yearly total | Item | Logo only (CMC) | Logo + blue checkmark (VMC) | |---|---|---| | Our plan | $899 | $1,499 | | Est. DigiCert certificate fee | $950 | $1,350 | | **Est. total per year** | **$1,849** | **$2,849** | Estimates use current issuer pricing. Your exact amount is shown at checkout. Prices in US dollars. Full refund if the certificate issuer rejects your application. ## Our promises - **Refund if rejected.** If the certificate issuer rejects your application, you get a full refund of the plan and the issuer fee. - **Estimate first.** The estimated total is shown before you pay, on the website, in the API and through your AI assistant. - **Try it in test mode.** Signed-in users can walk through every step, including checkout, without being charged. - **Cancel anytime.** Your plan simply doesn't renew. Everything stays live until the end of the year you paid for. ## Pricing questions **Why is the issuer's fee separate?** Your certificate comes from an independent certificate issuer (DigiCert) that does its own checks of your business and logo. Their fee is theirs; we pass it through at cost, so you pay once, in one place. **How much is the issuer's fee?** About $750–$1,750 per year, depending on the certificate type. Current estimates: $1,350 for the checkmark (VMC) and $950 for logo only (CMC). Your exact amount is shown before you pay. **What if my application is rejected?** If the certificate issuer rejects your application, you get a full refund of the plan and the issuer fee. **Is the price per domain?** Yes. Each domain that should show your logo has its own plan. Most companies need one. **Does the price include a trademark?** No. Trademark filing is a separate service: email trademark@verifiedinbox.ai. Meanwhile, you may qualify for the logo-only plan. **Can I move from logo-only to the checkmark later?** Yes. Once your trademark is registered, contact us and we'll move you to the checkmark plan. **What happens if I cancel?** Your plan simply doesn't renew. Your logo, certificate and monitoring stay live until the end of the year you paid for. **How do I pay?** By card, through Stripe's secure checkout. Your AI assistant can prepare the checkout link, but only you can pay. Prices are in US dollars. **Can I try it without paying?** Yes. All checks are free and need no account. Test mode lets signed-in users walk through every step without charges. Questions about larger rollouts or many domains? [Talk to a human](https://verifiedinbox.ai/support?kind=contact_human). --- # Partners > VerifiedInbox turns "we want the blue checkmark" into prepared, pre-validated, DNS-ready applications, through a web app, a REST API and an MCP server that AI assistants use. Partner with us. Companies want their logo, and Gmail's blue checkmark, next to their emails. Most give up somewhere between DMARC settings, logo file formats, trademark evidence and certificate paperwork. VerifiedInbox removes that friction: we prepare every applicant end to end, and we keep them live and renewed year after year. ## Who we work with ### Certificate issuers We bring you **prepared, pre-validated applicants**: - Domain control already proven (DNS TXT), and DMARC at enforcement or on a tracked rollout plan. - Logo converted to SVG Tiny P/S with a visual comparison against the original, and approved by the customer. - Trademark matched against USPTO records (AI vision plus manual review), or prior-use evidence for CMC. - Legal entity checked against company registries; applicant authority confirmed. - BIMI records prepared and verified against authoritative nameservers. The result: fewer back-and-forths, fewer abandoned orders, lower validation cost per certificate, and renewals that happen on time. Our pipeline is API- and MCP-driven, so demand also arrives from AI assistants acting for their users. ### Registrars and DNS hosts Offer "your logo in the inbox" to the domains you already manage. We generate the exact records for each domain and verify them in seconds; our API lets you show status inside your own dashboard. ### Email service providers and marketing platforms Your customers already care about open rates, brand and deliverability. Embed our free eligibility check and setup flow in your product; we handle certificates, hosting, monitoring and renewals. ### Agencies and resellers Manage many client domains from one account with multiple organizations. We do the paperwork; you own the relationship. ### Trust-framework and identity partners Domain control, business identity, trademark evidence and logo provenance in one auditable pipeline. We're interested in verified-organization data, KYB providers and mark-certificate standards work. ## What partners get - **Prepared applicants** — validated data and documents, not raw leads. - **API-first pipeline** — every step is an API call and an MCP tool, with explicit state machines and separate outcomes (eligibility, certificate issued, BIMI published, display observed). - **Retention built in** — automatic renewals with reminders at 60, 30, 14, 7 and 1 day. - **Clear responsibilities** — the issuer validates and issues; we prepare, host, monitor and renew. - **Sandbox** — a full test environment with a simulated issuer and test payments. ## How integration works 1. **REST API** — `https://api.verifiedinbox.ai/v1`. Start with `POST /v1/checks/domain` and `POST /v1/applications`; read state from `GET /v1/applications/{id}`. Every response includes `next_actions`. [API reference](https://verifiedinbox.ai/docs/reference/api/v1.md). 2. **Events and webhooks** — every application has a timeline at `GET /v1/applications/{id}/events`. Partner webhooks for status changes (for example "certificate issued" or "renewal due") are set up with you during onboarding. 3. **MCP** — `https://api.verifiedinbox.ai/mcp`, the same capabilities for AI assistants. [MCP reference](https://verifiedinbox.ai/docs/mcp.md). 4. **Issuer operations** — for certificate issuers, orders, validation status, customer actions, issuance and revocation are mapped to our certificate state machine, via your API where available and an operator back office where not. ## Get in touch Tell us who you are and what you'd like to build: [partnership inquiry](https://verifiedinbox.ai/support?kind=partnership). --- # About VerifiedInbox > VerifiedInbox is on a mission to make email trustworthy again: a verified logo next to real senders, real protection against fakes, and reliable signals for the AI agents that will read email for us. Email is how the world signs in, pays bills, does business and keeps in touch. It is also the easiest place to pretend to be someone else. We want every person and every business to know who an email really comes from. ## Our mission Real senders should look real, fakes should have nowhere to hide, and the AI tools we rely on should know the difference. - **Real senders, easy to spot.** A verified logo and checkmark next to real senders, so anyone can tell a real email from a fake at a glance. - **Fewer fakes in every inbox.** Most scams work because anyone can pretend to send email from someone else's address. We help every domain switch on the protection that stops this ([DMARC](https://verifiedinbox.ai/docs/glossary.md#dmarc)) and prove who is behind it. - **Ready for AI that reads your email.** AI agents will soon read, sort, answer and act on email for people. They need a reliable way to know who a sender really is. Verified domains give them one. ## How we work We do the hard parts: - **We run the checks.** Your email settings, logo, trademark and business, checked for you. - **We get the certificate.** We prepare everything the certificate issuer needs and order it for you. - **We host and watch.** Your logo and certificate stay online, and we alert you if anything breaks. - **We renew every year.** No expired certificates, no logo quietly disappearing. Use VerifiedInbox on the web, from your own software with our [API](https://verifiedinbox.ai/docs/reference/api/v1.md), or let an AI assistant do it through our [MCP server](https://verifiedinbox.ai/docs/mcp.md). ## Company VerifiedInbox is a product of Email Intelligence LLC. Email Intelligence LLC 123 Placeholder Avenue, Suite 100 Chicago, IL 60601 United States [Talk to a human](https://verifiedinbox.ai/support?kind=contact_human). ## Made by thinking machines in Illinois Most of VerifiedInbox is built and run by AI agents, with a few humans keeping an eye on them. They read the fine print, double check every record and never forget a renewal. They still can't make coffee. We're working on it. --- # Support > Talk to a human, get help with a step, send feedback or a feature request, or start a partnership conversation. Everything can be done self-service; humans are here if you want one. ## How to reach us - **Web form:** [verifiedinbox.ai/support](https://verifiedinbox.ai/support.md). Choose a topic: talk to a human, get help, feedback, feature request or partnership. - **Email:** [support@verifiedinbox.ai](mailto:support@verifiedinbox.ai). - **Trademark filing service:** [trademark@verifiedinbox.ai](mailto:trademark@verifiedinbox.ai). - **From your AI assistant:** the MCP tools `contact_support` (kinds `contact_human`, `support`) and `send_feedback` (feedback or feature request). - **API:** `POST https://api.verifiedinbox.ai/v1/support` with `{ "kind": "contact_human" | "support" | "feedback" | "feature_request" | "partnership", "message", "email", "name"?, "company"?, "application_id"? }`. Works without an account (email required). A human replies by email, usually within one business day. If your question is about a specific domain, include the domain (and your application id if you have it). ## Before you write Many answers are in the docs: [how it works](https://verifiedinbox.ai/docs/how-it-works.md), [eligibility](https://verifiedinbox.ai/docs/eligibility.md), [BIMI & DNS setup](https://verifiedinbox.ai/docs/bimi.md), [certificates and renewals](https://verifiedinbox.ai/docs/certificates-and-renewals.md) and the [FAQ](https://verifiedinbox.ai/docs/faq.md). --- # VerifiedInbox documentation > How to get your logo, and Gmail's blue checkmark, next to your company's emails, and how we keep it there. VerifiedInbox gets your company's logo shown next to the emails you send, and, if your logo is a registered trademark, adds Gmail's [blue checkmark](https://verifiedinbox.ai/docs/glossary.md#blue-checkmark). You enter your domain; we take care of the checks, the logo file, the certificate, the setup and every renewal. You can do everything three ways: - **On the website:** a step-by-step wizard. Start on the [home page](https://verifiedinbox.ai/index.md). - **With your AI assistant:** paste *"Add a verified VMC mark to my email domain using verifiedinbox.ai/get-started.md"* into Claude, ChatGPT or Cursor. See [MCP](https://verifiedinbox.ai/docs/mcp.md). - **With the API:** for developers and platforms. See the [API Reference](https://verifiedinbox.ai/docs/reference/api/v1.md) and the [OpenAPI schema](https://verifiedinbox.ai/openapi.json). ## The short version 1. **Free checks.** Enter your domain. We check your email settings, prepare your logo, look up your trademark and tell you which option you qualify for, with the full price. No account needed. 2. **Pay when ready.** Create an account and pay once a year per domain. 3. **Setup.** Confirm your business details, approve your logo and copy a few settings into your domain. 4. **Certificate.** The certificate issuer (DigiCert) verifies everything and issues your certificate. 5. **Live.** Your logo appears in Gmail and other supporting inboxes. We monitor it and renew every year. ## Two options - **Logo + blue checkmark** with a [VMC](https://verifiedinbox.ai/docs/glossary.md#vmc). Needs a registered trademark. - **Logo only** with a [CMC](https://verifiedinbox.ai/docs/glossary.md#cmc). No trademark needed; your logo must have been public for at least 12 months. Not sure? The free check tells you. Read more in [VMC vs CMC](https://verifiedinbox.ai/docs/vmc-vs-cmc.md) and [Eligibility](https://verifiedinbox.ai/docs/eligibility.md). ## Need a human? Everything can be done self-service; humans are here if you want one: [contact support](https://verifiedinbox.ai/support?kind=contact_human). --- # How it works > The 11 steps from your domain to your logo in the inbox. The first five are free and need no account. The process is a wizard with 11 steps in four phases. We start with the quick, free steps so you know where you stand within minutes. Slow work (like the trademark search) starts in the background as soon as it can, so you rarely wait. You can go back to any completed step at any time. Every step has its own link, so you can bookmark it or share it with a colleague. ## Phase 1: Check (free, no account) | # | Step | What happens | |---|---|---| | 1 | **Your domain** | We confirm it's your own [domain](https://verifiedinbox.ai/docs/glossary.md#domain) (not a shared one like gmail.com) and that it's registered, and show a preview of your emails in Gmail. | | 2 | **Email security** | We check your email security settings ([SPF](https://verifiedinbox.ai/docs/spf.md), [DKIM](https://verifiedinbox.ai/docs/dkim.md), [DMARC](https://verifiedinbox.ai/docs/dmarc.md), [BIMI](https://verifiedinbox.ai/docs/bimi.md)) and explain clearly what's ready and what to fix. To continue, your domain needs DMARC in watch mode (or stronger) with a report address; you can finish the [safe rollout](https://verifiedinbox.ai/docs/glossary.md#dmarc-rollout) during setup. | | 3 | **Your logo** | Upload your logo, give us a link, or use the one we found on your website. We prepare the special file inboxes need and show it in a Gmail preview. | | 4 | **Trademark** | We search the US trademark office ([USPTO](https://verifiedinbox.ai/docs/glossary.md#uspto)) for your logo, or check a registration number you give us. | | 5 | **Eligibility** | You see which option you qualify for (logo + checkmark, logo only, or not yet), the evidence, the next steps and the estimated total price. | ## Phase 2: Setup (account and payment) | # | Step | What happens | |---|---|---| | 6 | **Checkout** | Sign in (your free-step progress moves into your account) and pay through Stripe's secure checkout. | | 7 | **Business** | Enter your company's legal details and who is applying. We verify them ([business verification](https://verifiedinbox.ai/docs/glossary.md#business-verification)). | | 8 | **Logo approval** | Compare your original logo with the inbox-ready version side by side, and approve it. | | 9 | **DNS** | All the settings to add to your domain, on one page, with copy buttons. We confirm them within seconds. By this step, DMARC must filter or block fake emails ([enforcement](https://verifiedinbox.ai/docs/glossary.md#dmarc-policy)). See [BIMI](https://verifiedinbox.ai/docs/bimi.md). | ## Phase 3: Certificate | # | Step | What happens | |---|---|---| | 10 | **Certificate** | We order your certificate from the [certificate issuer](https://verifiedinbox.ai/docs/glossary.md#certificate-issuer). They verify your business and logo; if they need anything from you (for example an email confirmation), we tell you. | ## Phase 4: Live | # | Step | What happens | |---|---|---| | 11 | **Go live** | Your logo and certificate are published. You send a test email and confirm you see your logo in Gmail. Monitoring and automatic renewals switch on. | ## How long it takes - **Free checks:** about a minute. The trademark search may take a few minutes in the background. - **Setup:** as fast as you can enter your details and add the DNS settings. If several tools send email for your domain, the safe DMARC rollout suggests about four weeks before blocking; you can start it in step 2 and skip ahead if you choose. - **Certificate:** the issuer's checks usually take from a few days to a few weeks. ## What "done" means We track four results separately, because they happen at different times: 1. **Eligibility assessed:** you know which option you qualify for. 2. **Certificate issued:** the issuer has issued your certificate. 3. **Published:** your settings and hosted files are live and correct. 4. **Seen in the inbox:** you've seen your logo in a real Gmail inbox. Gmail decides when to show logos and checkmarks, so display can't be guaranteed, but we make sure everything on your side is right. For a logo-only (CMC) plan, success is your logo showing; no checkmark is expected. ## Your progress is saved Your progress lives on our servers, not just in your browser: - Without an account, it's tied to your browser (and to a private guest token if you use the API or an AI assistant). - You can move to another device with a one-time **resume link**. - When you sign in, your progress moves into your account automatically. --- # VMC vs CMC > The two certificates that put your logo in the inbox, what each shows in Gmail, and what each requires. Inboxes only show your logo if it's backed by a mark certificate. There are two kinds. | | Logo + blue checkmark | Logo only | |---|---|---| | Certificate | [VMC](https://verifiedinbox.ai/docs/glossary.md#vmc) (Verified Mark Certificate) | [CMC](https://verifiedinbox.ai/docs/glossary.md#cmc) (Common Mark Certificate) | | Your logo in Gmail | ✓ | ✓ | | Gmail's blue checkmark | ✓ | — | | Registered trademark needed | Yes (USPTO in our first release) | No | | Logo in public use | — | At least 12 months | | Email security settings | Same for both | Same for both | | Our plan | $1,499 / domain / year | $899 / domain / year | | Issuer fee (separate) | est. ~$1,350 / year | est. ~$950 / year | ## Logo + blue checkmark (VMC) A VMC proves your logo is a [registered trademark](https://verifiedinbox.ai/docs/glossary.md#trademark) owned by your company. Gmail shows your logo and a blue checkmark next to your name; tapping it explains that the sender has verified they own the domain and the logo. You need: - Your own domain. - A registered trademark for the logo you want to show. We check the US trademark office ([USPTO](https://verifiedinbox.ai/docs/glossary.md#uspto)); see [Trademarks](https://verifiedinbox.ai/docs/trademarks.md). - Email security settings at "enforcement" (we guide you through a [safe rollout](https://verifiedinbox.ai/docs/glossary.md#dmarc-rollout)). ## Logo only (CMC) A CMC is for companies whose logo isn't a registered trademark yet. Gmail shows your logo, but **no checkmark**. You need: - Your own domain. - Proof your logo has been used publicly for at least 12 months ([prior use](https://verifiedinbox.ai/docs/glossary.md#prior-use)). We check public web archives of your website for you. - The same email security settings as a VMC. ## Which should I choose? - **You have a registered trademark:** VMC. The checkmark is the strongest trust signal. - **No trademark, but your logo has been public for a year:** CMC now. You can [file a trademark](https://verifiedinbox.ai/docs/trademarks.md#trademark-filing-service) and move to a VMC once it's registered. - **New logo, no trademark:** you're not eligible yet. See [Eligibility](https://verifiedinbox.ai/docs/eligibility.md) for what to do meanwhile. The free check makes this decision for you and explains why. --- # Eligibility > How we decide whether your domain qualifies for the logo with the checkmark, the logo only, or neither yet, and what to do next in each case. After the free checks, you get one of three results, with the evidence behind it and your next steps. You see this before you pay anything. ## The three results ### Eligible for logo + blue checkmark Your logo matches a registered US trademark owned by your company (or one you've told us about and we've verified). Next: review the estimate and continue to checkout. ### Eligible for logo only We didn't find a registered trademark, but your logo has been in public use for at least 12 months. Next: continue with a [CMC](https://verifiedinbox.ai/docs/glossary.md#cmc), and optionally file a trademark to upgrade later. ### Not currently eligible Neither route works yet, usually because the company or logo is new and there's no registered trademark. This is common for young companies, and it's temporary. We show you: - **What's missing**, requirement by requirement, with the evidence we found. For example: *"No matching USPTO registration found"* or *"Earliest web archive capture: March 2, 2026 (7 months ago)."* - **When you may qualify**, if we can tell (for example the date your logo reaches 12 months of public use). - **Next steps:** - **Get notified.** Leave your email and we'll tell you when you qualify. Creating an account is optional. - **File a trademark.** Our [trademark filing service](https://verifiedinbox.ai/docs/glossary.md#trademark-service) can help: email [trademark@verifiedinbox.ai](mailto:trademark@verifiedinbox.ai). - **Prepare meanwhile.** You can already fix your email security settings, so you're ready when the time comes. ## What we check | Requirement | Logo + checkmark (VMC) | Logo only (CMC) | |---|---|---| | Your own, registered domain | ✓ | ✓ | | Registered trademark matching your logo | ✓ | — | | Logo publicly used for 12+ months | — | ✓ | | A logo we can convert to the inbox format | ✓ | ✓ | | Email security at enforcement | needed before the certificate order | needed before the certificate order | Email security settings don't block eligibility. To get past the email security step, your domain only needs DMARC in watch mode with a report address (plus SPF and DKIM if it sends email); filtering or blocking fake emails can wait until the DNS step during setup. The certificate issuer makes the final decision during its own checks; if it rejects your application, you get a full refund. ## Evidence we use - **Trademarks:** US Patent and Trademark Office records, matched against your logo automatically, with a manual review when it's unclear. See [Trademarks](https://verifiedinbox.ai/docs/trademarks.md). - **Public use:** public web archive captures of your website showing your logo ([prior use](https://verifiedinbox.ai/docs/glossary.md#prior-use)). - **Domain:** registration and DNS lookups. ## The estimate With an eligible result, you see the estimated yearly total: our plan plus the certificate issuer's fee, as separate lines. See [Pricing](https://verifiedinbox.ai/pricing.md). --- # Ask AI > The built-in assistant that answers questions about your verification and our docs, and helps you move each step forward. Ask AI is the assistant built into VerifiedInbox. Ask it a question about your setup, the next step or anything in our docs. It can also do the work with you: it prepares each change, and nothing happens until you confirm. Ask AI is free with an account. It is separate from your own AI assistant (Claude, ChatGPT, Cursor), which connects through [MCP](https://verifiedinbox.ai/docs/glossary.md#mcp). See the [MCP server](https://verifiedinbox.ai/docs/mcp.md) guide for that. ## Open Ask AI Select the round blue **AI** button in the bottom right corner of any page, including these docs. - **On a computer,** Ask AI opens as a panel on the right. Drag its edge to make it wider or narrower. The page stays usable beside it. - **On a phone or tablet,** it opens as a sheet from the bottom. Drag it up or use the full screen button for more room. If you aren't signed in, Ask AI asks you to log in first. Your place on the page is kept, so you come back to where you were. ## What you can ask Ask AI uses the page you are on to understand what you need. | Where you open it | What it knows | |---|---| | A verification step | The domain, the current step, what is missing and what comes next | | Your dashboard | All of your domains and which ones need attention | | Docs and other pages | Everything in our documentation and glossary | Examples: - *What do I need to do on this step?* - *Which of my domains needs attention?* - *VMC or CMC: which one do I need?* - *What is wrong with my DMARC record?* Answers come from our documentation and from your live verification data, so they reflect where you really are. Ask AI can still make mistakes. Check important details before you act on them. ## What it can do for you Ask AI can check your domain, re-run checks after you update your settings and read the status of any step. When a step needs input, it can fill it in with you, for example submitting your logo, recording how your domain uses email or creating a payment link. Every change is shown as a card with the details. Select **Confirm** to go ahead or **Cancel** to drop it. Your verification page updates as soon as a change is made, exactly as if you had done it yourself. Ask AI follows the same rules as the website. It can't skip a step or move on until the current step is complete. ## What it won't do - **Pay for anything.** It can create a payment link; only you can pay. - **Change your DNS.** You add records yourself. We never ask for your DNS login. - **Renew early, reissue or revoke a certificate.** Do these from your dashboard. - **Send emails or contact support for you.** Use the [contact form](https://verifiedinbox.ai/support?kind=contact_human). - **Handle credentials.** It never creates or shows API keys or other secrets. - **Show anyone else's data.** Ask AI only sees the organization and environment (live or test) of the chat you are in. - **Talk about other topics.** It only answers questions about VerifiedInbox and email verification. ## Your chats - Select the menu button to see your earlier chats, and the pencil button to start a new one. - A chat about a domain stays with that domain. You can continue it at any time. - Chats are private to the person who started them. - Copy any question or answer with the copy button next to it. Messages are stored so you can come back to them. How we handle this data is described in our [Privacy policy](https://verifiedinbox.ai/privacy.md). ## Limits To keep Ask AI fast and fair for everyone, it limits how much it can be used. The limits cover: - how many chats you can start for each domain, - how many messages a chat can hold, - how long a single message can be (the counter under the message box shows how many characters are left), - how many messages you can send in a short time. Domains on a paid plan get higher limits than free ones. When you reach a limit, Ask AI tells you which one and, if you only need to wait, for how long. It then offers a button to email support. ### Need a higher limit? [Contact support](https://verifiedinbox.ai/support?kind=support) and tell us which limit you hit and what you are working on. We can raise limits for your organization. A person replies by email, usually within one business day. ## If Ask AI isn't available Sometimes Ask AI is paused for maintenance or heavy demand. The rest of VerifiedInbox keeps working, and you can always [talk to a person](https://verifiedinbox.ai/support?kind=contact_human). ## More help - [How it works](https://verifiedinbox.ai/docs/how-it-works.md): the full journey, step by step. - [Security](https://verifiedinbox.ai/docs/security.md): how we protect your data and limit what software and AI can do. - [Support](https://verifiedinbox.ai/docs/support.md): every way to reach us. --- # Receiving and sending emails > How domain email works, what MX records do, and how to connect common email providers to your domain. Your [domain](https://verifiedinbox.ai/docs/glossary.md#domain) is the name after the `@` in your email address, such as `yourcompany.com`. Behind the scenes, receiving email and sending email are handled separately in your domain's [DNS](https://verifiedinbox.ai/docs/glossary.md#dns) settings. Because the two are separate, you can read and reply to daily team mail in one service (such as Google Workspace or Microsoft 365) while sending product receipts, password resets, or newsletters through another service (such as Postmark or Resend). ## How receiving email works: MX records When someone sends an email to `alex@yourcompany.com`, their mail server looks up your domain's [MX records](https://verifiedinbox.ai/docs/glossary.md#mx-record) (Mail Exchanger records). An MX record tells the internet which [mailbox provider](https://verifiedinbox.ai/docs/glossary.md#mailbox-provider) accepts incoming mail for your domain. Each MX record has two parts: - **Priority (or preference):** a number that controls the order servers are tried. Lower numbers come first (for example, priority `1` or `10` is tried before `20`). - **Mail server (host):** the address of your provider's incoming mail server. If your domain has no MX records, it can still send email, but nobody can reply to addresses on that domain. ## Common email providers and example MX records Choose one mailbox provider to receive mail for a given domain (never mix MX records from two different providers on the same domain). Always copy the exact records shown in your provider's setup wizard. ### Google Workspace | Field | Value | |---|---| | **Example MX record** | `1 smtp.google.com` | | **Setup guide** | [Google Workspace MX setup](https://support.google.com/a/answer/174125) | ### Microsoft 365 | Field | Value | |---|---| | **Example MX record** | `0 .mail.protection.outlook.com` | | **Setup guide** | [Microsoft 365 DNS records](https://learn.microsoft.com/en-us/microsoft-365/admin/get-help-with-domains/create-dns-records-at-any-dns-hosting-provider) | ### Fastmail | Field | Value | |---|---| | **Primary MX** | `10 in1-smtp.messagingengine.com` | | **Secondary MX** | `20 in2-smtp.messagingengine.com` | | **Setup guide** | [Fastmail MX records](https://www.fastmail.help/hc/en-us/articles/1500000278002-Setting-up-your-domain-MX-records) | ### Zoho Mail | Field | Value | |---|---| | **Primary MX** | `10 mx.zoho.com` | | **Secondary MX** | `20 mx2.zoho.com` | | **Tertiary MX** | `50 mx3.zoho.com` | | **Setup guide** | [Zoho Mail MX configuration](https://www.zoho.com/mail/help/adminconsole/configure-email-delivery.html) | ### Proton Mail | Field | Value | |---|---| | **Primary MX** | `10 mail.protonmail.ch` | | **Secondary MX** | `20 mailsec.protonmail.ch` | | **Setup guide** | [Proton Mail custom domain setup](https://proton.me/support/custom-domain) | ## How sending email works Unlike incoming mail, outgoing mail does not use MX records. Any server on the internet can try to send an email that claims to come from `@yourcompany.com`. To stop impersonators and prove which emails are genuinely yours, inboxes check three security settings on your domain: 1. **[SPF](https://verifiedinbox.ai/docs/spf.md):** lists every service allowed to send email for your domain. 2. **[DKIM](https://verifiedinbox.ai/docs/dkim.md):** adds a cryptographic digital signature to each outgoing message. 3. **[DMARC](https://verifiedinbox.ai/docs/dmarc.md):** tells inboxes to quarantine or block messages that fail SPF and DKIM, and requires the authenticated domain to match the "From" address ([alignment](https://verifiedinbox.ai/docs/glossary.md#alignment)). Once those three protections are in place, **[BIMI](https://verifiedinbox.ai/docs/bimi.md)** lets Gmail and other supporting inboxes display your official logo and blue verified checkmark next to your messages. --- # SPF: who can send emails for you > How SPF works, how to configure your SPF record using your email provider's documentation, full mechanism syntax, and how to fix common SPF problems. [SPF](https://verifiedinbox.ai/docs/glossary.md#spf) (Sender Policy Framework, defined in [RFC 7208](https://datatracker.ietf.org/doc/html/rfc7208)) is a public guest list of the servers and services allowed to send email on behalf of your [domain](https://verifiedinbox.ai/docs/glossary.md#domain). Standard email (SMTP) does not verify the sender address on its own. Without SPF and [DKIM](https://verifiedinbox.ai/docs/dkim.md), an attacker can send messages that appear to come from your domain. Publishing an SPF record protects your domain from impersonation, improves deliverability, and is one of the building blocks required for [DMARC](https://verifiedinbox.ai/docs/dmarc.md) and your [BIMI](https://verifiedinbox.ai/docs/bimi.md) inbox logo. We strongly recommend configuring an SPF record for every domain you own, and always using your email provider's official documentation to get the exact value they require. ## How SPF works When a mail server receives a message, it checks the domain in the message's **Return-Path** (also called the `MAIL FROM` or bounce address) rather than the visible `From:` header shown in the inbox: 1. **You publish an SPF record:** Add a single [TXT record](https://verifiedinbox.ai/docs/glossary.md#txt-record) starting with `v=spf1` on your domain (and on any [subdomain](https://verifiedinbox.ai/docs/glossary.md#subdomain) that sends mail). 2. **An email is sent:** A server with IP address `192.0.2.1` connects to the recipient's mail server and specifies a `Return-Path` domain (or `HELO`/`EHLO` domain). 3. **The receiving server checks your list:** The receiver looks up the `v=spf1` TXT record for that `Return-Path` domain and checks whether the sending server's IP address matches your authorized mechanisms. 4. **The result is passed to DMARC:** If the IP matches, SPF returns `Pass`. If it does not match, SPF returns `SoftFail` (`~all`) or `Fail` (`-all`), and [DMARC](https://verifiedinbox.ai/docs/dmarc.md) decides whether to quarantine or reject the message. Because SPF checks the `Return-Path` domain while readers see the `From:` header, DMARC also checks [alignment](https://verifiedinbox.ai/docs/glossary.md#alignment): the `Return-Path` domain must match your `From:` domain (or be a subdomain of it) for SPF alone to satisfy DMARC. ## How to configure an SPF record An SPF record must always be published as a DNS [TXT record](https://verifiedinbox.ai/docs/glossary.md#txt-record) (the older dedicated `SPF` DNS record type was retired by RFC 7208). At the root of your domain (`@`), a typical record looks like this: ```text v=spf1 include:_spf.google.com ~all ``` - `v=spf1` identifies the TXT record as an SPF version 1 policy. Every SPF record must begin with `v=spf1`. - `include:_spf.google.com` authorizes Google Workspace's sending servers. If you use more than one service (for example Google Workspace for team mail and Postmark for product emails), combine them into the **same** TXT record: ```text v=spf1 include:_spf.google.com include:spf.mtasv.net ~all ``` - `~all` (`SoftFail`) or `-all` (`Fail`) at the very end tells receiving servers that no other IP addresses are authorized to send mail for the domain. ### Domains and subdomains that never send email If you own parked domains, or subdomains like `www` that never send email, publish a strict no-mail SPF record on them so attackers cannot spoof them: ```text v=spf1 -all ``` ## Reputable, developer-friendly email providers Always check the official documentation of each service that sends email for you, copy their recommended `include:` mechanism or custom return-path setup, and add it to your domain's DNS settings. ### Postmark | Field | Value | |---|---| | **Typical SPF setup** | Custom `Return-Path` CNAME (`pm-bounces`) or `include:spf.mtasv.net` | | **Official guide** | [Postmark SPF & Return-Path guide](https://postmarkapp.com/support/article/1090-how-do-i-set-up-spf-on-my-domain) | ### Resend | Field | Value | |---|---| | **Typical SPF setup** | SPF TXT record on a dedicated sending subdomain (`send`) | | **Official guide** | [Resend domain verification docs](https://resend.com/docs/dashboard/domains/introduction) | ### Amazon SES | Field | Value | |---|---| | **Typical SPF setup** | Custom `MAIL FROM` subdomain with `v=spf1 include:amazonses.com ~all` | | **Official guide** | [Amazon SES SPF authentication](https://docs.aws.amazon.com/ses/latest/dg/send-email-authentication-spf.html) | ### SendGrid | Field | Value | |---|---| | **Typical SPF setup** | Automated security CNAMEs (or `include:sendgrid.net`) | | **Official guide** | [SendGrid domain authentication](https://www.twilio.com/docs/sendgrid/ui/account-and-settings/how-to-set-up-domain-authentication) | ### Mailgun | Field | Value | |---|---| | **Typical SPF setup** | `include:mailgun.org` on your sending domain or subdomain | | **Official guide** | [Mailgun domain verification](https://documentation.mailgun.com/docs/mailgun/user-manual/get-started/verify-domain) | ### Google Workspace | Field | Value | |---|---| | **Typical SPF setup** | `include:_spf.google.com` | | **Official guide** | [Google Workspace SPF setup](https://support.google.com/a/answer/33786) | ### Microsoft 365 | Field | Value | |---|---| | **Typical SPF setup** | `include:spf.protection.outlook.com` | | **Official guide** | [Microsoft 365 SPF setup](https://learn.microsoft.com/en-us/defender-office-365/email-authentication-spf-configure) | ## Technical reference: SPF syntax, mechanisms & results Receiving servers evaluate an SPF record from left to right. The first mechanism that matches the connecting server's IP address determines the result. If no mechanism matches and no `redirect=` modifier is present, the default result is `Neutral` (as if `?all` were at the end). ### Qualifiers Every mechanism can be prefixed with one of four qualifiers. When omitted, `+` (`Pass`) is the default. | Qualifier | SPF result | Meaning | |---|---|---| | `+` *(default)* | `Pass` | The host is authorized to send mail. | | `-` | `Fail` | The host is **not** authorized (for example `-all`). Receivers may reject the message. | | `~` | `SoftFail` | The host is not authorized, but the domain is in transition (`~all`). Receivers accept and mark as suspicious, or defer to DMARC. | | `?` | `Neutral` | Makes no assertion about whether the host is authorized (`?all`). Treated the same as `None`. | ### Mechanisms | Mechanism | Uses DNS lookup? | What it matches | |---|---|---| | `all` | No | Matches all IP addresses. Placed at the very end (`~all` or `-all`) as the default rule; any mechanism after `all` is ignored. | | `include:` | Yes | Evaluates the SPF record of ``. If that evaluation returns `Pass`, the `include:` matches. A `-all` or `~all` inside the included record does not fail your outer record; evaluation simply moves to the next term. If `` has no SPF record, it triggers a `PermError`. | | `ip4:` | No | Matches a single IPv4 address (`ip4:192.0.2.1`, default `/32`) or CIDR block (`ip4:192.0.2.0/24`). | | `ip6:` | No | Matches a single IPv6 address (default `/128`) or IPv6 CIDR block (`ip6:2001:db8::/32`). | | `a` or `a:` | Yes | Matches the `A` (IPv4) or `AAAA` (IPv6) records of the current domain or `` (optional `/cidr`). | | `mx` or `mx:` | Yes | Matches the IP addresses of the `MX` servers for the current domain or `` (capped at 10 address lookups per `mx` term). | | `exists:` | Yes | Performs an `A` record lookup on `` (often combined with macros); matches if any `A` record exists. | | `ptr` *(deprecated)* | Yes | Reverse DNS lookup. **Do not use:** RFC 7208 deprecates `ptr` because it is slow, unreliable, and adds heavy DNS load. | ### Modifiers Modifiers are optional `name=value` pairs that may appear at most once per record. | Modifier | Uses DNS lookup? | What it does | |---|---|---| | `redirect=` | Yes | Replaces the record with ``'s SPF policy when no earlier mechanism matches. Useful when one organization shares a single SPF policy across several domains. Ignored if `all` is present in the record. | | `exp=` | No | Specifies a domain whose TXT record provides a custom SMTP rejection string when SPF returns `Fail`. Rarely used. | ### SPF evaluation results | Result | Meaning | Typical action | |---|---|---| | `Pass` | The sending IP is authorized by the SPF record. | Accept (and pass SPF for DMARC if aligned). | | `Fail` | The sending IP is explicitly unauthorized (`-all`). | Reject (`550 5.7.1`) or apply DMARC policy. | | `SoftFail` | The sending IP is probably unauthorized (`~all`). | Accept and mark as spam, or apply DMARC policy. | | `Neutral` | The record makes no assertion (`?all` or no `all` term). | Treat as unauthenticated. | | `None` | No SPF record exists on the domain, or the domain is invalid. | Treat as unauthenticated. | | `TempError` | Transient DNS error (timeout or `SERVFAIL`). | Temporarily defer (`451`) and retry later. | | `PermError` | Permanent error in the published SPF record (syntax error, multiple SPF records, >10 lookups, or >2 void lookups). | Fails SPF authentication until you fix the DNS record. | ## Common issues - **Multiple SPF records on the same domain (`PermError`).** A domain or subdomain may only have **one** `v=spf1` TXT record. Publishing two separate `v=spf1` records causes SPF to fail immediately with `PermError`. Merge all `include:` and `ip4:`/`ip6:` entries into a single record. Note that long TXT records (>255 characters) split into multiple quoted strings inside a *single* DNS TXT record are concatenated automatically without spaces; make sure there is a space between terms at string boundaries. - **Too many DNS lookups: the 10-lookup limit (`PermError`).** RFC 7208 allows at most **10** DNS-querying mechanisms and modifiers (`include`, `a`, `mx`, `ptr`, `exists`, and `redirect`) across the entire evaluation, including any nested lookups inside `include:` records. (`all`, `ip4`, and `ip6` do not count toward this limit.) To stay under 10 lookups: - Remove `include:` statements for old tools and services you no longer use. - **Use separate subdomains for different mail streams.** Each [subdomain](https://verifiedinbox.ai/docs/glossary.md#subdomain) (such as `mail.yourcompany.com` for marketing or `send.yourcompany.com` for transactional receipts) gets its own independent 10-lookup budget. - Prefer `ip4:` and `ip6:` over `a` or `mx` for static servers you control. - **Too many void lookups: the 2-void-lookup limit (`PermError`).** RFC 7208 recommends failing with `PermError` if more than **2** DNS lookups return either an empty answer (`0` records) or `NXDOMAIN` (non-existent domain). This usually happens when an `include:` or `a:` points to a decommissioned service or a typoed domain name. - **Using `+all` or omitting `all`.** Ending an SPF record with `+all` authorizes every server on the internet to impersonate your domain. Omitting `all` (without `redirect=`) leaves the default as `?all` (`Neutral`). Always end your record with `~all` or `-all`. - **Unaligned `Return-Path` and email forwarding.** Many third-party email tools use their own domain in the `Return-Path` unless you configure a custom return-path (or custom `MAIL FROM`) subdomain. In addition, when a recipient auto-forwards an email to another inbox, the forwarder's IP address often fails the original sender's SPF check. Configuring [DKIM](https://verifiedinbox.ai/docs/dkim.md) alongside SPF ensures your emails still pass [DMARC](https://verifiedinbox.ai/docs/dmarc.md) even when forwarded. --- # DKIM: digital signatures > How DKIM signs your outgoing emails, how DKIM records and headers work, how to set up keys with your email provider, and how to fix common issues and edge cases. [DKIM](https://verifiedinbox.ai/docs/glossary.md#dkim) (DomainKeys Identified Mail, defined in [RFC 6376](https://datatracker.ietf.org/doc/html/rfc6376)) attaches a cryptographic digital signature to every email you send. Think of it like a tamper-proof seal: receiving mail servers use it to verify that the email genuinely came from your [domain](https://verifiedinbox.ai/docs/glossary.md#domain) and that neither the headers nor the message body were altered in transit. Together with [SPF](https://verifiedinbox.ai/docs/spf.md) and [DMARC](https://verifiedinbox.ai/docs/dmarc.md), DKIM protects your domain from spoofing, builds long-term domain reputation with inbox providers, and is required before inboxes will display your [BIMI](https://verifiedinbox.ai/docs/bimi.md) logo and verified checkmark. We recommend enabling DKIM for every service that sends email from your domain, following each provider's official documentation. ## Why DKIM matters (and what it does not do) - **Confirms authenticity and integrity:** Proves that an authorized mail server signed the message and that the signed headers (`From`, `To`, `Subject`) and body were not modified on the way. - **Survives email forwarding:** Unlike [SPF](https://verifiedinbox.ai/docs/spf.md) (which checks the sending server's IP address and breaks when a recipient auto-forwards an email), a DKIM signature travels inside the message headers and remains valid across forwards as long as the message content is not changed. - **Builds domain reputation:** Inbox providers such as Gmail, Yahoo, and Apple Mail track sending reputation against the domain in your DKIM signature (`d=`). Gmail and Yahoo require both SPF and DKIM for bulk senders (5,000+ messages per day). - **Does not encrypt the email body:** DKIM verifies *who* sent the message and that it was not tampered with, but it does not encrypt the message content itself (transport encryption is handled separately by TLS). ## How DKIM works DKIM has two parts: a **public key** published in your domain's [DNS](https://verifiedinbox.ai/docs/glossary.md#dns) and a **private key** kept on your email provider's outgoing mail server. 1. **Key generation:** Your email provider generates a public/private key pair for your domain. 2. **Public key in DNS:** You publish the public key at `._domainkey.` as a DNS [TXT record](https://verifiedinbox.ai/docs/glossary.md#txt-record) (or as a `CNAME` record pointing to a TXT record hosted by your provider). 3. **Signing outgoing mail:** When you send an email, your provider hashes the message body and selected headers, signs them with the private key, and attaches a `DKIM-Signature` header to the message. 4. **Verification at the inbox:** The receiving server reads the domain (`d=`) and [DKIM selector](https://verifiedinbox.ai/docs/glossary.md#dkim-selector) (`s=`) from the `DKIM-Signature` header, looks up `._domainkey.` in DNS, and uses the public key to verify the signature. ### Multiple DKIM records on one domain Unlike SPF and DMARC (where a domain may only have a single record), **your domain can have as many DKIM records as you need**. Each email service you use (such as Google Workspace, Postmark, or SendGrid) publishes its key under its own unique selector name: ```text google._domainkey.yourcompany.com 20261019120000pm._domainkey.yourcompany.com s1._domainkey.yourcompany.com ``` Because DNS does not allow listing all selectors automatically, you can find any sender's active selector by sending a test email, opening **More → Show original** in Gmail, and checking the `s=` value inside the `DKIM-Signature` header. In the VerifiedInbox **Email security** step, you can also send a free test email to the address we give you: we read its signature and check that key for you. ## How to configure and verify DKIM 1. **Generate a DKIM key in your email provider's dashboard.** Choose a **2048-bit** key whenever your DNS provider supports it (2048-bit keys are much stronger than 1024-bit keys; keys shorter than 1024 bits are insecure and rejected by modern inboxes). 2. **Add the DNS record at your domain host.** Copy the **Host / Name** (which always contains `._domainkey`, such as `google._domainkey`) and the **Value** (either a `v=DKIM1; k=rsa; p=...` TXT value or a `CNAME` target) into your domain's DNS settings. 3. **Turn on DKIM signing in your provider.** After saving the DNS record, click **Start authentication** or **Verify** in your email provider's admin console so it begins signing outgoing messages. 4. **Verify with a real external test email.** Send an email to an external Gmail address (or to the free test address in the VerifiedInbox **Email security** or **Go live** step) rather than to your own address. In Gmail, open **Show original** and confirm `Authentication-Results` shows `dkim=pass` with `header.d=yourcompany.com`. ## Reputable, developer-friendly email providers Follow the official guide for each service that sends email for you: ### Postmark | Field | Value | |---|---| | **Selector & record type** | Timestamped TXT selector (for example `20261019120000pm._domainkey`) | | **Official guide** | [Postmark DKIM setup](https://postmarkapp.com/support/article/1091-how-do-i-set-up-dkim-on-my-domain) | ### Resend | Field | Value | |---|---| | **Selector & record type** | `resend._domainkey` (TXT record) | | **Official guide** | [Resend domain verification docs](https://resend.com/docs/dashboard/domains/introduction) | ### Amazon SES | Field | Value | |---|---| | **Selector & record type** | Three Easy DKIM CNAME records (`._domainkey`) | | **Official guide** | [Amazon SES Easy DKIM guide](https://docs.aws.amazon.com/ses/latest/dg/send-email-authentication-dkim-easy.html) | ### SendGrid | Field | Value | |---|---| | **Selector & record type** | `s1._domainkey` and `s2._domainkey` (CNAME records) | | **Official guide** | [SendGrid domain authentication](https://www.twilio.com/docs/sendgrid/ui/account-and-settings/how-to-set-up-domain-authentication) | ### Mailgun | Field | Value | |---|---| | **Selector & record type** | `smtp._domainkey`, `krs._domainkey`, or `mx._domainkey` (TXT or CNAME) | | **Official guide** | [Mailgun DKIM setup](https://documentation.mailgun.com/docs/mailgun/user-manual/get-started/verify-domain) | ### Google Workspace | Field | Value | |---|---| | **Selector & record type** | `google._domainkey` (TXT record, 2048-bit recommended) | | **Official guide** | [Set up DKIM for Google Workspace](https://knowledge.workspace.google.com/admin/security/set-up-dkim) | ### Microsoft 365 | Field | Value | |---|---| | **Selector & record type** | `selector1._domainkey` and `selector2._domainkey` (CNAME records) | | **Official guide** | [Use DKIM with Microsoft 365](https://learn.microsoft.com/en-us/defender-office-365/email-authentication-dkim-configure) | ## Technical reference: DKIM record & header tags ### DKIM DNS TXT record tags A DKIM DNS record at `._domainkey.` is a semicolon-separated list of `tag=value` pairs: ```text v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA... ``` | Tag | Required? | Meaning | |---|---|---| | `v=DKIM1` | Recommended first | Identifies the TXT record as a DKIM key record. | | `k=rsa` | Optional (default `rsa`) | Key algorithm: `rsa` (most common) or `ed25519`. | | `p=` | Required | The base64-encoded public key. **Note:** An empty `p=` (`v=DKIM1; p=`) explicitly **revokes** that key. | | `t=y` / `t=s` | Optional | Flags: `t=y` indicates the domain is testing DKIM; `t=s` requires strict subdomain matching. Avoid `t=y` in production. | ### `DKIM-Signature` email header tags Every signed email carries a `DKIM-Signature` header similar to this: ```text DKIM-Signature: v=1; a=rsa-sha256; d=yourcompany.com; s=google; h=from:to:subject:date:message-id; bh=uMixy0BsCqhbru4fqPZQdeZY5Pq865sNAnOAxNgUS0s=; b=LiIvJeRyqMo0gngiCygwpiKphJjYezb5kXBKCNj8DqRV... ``` | Tag | Example | What it means | |---|---|---| | `v=` | `v=1` | DKIM signature version (always `1`). | | `d=` | `d=yourcompany.com` | The signing domain. To pass [DMARC](https://verifiedinbox.ai/docs/dmarc.md) via DKIM, `d=` must [align](https://verifiedinbox.ai/docs/glossary.md#alignment) with the visible `From:` domain. | | `s=` | `s=google` | The [DKIM selector](https://verifiedinbox.ai/docs/glossary.md#dkim-selector) used to query `._domainkey.` in DNS. | | `a=` | `a=rsa-sha256` | Algorithm used to hash the message and generate the signature (`rsa-sha256` or `ed25519-sha256`). | | `h=` | `h=from:to:subject` | Colon-separated list of email headers covered by the signature. | | `bh=` | `bh=uMixy0Bs...=` | Base64 hash of the canonicalized email body. Computed separately so receivers can verify headers before reading a large body. | | `b=` | `b=LiIvJeRy...=` | The digital signature computed over the headers in `h=` and the `bh=` body hash using the private key. | ## Common issues & edge cases - **Record added in DNS, but signing not turned on in the provider.** Many providers (including Google Workspace and Microsoft 365) do not start signing outgoing mail until you click **Start authentication** or enable DKIM in their admin console *after* the DNS record is published. - **Provider signing with its own default domain (`d=`) instead of yours.** Before you verify a custom domain in a third-party sender (or before you click **Start authentication** in Google Workspace), the provider may sign messages with a shared domain such as `d=*.gappssmtp.com`, `d=*.onmicrosoft.com`, or `d=sendgrid.info`. Even though `dkim=pass` appears in the raw header, DMARC [alignment](https://verifiedinbox.ai/docs/glossary.md#alignment) fails because `d=` does not match your `From:` domain. - **2048-bit key length limits (255-character DNS TXT strings).** A 2048-bit RSA public key is roughly 400 characters long, which exceeds the 255-character limit of a single DNS TXT string. Most modern DNS hosts automatically split long TXT values into multiple quoted strings (which DNS resolvers concatenate without spaces). If your DNS host rejects values over 255 characters, split the string according to your host's instructions or generate a 1024-bit key. Never paste stray spaces, line breaks, or quotation marks inside the `p=` key. - **Outbound gateways or security appliances modifying messages after signing.** If an outbound gateway, archiving proxy, or mailing list appends a legal disclaimer footer, rewrites tracking links, or modifies the `Subject:` header *after* the message was signed, the body hash (`bh=`) or signature (`b=`) breaks and DKIM fails. Make sure DKIM signing happens at the final outbound hop, or configure gateways not to alter signed messages. - **Google Workspace 24–72 hour new-account delay and stale warning banner.** After turning on Gmail for a new Google Workspace organization, you may need to wait 24 to 72 hours before the Admin console lets you generate a DKIM key. In addition, the Google Admin console can continue showing *"You must update the DNS records for this domain"* for up to 48 hours after your record is live. - **Selector prefix conflict (`google._domainkey` already in use).** If another service already uses the `google` selector on your domain, choose a custom prefix selector (such as `google2`) when generating your key in Google Workspace. - **Cloudflare proxy ("orange cloud") enabled on DKIM CNAME records.** When a provider gives you `CNAME` records for DKIM (such as Amazon SES, SendGrid, or Microsoft 365), set them to **DNS only** (grey cloud) in Cloudflare so mail servers can resolve the target TXT record. - **Duplicated domain name in the Host field.** Some DNS providers automatically append `.yourcompany.com` to the Host field. Entering `google._domainkey.yourcompany.com` creates `google._domainkey.yourcompany.com.yourcompany.com`. VerifiedInbox queries your [authoritative nameservers](https://verifiedinbox.ai/docs/glossary.md#authoritative-dns) directly and warns you if this happens. - **Testing by emailing yourself in the same mailbox.** Sending a message from your address to the exact same address often bypasses external gateway signing or verification. Always test by sending to an external Gmail address or to the test address in the VerifiedInbox **Email security** or **Go live** step. --- # DMARC: fake email protection > How DMARC protects your domain from spoofing, how alignment and DMARC quarantine work, what new domains should do, full record syntax, and common edge cases. [DMARC](https://verifiedinbox.ai/docs/glossary.md#dmarc) (Domain-based Message Authentication, Reporting, and Conformance, defined in [RFC 7489](https://datatracker.ietf.org/doc/html/rfc7489)) is the public setting on your [domain](https://verifiedinbox.ai/docs/glossary.md#domain) that tells inboxes what to do when an email claims to come from you, and where to send daily reports about who is using your domain. Without DMARC, each receiving mail provider has to guess how to handle messages that fail [SPF](https://verifiedinbox.ai/docs/spf.md) or [DKIM](https://verifiedinbox.ai/docs/dkim.md). DMARC puts you in control: instead of trying to filter out bad mail after the fact, you give every inbox a clear rule to **filter in legitimate mail** from your domain and quarantine or block fakes. Inboxes will only display your [BIMI](https://verifiedinbox.ai/docs/bimi.md) logo and blue verified checkmark when your DMARC policy is set to **enforcement**: either `p=quarantine` (at `100%`) or `p=reject`. ## Why DMARC matters - **Stops spoofing and phishing:** Protects your customers and team from attackers forging `@yourcompany.com` in the `From:` line (such as fake invoices or password-reset scams). - **Full visibility (including "Shadow IT"):** Receiving servers (Gmail, Yahoo, Outlook, Apple Mail) email you daily XML summaries showing every IP address and service sending mail as your domain, and whether each one passed SPF and DKIM. - **Better deliverability and compliance:** Gmail and Yahoo require DMARC for bulk senders (5,000+ messages per day), and an enforced DMARC policy is required for [VMC](https://verifiedinbox.ai/docs/glossary.md#vmc) and [CMC](https://verifiedinbox.ai/docs/glossary.md#cmc) mark certificates. ## How DMARC and alignment work DMARC builds on top of [SPF](https://verifiedinbox.ai/docs/spf.md) and [DKIM](https://verifiedinbox.ai/docs/dkim.md). You must set up SPF and/or DKIM before enforcing DMARC. ### The "at least one aligned pass" rule To pass DMARC, an email must pass **at least one** of these two checks: 1. **SPF authentication + SPF [alignment](https://verifiedinbox.ai/docs/glossary.md#alignment):** The sending server passes SPF, **and** the envelope `Return-Path` (`MAIL FROM`) domain matches the visible `From:` header domain. 2. **DKIM authentication + DKIM [alignment](https://verifiedinbox.ai/docs/glossary.md#alignment):** The message has a valid `DKIM-Signature`, **and** the signing domain (`d=`) matches the visible `From:` header domain. A message only fails DMARC if **both** aligned SPF and aligned DKIM fail. This is why setting up both SPF and DKIM is best practice: when a recipient auto-forwards your email to another inbox, SPF usually fails (because the forwarder's IP is not in your SPF record), but your DKIM signature survives and the message still passes DMARC. ### Relaxed vs. strict alignment (`aspf` and `adkim`) DMARC supports two alignment modes for comparing the `From:` domain (`yourcompany.com`) against the SPF `Return-Path` domain or DKIM `d=` domain: | Check | Relaxed alignment (`r`, default) | Strict alignment (`s`) | |---|---|---| | **SPF (`aspf`)** | The `Return-Path` domain can be the exact `From:` domain **or any subdomain** of your [organizational domain](https://verifiedinbox.ai/docs/glossary.md#organizational-domain) (for example `pm-bounces.yourcompany.com` aligns with `yourcompany.com`). | The `Return-Path` domain must match the `From:` domain **character for character**. | | **DKIM (`adkim`)** | The DKIM `d=` domain can be the exact `From:` domain **or any subdomain** of your organizational domain. | The DKIM `d=` domain must match the `From:` domain **character for character**. | **Keep relaxed alignment (`r`, the default) unless you have a specific reason not to.** Many email providers (Postmark, Amazon SES, Resend, SendGrid) use a custom subdomain for the `Return-Path` bounce address; setting `aspf=s` will cause those messages to fail SPF alignment. Only use strict alignment (`s`) if untrusted third parties operate subdomains under your main domain. ## Before you begin 1. **Turn on SPF and DKIM first.** If you enforce DMARC before SPF or DKIM is authenticating your mail, your own messages will be sent to spam or rejected. On an active domain, Google Workspace recommends letting SPF and DKIM run for at least 48 hours before tightening DMARC. 2. **Create a dedicated mailbox or group for DMARC reports (`rua=`).** Active domains receive multiple XML report emails every day (large domains can receive hundreds). Use a dedicated address such as `dmarc@yourcompany.com` (or a DMARC reporting service) rather than your personal inbox. The mailbox must exist and your domain must be able to receive email, or the reports are lost. In the VerifiedInbox **Email security** step, every suggested record uses the address in your current record, or `dmarc@` your main domain; choose **Use a different address** to change it in all of them at once. ### How VerifiedInbox uses DMARC - The **Email security** step asks who sends email from your domain. **Not yet** and **Only from our mailbox** get the blocking record right away; **From several tools** gets the four-stage rollout below. - To continue past that step, DMARC must be published in watch mode (`p=none`) or stronger, with a report address. You don't have to wait for the whole rollout. - By the **DNS** step, DMARC must be at enforcement (`p=quarantine` at `100%` or `p=reject`). We check again right before ordering your certificate. - Suggested records keep the extra tags of your current record (such as `ruf`, strict `adkim` or `aspf`). We remove `sp=none` and say so, because BIMI requires your subdomains to be protected too. ## New domains without DMARC If your domain is brand new, sends no email yet, or only uses a single mailbox provider with no third-party mailing tools, **you do not need a multi-week rollout**: 1. Configure [SPF](https://verifiedinbox.ai/docs/spf.md) and [DKIM](https://verifiedinbox.ai/docs/dkim.md) for your mailbox provider. 2. Publish a strict DMARC enforcement record right away at `_dmarc`: ```text v=DMARC1; p=reject; rua=mailto:dmarc@yourcompany.com ``` Because no unauthenticated services are sending mail from your domain, going straight to `p=reject` protects your brand immediately and satisfies the DMARC requirement for your [BIMI](https://verifiedinbox.ai/docs/bimi.md) inbox logo on day one. If your domain **already sends business email every day** across multiple tools (newsletters, CRM, billing, helpdesk) and currently has no DMARC record, follow the staged rollout below so you do not accidentally filter your own messages. ## How DMARC quarantine works, how to configure it, and how to troubleshoot ### What quarantine does Your DMARC policy (`p=`) has three possible settings: - `p=none` **(Monitor only):** Takes no action on failing mail; delivers messages normally and sends daily XML reports to your `rua=` address so you can see who is sending as your domain. - `p=quarantine` **(Send fakes to spam):** Instructs Gmail, Outlook, Yahoo, and other inboxes to move any email that fails DMARC into the recipient's **spam or junk folder** instead of the primary inbox. - Unlike `p=none`, `p=quarantine` actively protects recipients from fake emails. - Unlike `p=reject`, a legitimate message sent from a misconfigured tool is still recoverable from the recipient's spam folder while you fix that sender's SPF or DKIM settings. - Once `p=quarantine` applies to 100% of messages (when `pct` is omitted or set to `pct=100`), **your domain meets the DMARC enforcement requirement for [BIMI](https://verifiedinbox.ai/docs/bimi.md) and your [VMC](https://verifiedinbox.ai/docs/glossary.md#vmc) or [CMC](https://verifiedinbox.ai/docs/glossary.md#cmc) certificate.** - `p=reject` **(Block fakes outright):** Instructs receiving servers to drop failing messages at the SMTP door before they ever reach the inbox or spam folder. This is the strongest protection for your brand. ### Configuring a safe rollout to quarantine and reject For active domains, tighten DMARC in four stages ([safe DMARC rollout](https://verifiedinbox.ai/docs/glossary.md#dmarc-rollout)): #### Stage 1: Watch | Field | Value | |---|---| | **Host (name)** | `_dmarc` | | **Record value** | `v=DMARC1; p=none; rua=mailto:dmarc@yourcompany.com` | | **Suggested time** | 14 days | | **What happens** | No mail is filtered. Daily reports reveal every service sending as your domain. | #### Stage 2: Quarantine 25% | Field | Value | |---|---| | **Host (name)** | `_dmarc` | | **Record value** | `v=DMARC1; p=quarantine; pct=25; rua=mailto:dmarc@yourcompany.com` | | **Suggested time** | ~7 days | | **What happens** | One quarter of unauthenticated messages go to spam. Note: `pct` below `100` does not yet qualify for BIMI. | #### Stage 3: Quarantine 100% | Field | Value | |---|---| | **Host (name)** | `_dmarc` | | **Record value** | `v=DMARC1; p=quarantine; rua=mailto:dmarc@yourcompany.com` | | **Suggested time** | ~7 days | | **What happens** | All failing messages go to spam. **Qualifies for BIMI and your inbox logo.** | #### Stage 4: Reject all (recommended) | Field | Value | |---|---| | **Host (name)** | `_dmarc` | | **Record value** | `v=DMARC1; p=reject; rua=mailto:dmarc@yourcompany.com` | | **Suggested time** | Ongoing (keep `rua=` active) | | **What happens** | Failing messages are blocked at the server door. High-volume senders can optionally step through `p=reject; pct=25` and `pct=50` before `pct=100`. | ### Reading DMARC reports and troubleshooting quarantine issues As daily `rua=` reports arrive (or when you test a message during quarantine), every sender falls into one of three buckets: 1. **Known, aligned sources:** Services you use where DKIM and/or SPF pass and align with your domain. No action needed. 2. **Unconfigured legitimate sources ("Shadow IT"):** A tool your team uses (such as a helpdesk, billing system, or marketing platform) that is sending as `@yourcompany.com` without aligned DKIM or SPF. - **Fix DKIM first:** Enable custom domain DKIM signing in that tool's dashboard so its `DKIM-Signature` uses `d=yourcompany.com` (rather than a shared domain like `d=sendgrid.info`). - **Fix SPF alignment:** Add the provider's `include:` to your [SPF record](https://verifiedinbox.ai/docs/spf.md) and configure their custom `Return-Path` subdomain so the bounce domain aligns with `yourcompany.com`. 3. **Forwarded mail and spoofers:** - If DKIM passes and aligns while SPF fails, the email was almost certainly **auto-forwarded** by a recipient; DMARC still passes. - Unknown IPs where both SPF and DKIM fail in small numbers (or obvious spam campaigns) are the exact spoofers DMARC is designed to quarantine and block. If legitimate emails land in spam after you enable `p=quarantine`, open a message in Gmail via **More → Show original** (or send a test email from the VerifiedInbox **Email security** or **Go live** step) to inspect the `Authentication-Results` header. If you need time from another department to add a DKIM key, you can temporarily lower `pct=25` or step back to `p=none`, then return to `p=quarantine` or `p=reject` once fixed. Keep monitoring your `rua=` reports even after reaching `p=reject`: if someone later adopts a new email tool without configuring DKIM, `p=reject` will block that tool's emails until you authorize it. ## Technical reference: DMARC syntax and example records A DMARC record is published as a single [TXT record](https://verifiedinbox.ai/docs/glossary.md#txt-record) at `_dmarc.`. Tags are separated by semicolons (`;`). **`v=DMARC1` must be the first tag, and `p=` must be the second tag**; all other tags may appear in any order. ### Tags | Tag | Allowed values | Meaning | |---|---|---| | `v` *(required, first)* | `DMARC1` | Protocol version. Must always be `v=DMARC1`. | | `p` *(required, second)* | `none`, `quarantine`, `reject` | Policy for the domain itself. BIMI requires `quarantine` or `reject`. | | `sp` *(optional)* | `none`, `quarantine`, `reject` | Policy for [subdomains](https://verifiedinbox.ai/docs/glossary.md#subdomain). Inherits `p` if omitted. **For BIMI, never set `sp=none`.** | | `pct` *(optional)* | `1` to `100` (default `100`) | Percentage of failing messages the policy applies to. **BIMI requires `100` (or omitting `pct`).** | | `rua` *(recommended)* | `mailto:address@domain` | Where daily aggregate XML reports are sent. Separate multiple addresses with commas, each prefixed with `mailto:`. | | `ruf` *(optional)* | `mailto:address@domain` | Where message-level failure (forensic) reports are sent. Not supported by Gmail or most major providers for privacy reasons. | | `adkim` *(optional)* | `r` (default) or `s` | DKIM [alignment](https://verifiedinbox.ai/docs/glossary.md#alignment) mode: `r` (relaxed, allows subdomains) or `s` (strict, exact domain match). | | `aspf` *(optional)* | `r` (default) or `s` | SPF alignment mode: `r` (relaxed, allows subdomains) or `s` (strict, exact domain match). | | `fo` *(optional)* | `0` (default), `1`, `d`, `s` | Forensic reporting trigger options when `ruf` is used. | | `ri` *(optional)* | Seconds (default `86400`) | Requested interval between aggregate report batches (24 hours by default). | ### Example records **Stage 1 monitoring (with two report recipients):** ```text v=DMARC1; p=none; rua=mailto:dmarc@yourcompany.com, mailto:postmaster@yourcompany.com ``` **Standard enforcement with quarantine (BIMI-ready):** ```text v=DMARC1; p=quarantine; rua=mailto:dmarc@yourcompany.com ``` **Strict enforcement for the domain and all subdomains (recommended target state):** ```text v=DMARC1; p=reject; sp=reject; adkim=r; aspf=r; rua=mailto:dmarc@yourcompany.com ``` ## Common issues & edge cases - **Sending `rua=` reports to a different domain without External Report Authorization.** By default, receiving servers only send `rua=` reports to addresses on the **same** domain as the DMARC record (for example `mailto:dmarc@yourcompany.com` for `yourcompany.com`). If you send reports to an external domain (such as `mailto:reports@dmarc-vendor.com`), the external domain must publish a DNS TXT record at `yourcompany.com._report._dmarc.dmarc-vendor.com` with the value `v=DMARC1`. Without that authorization record, receivers silently drop your `rua=` address. - **Omitting `mailto:` on a second `rua=` address.** When listing multiple report addresses, every address needs its own `mailto:` prefix, separated by a comma: `rua=mailto:dmarc@yourcompany.com, mailto:ops@yourcompany.com`. Writing `rua=mailto:dmarc@yourcompany.com, ops@yourcompany.com` is a syntax error. - **Using strict SPF alignment (`aspf=s`) with a provider that uses a return-path subdomain.** Many providers (Postmark, Amazon SES, Resend, SendGrid) send bounces to a subdomain like `pm-bounces.yourcompany.com`. Under `aspf=s`, that subdomain does not match `yourcompany.com` and SPF alignment fails. Leave `aspf=r` (the default) so subdomains align properly. - **Leaving `p=none` in place.** `v=DMARC1; p=none` only monitors; inboxes will not display a [BIMI](https://verifiedinbox.ai/docs/bimi.md) logo until `p=quarantine` (with `pct=100`) or `p=reject` is live. - **Setting `pct` below `100` or `sp=none`.** The BIMI specification requires `pct=100` (or no `pct` tag) on your domain and forbids `sp=none` on the [organizational domain](https://verifiedinbox.ai/docs/glossary.md#organizational-domain). If you omit `sp=`, subdomains automatically inherit your `p=quarantine` or `p=reject` policy. - **Two `_dmarc` TXT records.** A domain may only have **one** TXT record starting with `v=DMARC1` at `_dmarc`. If two `_dmarc` records exist, receivers ignore DMARC altogether. - **Putting `p=` anywhere other than right after `v=DMARC1;`.** The DMARC specification requires `v=DMARC1` to be the first tag and `p=...` (or `sp=...`) to follow immediately as the second tag. - **Duplicated domain name in the Host field.** Some DNS providers automatically append `.yourcompany.com` to whatever you type in the Host field. If you enter `_dmarc.yourcompany.com`, it may create `_dmarc.yourcompany.com.yourcompany.com`. VerifiedInbox checks your [authoritative nameservers](https://verifiedinbox.ai/docs/glossary.md#authoritative-dns) directly and warns you if this happens. --- # BIMI: brand identity in emails > How BIMI works, its five constituent parts, every DNS record required, Gmail SVG and PEM rules, and how to configure and troubleshoot your inbox logo. [BIMI](https://verifiedinbox.ai/docs/glossary.md#bimi) (Brand Indicators for Message Identification) is the open industry standard that lets Gmail, Apple Mail, Yahoo Mail, Fastmail, and other supporting inboxes display your official company logo next to the emails you send. BIMI is a display signal layered on top of strong email authentication: your logo only appears when a message is cryptographically authenticated and passes [DMARC](https://verifiedinbox.ai/docs/dmarc.md) at enforcement. In Gmail: - With a **[VMC](https://verifiedinbox.ai/docs/glossary.md#vmc)** (Verified Mark Certificate, backed by a registered trademark), Gmail displays your **logo and the [blue verified checkmark](https://verifiedinbox.ai/docs/glossary.md#blue-checkmark)**. - With a **[CMC](https://verifiedinbox.ai/docs/glossary.md#cmc)** (Common Mark Certificate, backed by 12+ months of prior use), Gmail displays your **logo** next to your sender name without the checkmark. ## How BIMI works Inboxes do not take your logo from an email attachment. Instead, every time an email arrives from your [domain](https://verifiedinbox.ai/docs/glossary.md#domain): 1. **Authenticate and align:** The receiving inbox checks that the message passes [SPF](https://verifiedinbox.ai/docs/spf.md) or [DKIM](https://verifiedinbox.ai/docs/dkim.md) aligned with the visible `From:` domain, and that your domain's [DMARC](https://verifiedinbox.ai/docs/dmarc.md) policy is at enforcement (`p=quarantine` at `100%` or `p=reject`). 2. **Look up your BIMI DNS record:** The inbox queries a public [TXT record](https://verifiedinbox.ai/docs/glossary.md#txt-record) at `default._bimi.` (or falls back to your main [organizational domain](https://verifiedinbox.ai/docs/glossary.md#organizational-domain)). 3. **Fetch the logo and certificate:** That record points to two HTTPS URLs: your [SVG Tiny P/S](https://verifiedinbox.ai/docs/glossary.md#svg-tiny-ps) logo file (`l=`) and your mark certificate [PEM](https://verifiedinbox.ai/docs/glossary.md#pem) chain (`a=`). 4. **Verify and display:** The inbox validates the certificate chain, confirms the SVG embedded inside the certificate matches your logo, and renders your logo beside the message. ## The five constituent parts of BIMI Every live BIMI setup relies on five parts working together: 1. **Aligned [SPF](https://verifiedinbox.ai/docs/spf.md) or [DKIM](https://verifiedinbox.ai/docs/dkim.md) authentication:** At least one must pass and [align](https://verifiedinbox.ai/docs/glossary.md#alignment) with the visible `From:` header domain. Aligned DKIM is strongly recommended because it survives forwarding and works even when an email service cannot customize the `Return-Path` domain. 2. **[DMARC](https://verifiedinbox.ai/docs/dmarc.md) at 100% enforcement:** Your organizational domain (and any sending subdomain) must publish `p=quarantine` (with `pct=100` or no `pct` tag) or `p=reject`, and must **not** set `sp=none`. 3. **A compliant [SVG Tiny P/S](https://verifiedinbox.ai/docs/glossary.md#svg-tiny-ps) logo:** A square, self-contained vector file meeting both the BIMI specification and Gmail's strict rendering rules (VerifiedInbox converts and checks your logo automatically; see [Logo requirements](https://verifiedinbox.ai/docs/logo-requirements.md)): - `version="1.2"` and `baseProfile="tiny-ps"` on the `` root element. - Square `viewBox` with **absolute pixel dimensions** of at least `width="96" height="96"` (never relative percentages like `width="100%"`), designed to remain recognizable at 20 to 40 px in both light and dark modes. - A **solid background color** baked into the SVG (inboxes crop the image into a circle or squircle; transparent backgrounds often render poorly in dark mode). - A `` element containing your organization's name (and an optional `` element for accessibility). - File size of **32 KB or smaller**. - **Forbidden elements:** no scripts, no animations or interactive elements, no external links or references, no embedded raster images (PNG/JPG), and no `x=` or `y=` attributes on the root `` element. 4. **A mark certificate ([VMC](https://verifiedinbox.ai/docs/glossary.md#vmc) or [CMC](https://verifiedinbox.ai/docs/glossary.md#cmc)) PEM chain:** Issued by an accredited Mark Verifying Authority (such as DigiCert). The hosted `.pem` file must include the **complete certificate chain** in order: your entity certificate (which cryptographically embeds a copy of your SVG logo), followed by the intermediate CA certificate(s) and root CA certificate. See [VMC vs CMC](https://verifiedinbox.ai/docs/vmc-vs-cmc.md) and [Certificates & renewals](https://verifiedinbox.ai/docs/certificates-and-renewals.md). 5. **The `default._bimi` DNS TXT record and HTTPS hosting:** The public DNS record linking your hosted `.svg` and `.pem` files over HTTPS (TLS 1.2+). ## DNS records required for BIMI In the VerifiedInbox setup wizard, the **DNS** step shows every record your domain needs on one page, with copy buttons and live status checks against your domain's [authoritative nameservers](https://verifiedinbox.ai/docs/glossary.md#authoritative-dns) (so changes appear within seconds rather than hours). Some DNS providers ask for the full host name (`default._bimi.yourdomain.com`), while others only want the prefix (`default._bimi`). The wizard shows both formats for your DNS provider. ### 1. Domain ownership | Field | Value | |---|---| | **Purpose** | Proves you control the domain ([ownership check](https://verifiedinbox.ai/docs/glossary.md#domain-ownership)) | | **Type** | `TXT` | | **Host (name)** | `_verifiedinbox` | | **Example value** | `verifiedinbox-verification=…` (your unique code) | ### 2. Allowed senders (SPF) | Field | Value | |---|---| | **Purpose** | Lists the servers allowed to send email for your domain ([SPF](https://verifiedinbox.ai/docs/spf.md)) | | **Type** | `TXT` | | **Host (name)** | `@` (root domain) | | **Example value** | `v=spf1 include:_spf.google.com ~all` | ### 3. Email signatures (DKIM) | Field | Value | |---|---| | **Purpose** | Publishes the public key used to verify your outgoing email signatures ([DKIM](https://verifiedinbox.ai/docs/dkim.md)) | | **Type** | `TXT` or `CNAME` | | **Host (name)** | `._domainkey` | | **Example value** | Provided by each email service you use | ### 4. Fake email protection (DMARC) | Field | Value | |---|---| | **Purpose** | Instructs inboxes to quarantine or block unauthenticated mail ([DMARC](https://verifiedinbox.ai/docs/dmarc.md)) | | **Type** | `TXT` | | **Host (name)** | `_dmarc` | | **Example value** | `v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com` | ### 5. Inbox logo & certificate (BIMI) | Field | Value | |---|---| | **Purpose** | Points supporting inboxes to your hosted logo and mark certificate ([BIMI](https://verifiedinbox.ai/docs/glossary.md#bimi)) | | **Type** | `TXT` | | **Host (name)** | `default._bimi` | | **Example value** | `v=BIMI1; l=https://bimi.verifiedinbox.ai/yourdomain.com/logo.svg; a=https://bimi.verifiedinbox.ai/yourdomain.com/certificate.pem` | ## How to configure BIMI 1. **Complete your email security and certificate steps.** Once your certificate is issued, VerifiedInbox automatically hosts your approved [SVG Tiny P/S](https://verifiedinbox.ai/docs/glossary.md#svg-tiny-ps) logo and full [PEM](https://verifiedinbox.ai/docs/glossary.md#pem) certificate chain (entity + intermediate + root certificates) at `https://bimi.verifiedinbox.ai` with the required HTTPS headers and MIME types. See [Going live](https://verifiedinbox.ai/docs/hosting.md). 2. **Add the `default._bimi` TXT record** where you manage your domain's DNS (for example Cloudflare, Route 53, GoDaddy, Namecheap, or Squarespace): - **Type:** `TXT` - **Host / Name:** `default._bimi` - **Value:** ```text v=BIMI1; l=https://bimi.verifiedinbox.ai/yourdomain.com/logo.svg; a=https://bimi.verifiedinbox.ai/yourdomain.com/certificate.pem ``` - **TTL:** Default (or 3600 seconds). 3. **Click Check now** in the wizard. We query your authoritative nameservers directly to confirm the record is live and that the hosted files match. ## Technical reference: BIMI tags, selectors & declination ### BIMI DNS record tags A BIMI record is a semicolon-separated list of `tag=value` attributes published as a DNS `TXT` record at `._bimi.` (almost always `default._bimi.`): | Tag | Required? | Meaning | |---|---|---| | `v=BIMI1` | Required (first) | Identifies the TXT record as a BIMI version 1 assertion. | | `l=` | Required | HTTPS URL of your SVG Tiny P/S logo (`Content-Type: image/svg+xml`). Always include `l=` so every BIMI provider (Gmail, Apple Mail, Yahoo, Fastmail) can fetch the SVG. | | `a=` | Required by Gmail & Apple | HTTPS URL of your VMC or CMC certificate PEM chain (authority evidence). | | `avp=brand` \| `personal` | Optional | Avatar Preference hint telling supporting clients whether to prefer the BIMI `brand` logo or a user's `personal` profile photo when both exist. | ### Using BIMI selectors for multiple logos or mail streams By default, every inbox looks up `default._bimi.`. If your company wants to show a different logo for a specific brand or newsletter stream on the same domain: 1. Publish a second BIMI TXT record under a custom selector name, such as `newsletter._bimi.yourcompany.com`. 2. Add the `BIMI-Selector` header to outgoing emails on that stream: ```text BIMI-Selector: v=BIMI1; s=newsletter; ``` ### Excluding a subdomain (BIMI declination) Because subdomains automatically inherit the `default._bimi` record of your main [organizational domain](https://verifiedinbox.ai/docs/glossary.md#organizational-domain), you can explicitly opt a specific subdomain out of showing a BIMI logo by publishing a **declination record** (with empty `l=` and `a=` tags) at `default._bimi.`: ```text v=BIMI1; l=; a=; ``` ## How to troubleshoot BIMI issues If your BIMI record is published but you do not see your logo in the inbox yet: 1. **Send a real test email** using the **Go live** (or **Email security**) step in VerifiedInbox. We inspect the actual `Authentication-Results` header to verify that SPF, DKIM, and DMARC all pass with [alignment](https://verifiedinbox.ai/docs/glossary.md#alignment). 2. **Verify DMARC is at 100% enforcement on both the domain and organizational domain.** Make sure `_dmarc` has `p=quarantine` (without `pct` below `100`) or `p=reject`, and that your organizational domain does not set `sp=none`. 3. **Check the hosted files.** Both the `l=` logo URL and the `a=` certificate URL must be publicly reachable over HTTPS (TLS 1.2+), return status `200`, serve the full certificate chain (entity + intermediate CA certificates), and match the exact SVG embedded in your mark certificate. VerifiedInbox handles and monitors all of this automatically when you use our hosting. 4. **Allow 24 to 48 hours for inbox caching.** After all technical checks pass, Gmail and other mailbox providers cache DNS and certificate lookups for up to 48 hours before showing the logo on new messages. ## Common issues & edge cases - **DMARC policy is still `p=none` or `pct=25`.** Inboxes ignore BIMI unless DMARC is at `p=quarantine` (`pct=100`) or `p=reject`. - **Sending from a subdomain while the main domain has `sp=none`.** Even if `news.yourdomain.com` has its own `p=reject` DMARC record, BIMI evaluators also inspect the [organizational domain](https://verifiedinbox.ai/docs/glossary.md#organizational-domain) (`yourdomain.com`) and reject BIMI if `sp=none` is set there. - **Incomplete PEM certificate chain (missing intermediate CA certificates).** When you receive a VMC or CMC from an issuer, the `.pem` file hosted at `a=` must contain the entity certificate *plus* the intermediate CA certificate(s) in order. Hosting only the leaf certificate causes Gmail and Apple Mail validation to fail. (VerifiedInbox builds and serves the complete chain automatically.) - **Editing the SVG file after the certificate was issued.** A mark certificate embeds a cryptographic copy of your exact SVG Tiny P/S file. If the file at `l=` changes by even one character or whitespace byte, validation fails. To update your logo, request a [reissue](https://verifiedinbox.ai/docs/glossary.md#reissue) so a matching certificate is issued. - **Relative SVG dimensions (`width="100%"`) or `x=`/`y=` attributes on ``.** Gmail requires explicit pixel dimensions of at least `width="96" height="96"` on the `` root element and rejects files that use `100%` or include `x=`/`y=` attributes on ``. - **Self-encoding or hosting blockers (WAF, bot protection, geo-fencing, or wrong MIME type).** If you self-host your SVG or PEM behind a CDN firewall, Cloudflare bot challenge, IP allowlist, geo-block, or `Content-Disposition: attachment` header, mailbox provider crawlers cannot fetch the files. Using VerifiedInbox hosting (`https://bimi.verifiedinbox.ai`) avoids these issues. - **Self-asserted BIMI (`l=` without `a=`) not showing in Gmail or Apple Mail.** Some providers (such as Yahoo Mail) may display a "self-asserted" SVG logo without a certificate on bulk mail from reputable senders, but **Gmail and Apple Mail strictly require a VMC or CMC certificate (`a=`)** before displaying any BIMI logo. - **Testing with a one-off personal email on Yahoo or low-reputation domain.** Each mailbox provider applies its own sender reputation and volume rules. For example, Yahoo only displays BIMI logos on bulk/commercial mail streams, not on personal 1-to-1 messages. - **Smart quotes or line breaks in the TXT value.** Copy and paste the `v=BIMI1; ...` string as a single line of plain text without typographic quotes. ## Frequently asked questions ### Which inboxes support BIMI? Gmail, Apple Mail (iOS 16+, macOS Ventura+, and iCloud Mail), Yahoo Mail, Fastmail, La Poste, Onet Poczta, Zoho Mail, and Zone support BIMI. Gmail and Apple Mail require a mark certificate ([VMC](https://verifiedinbox.ai/docs/glossary.md#vmc) or [CMC](https://verifiedinbox.ai/docs/glossary.md#cmc)); Gmail adds the blue verified checkmark when your certificate is a VMC. ### Do I need both `l=` and `a=` in my BIMI record? Yes. Always include both `l=` (your SVG Tiny P/S logo URL) and `a=` (your VMC or CMC certificate PEM URL). While Gmail can also read the SVG embedded inside the `a=` PEM file, other providers (such as Yahoo and Fastmail) read the SVG directly from `l=`. ### How long is a BIMI mark certificate valid, and does the 47-day TLS rule affect it? BIMI mark certificates (VMC and CMC) are valid for up to **398 days** (about 13 months). The CA/Browser Forum's move toward 47-day lifetimes applies only to browser Web PKI TLS server certificates; BIMI mark certificates are governed separately by the AuthIndicators Working Group and are **not** affected by the 47-day TLS schedule. ### Can my BIMI record be on my domain while the SVG and PEM files are hosted on another domain? Yes. Cross-domain HTTPS hosting (such as hosting your files on `https://bimi.verifiedinbox.ai` while your DNS record lives at `default._bimi.yourdomain.com`) is completely standard and supported by every BIMI mailbox provider. ### Do I need to change my BIMI DNS record when my certificate renews? No. When VerifiedInbox hosts your logo and certificate, your `default._bimi` record points to stable URLs at `https://bimi.verifiedinbox.ai`. When we renew your certificate each year, we update the hosted certificate file in place with zero DNS changes required from you. ### What if my email provider cannot set a custom `Return-Path` for SPF? That is fine for BIMI. BIMI requires [DMARC](https://verifiedinbox.ai/docs/dmarc.md) alignment with the visible `From:` domain, which is satisfied as long as your email provider signs messages with a [DKIM](https://verifiedinbox.ai/docs/dkim.md) key whose `d=` domain aligns with your `From:` domain. ### Does BIMI replace a user's personal profile photo? Mailbox providers decide whether to show a personal contact photo or the BIMI brand logo in a given view, though you can include the optional `avp=brand` or `avp=personal` tag in your BIMI record as a preference hint. --- # Logo requirements > What makes a logo work in the inbox, how we convert yours to the required format, and how you approve the result. Inboxes show your logo as a small circle next to your name. They need it in a very specific file format, [SVG Tiny P/S](https://verifiedinbox.ai/docs/glossary.md#svg-tiny-ps). You don't need to create that file: give us your logo and we prepare it. ## What to give us Any of these works: - **An SVG file** of your logo (best). Ask your designer or check your brand guidelines. - **A link** to your logo file online. - **The logo on your website.** We look for it automatically; you just confirm. The file can be up to 512 KB. If you only have a PNG or JPG, we'll tell you; a vector version (SVG, or a PDF/EPS from your designer) gives the best result. ## What makes a logo look good in the inbox - **Square.** The logo is shown in a circle, so a square layout with some breathing room around the mark works best. - **A solid background.** Use your brand color or white behind the mark, rather than a transparent background. - **Simple and bold.** It's shown small. A symbol or monogram often reads better than a long wordmark. - **The same as your trademark** if you want the blue checkmark. See [Trademarks](https://verifiedinbox.ai/docs/trademarks.md). ## What we do to your logo 1. **We keep your original** file exactly as you gave it, separately. 2. **We create an inbox-ready copy** in SVG Tiny P/S: a square canvas, a title with your company name, and without the features the format doesn't allow (scripts, embedded images, animations, external links, some effects). 3. **We compare the two visually** and list every change we made, flagging any that alter how the logo looks. 4. **You review them side by side** and approve the version that will be used. Some advanced design effects can't be carried over exactly. When that happens, we tell you, and you can provide a simpler version. ## Approval In the setup phase, you approve the exact inbox-ready file. Nothing is sent to the certificate issuer until you do. If you change your logo later, we can prepare a new version and [reissue](https://verifiedinbox.ai/docs/glossary.md#reissue) your certificate. --- # Trademarks > Why the blue checkmark needs a registered trademark, how we find and verify yours, and what to do if you don't have one. Gmail's blue checkmark (a [VMC](https://verifiedinbox.ai/docs/glossary.md#vmc)) is only available for logos that are [registered trademarks](https://verifiedinbox.ai/docs/glossary.md#trademark) owned by your company. This is how the certificate issuer knows the logo is really yours. ## Which trademark offices we support Our first release supports the **United States Patent and Trademark Office** ([USPTO](https://verifiedinbox.ai/docs/glossary.md#uspto)). If your trademark is registered elsewhere, [contact us](https://verifiedinbox.ai/support?kind=support) and we'll tell you what's possible. The trademark must be **registered** (not just applied for), and it should be a design mark, or a design plus words, that matches the logo you want to show. ## How we find your trademark 1. When you add your logo, we automatically search the USPTO register for your company and your logo. 2. We compare your logo with the images of matching registrations, using AI image comparison with a backup visual-matching method. 3. You see the result: **match found**, **no match**, or **needs review**. If you already know your registration or serial number, enter it and we'll check that exact record. ### Manual review Sometimes a match isn't clear-cut (for example, a slightly different version of the logo, or the trademark is held by a parent company). A person at VerifiedInbox reviews these cases and records the decision. You'll see the outcome on your trademark step. ## The logo must match The logo you show in the inbox should be the same as the registered design. Small differences (colors, a missing tagline) may be accepted or not; the certificate issuer has the final say. If yours differs, we'll tell you before you pay. ## No trademark yet? You have two options, and you can do both: - **Logo only, now.** If your logo has been in public use for at least 12 months, you can get a [CMC](https://verifiedinbox.ai/docs/glossary.md#cmc): your logo in Gmail, without the checkmark. See [VMC vs CMC](https://verifiedinbox.ai/docs/vmc-vs-cmc.md). - **File a trademark.** See below. ## Trademark filing service We can help you register your logo as a trademark. Email [trademark@verifiedinbox.ai](mailto:trademark@verifiedinbox.ai) with your company name and logo, and we'll explain the options, timing and cost. Registration usually takes several months. When it's registered, we can move you from a CMC to a VMC. --- # Certificates and renewals > How your certificate is ordered and issued, and how we renew, reissue or revoke it so your logo never disappears. Your logo is backed by a mark certificate ([VMC](https://verifiedinbox.ai/docs/glossary.md#vmc) or [CMC](https://verifiedinbox.ai/docs/glossary.md#cmc)) issued by an independent [certificate issuer](https://verifiedinbox.ai/docs/glossary.md#certificate-issuer), currently DigiCert. We order it, track it and keep it renewed. ## Ordering We order your certificate automatically as soon as everything is ready: - payment received, - business details verified, - logo approved, - domain ownership confirmed. You don't have to press anything. The certificate step shows each prerequisite and whether it's met. ## Issuer validation The issuer does its own checks of your business, your authority to apply and your logo. They may: - email the applicant to confirm the request, - call the phone number you gave, or - ask for a document. When they need something from you, the certificate step (and an email from us) tells you exactly what to do. Validation usually takes from a few days to a few weeks. If the issuer rejects the application, you get a **full refund**. ## Statuses | Status | Meaning | |---|---| | Not ordered | Waiting for the prerequisites above | | Submitted | Sent to the issuer | | Issuer validation | The issuer is checking | | Waiting for you | The issuer needs an action from you | | Issued | Your certificate is ready and published | | Expiring | Renewal is under way | | Renewed | A new certificate replaced the old one | | Rejected / Revoked / Expired | See below | ## Renewals Mark certificates are valid for about **13 months** (at most 398 days). If one expires, your logo stops showing. We prevent that: - We **start the renewal automatically** about 45 days before expiry, as long as your plan is active. - We send **reminders 60, 30, 14, 7 and 1 day** before anything is due, so nothing surprises you. - If something needs your attention (for example the issuer re-confirming your details), we tell you early. - Renewal also renews your plan for another year; the estimated total, including the issuer's fee, is shown before it's charged. You can also renew early from the certificate step. ## Reissue A [reissue](https://verifiedinbox.ai/docs/glossary.md#reissue) gets you a fresh certificate within the same paid year, for example after you update your logo or your company details change. Start it from the certificate step and we handle the rest. ## Revocation [Revocation](https://verifiedinbox.ai/docs/glossary.md#revocation) cancels a certificate permanently, and your logo stops showing. Only do this if something is wrong (for example the certificate shows an outdated legal name, or you no longer use the domain). Because it can't be undone, it requires an explicit confirmation. ## Hosting Your certificate file ([PEM](https://verifiedinbox.ai/docs/glossary.md#pem)) and logo are hosted by us. See [Going live](https://verifiedinbox.ai/docs/hosting.md). --- # Going live & hosting > We host your inbox-ready logo and your certificate so inboxes can always find them. Inboxes don't receive your logo with each email. They look it up: your [BIMI](https://verifiedinbox.ai/docs/bimi.md) record points to a logo file and a certificate file on the internet, and the inbox downloads them. Those files must always be online, fast and correct. We take care of that ([logo and certificate hosting](https://verifiedinbox.ai/docs/glossary.md#logo-hosting)). ## Where your files live ```text https://bimi.verifiedinbox.ai/yourdomain.com/logo.svg https://bimi.verifiedinbox.ai/yourdomain.com/certificate.pem ``` Your BIMI record points to these addresses. You don't have to upload anything or change anything when we renew: the addresses stay the same, and we swap in the new certificate. ## What we take care of - **Correct formats:** the inbox-ready logo ([SVG Tiny P/S](https://verifiedinbox.ai/docs/glossary.md#svg-tiny-ps)) and the full certificate chain ([PEM](https://verifiedinbox.ai/docs/glossary.md#pem)), with the right content types. - **Matching files:** the hosted logo always matches the one inside your certificate. - **Availability:** files are served over HTTPS and cached for speed. - **Monitoring:** we regularly check that your record, logo and certificate are reachable and consistent, and alert you if not. ## Can I host the files myself? You can, but you'd have to update them at every renewal and keep them in sync with your certificate. Hosting is included in your plan, so most customers let us do it. ## Before you go live The addresses return "not found" until your certificate is issued. That's expected. --- # Test mode > Try every step, including checkout and certificate issuance, without paying or ordering a real certificate. Test mode ([sandbox](https://verifiedinbox.ai/docs/glossary.md#sandbox)) is a separate practice environment. It works like Stripe's test mode: you switch between **Live** and **Test** with one toggle, and the two never mix. ## What's different in test mode | | Live | Test | |---|---|---| | Payments | Real (Stripe) | Stripe test mode: nothing is charged; use Stripe's test cards | | Certificates | Issued by DigiCert | Simulated issuer; certificates aren't trusted by any inbox | | Data | Your real domains and orders | Kept completely separate | | API keys | `vi_live_…` | `vi_test_…` | | Banner | — | An orange "Test mode" bar across the top | The free checks (domain, email security, logo, trademark, eligibility) are real in both modes, because they only read public information. ## How to switch - **Website:** sign in, then use the Live/Test switch in your dashboard. An orange banner shows whenever you're in test mode. - **API:** use a `vi_test_…` key (keys are tied to one environment), or send `VI-Environment: test` with an OAuth token. - **MCP / AI assistants:** connect with a test key, or pass `environment: "test"` to account tools. ## Good uses for test mode - Showing your team the whole process before buying. - Letting your AI assistant rehearse the flow. - Building and testing an integration against our API. Test data may be reset from time to time. --- # Accounts and organizations > Sign in only when you need to, manage several companies and domains, invite your team and create API keys. You don't need an account for the free checks. You create one when you're ready to pay, and everything you did before moves into it automatically. ## Signing in We use secure single sign-on (through WorkOS): sign in with your email or your Google account, or your company's identity provider if it's set up. When you sign in, any domains you checked in that browser (or through a resume link) are **claimed** into your current organization in one step. If your organization already has an application for the same domain, we keep the more advanced one (a paid one wins over an unpaid one) and archive the other, so you never end up with duplicates. ## Organizations An organization is a company (or a client, if you're an agency). You can: - belong to **several organizations** and switch between them, - add **several domains** to each organization, each with its own plan and certificate, - invite teammates with a role (owner, admin or member). Each organization has its own billing, domains, API keys and history, and separate **live** and **test** data. See [Test mode](https://verifiedinbox.ai/docs/test-mode.md). ## Domains Each domain has its own application: its steps, statuses, certificate, renewal date and full history of events. Your dashboard shows what needs your attention across all of them. ## API keys Create [API keys](https://verifiedinbox.ai/docs/glossary.md#api-key) in **Dashboard → Settings → API keys** for your tools or AI assistant. - A key belongs to one organization and one environment: `vi_live_…` or `vi_test_…`. - Give each key only the permissions it needs (scopes): | Scope | Allows | |---|---| | `applications:read` | Reading applications, DNS records, certificates, orders | | `applications:write` | Starting and updating applications, business details, logo approval, claiming drafts | | `billing:write` | Creating checkout links (a person still has to pay) | | `certificates:write` | Ordering, renewing, reissuing and revoking certificates | | `support:write` | Contacting support | - The full key is shown **once**, when you create it. Store it safely, and revoke keys you no longer use. ## Continuing on another device Without an account, your progress is tied to your browser. To continue elsewhere, create a **resume link** from the wizard (or ask your AI assistant): it's single-use and valid for 24 hours. After you sign in, your account is all you need. ## Billing Plans are billed per domain, per year, through Stripe. Invoices and payment methods are available from the billing portal in your dashboard. See [Pricing](https://verifiedinbox.ai/pricing.md). --- # Security > How we protect your domain, your data and your certificate, and the limits we put on what software and AI agents can do. A mark certificate represents your brand in millions of inboxes. We treat every step that could affect it with care. ## Proving it's really you - **Domain control.** Before we order, reissue or revoke a certificate, you prove you control the domain by adding a unique [ownership record](https://verifiedinbox.ai/docs/glossary.md#domain-ownership). If that record disappears later, we notice. - **Authority.** The applicant must be allowed to act for the company: confirmed by an email address at your domain, or by your attestation, and then verified by the certificate issuer. - **Business verification.** We check your company against official registries before the issuer does its own checks. ## Private by default - Knowing a domain gives no one access to its application. Only the person who started it (in their browser or with their private guest token), members of the owning organization and our operators can see it. Anyone else gets "not found", so we never even confirm an application exists. - Free checks only read public information and never reveal anything about existing applications. - Links you share point to steps, never to credentials. One-time resume links keep their secret in the part of the URL that browsers don't send to servers, expire after 24 hours and work once. ## Organizations stay separate Every query is scoped to your organization and environment (live or test). API keys and AI-assistant sessions carry explicit permissions (scopes), so a read-only key can't change anything. ## Limits on software and AI agents - **Agents can't pay.** They can create a checkout link; only a person can complete payment on Stripe's page. - **No DNS changes on your behalf** in this release; you add records yourself, and we never ask for your DNS login. - **Revocation requires an explicit confirmation** and a dedicated permission. - **Idempotency.** Payments, certificate orders and retries are protected against accidental duplicates. ## Safe handling of files and links - Uploaded SVG logos are size-limited, checked, never rendered directly inside our pages and served with strict security headers. - Links you give us are fetched through a protected fetcher that only reaches public internet addresses, with size and time limits. ## Abuse protection Anonymous use is rate-limited, and expensive work (trademark searches, AI image comparison, automated browsing) has budgets, so the free tools stay fast for everyone. ## Secrets Our own credentials are kept out of code and stored as protected configuration. We don't store your DNS provider credentials. ## Reporting a problem If you think you've found a security issue, email [support@verifiedinbox.ai](mailto:support@verifiedinbox.ai) with the details. We'll respond quickly. --- # MCP server > Let Claude, ChatGPT, Cursor and other AI assistants check your domain and move your verification forward, step by step. [MCP](https://verifiedinbox.ai/docs/glossary.md#mcp) (Model Context Protocol) is how AI assistants use tools. Our MCP server gives your assistant every VerifiedInbox capability, from the first free check to certificate renewals. The easiest way to start: paste this into your assistant. > Add a verified VMC mark to my email domain using verifiedinbox.ai/get-started.md The [get-started guide](https://verifiedinbox.ai/get-started.md) tells your assistant everything else. ## Endpoint - **URL:** `https://api.verifiedinbox.ai/mcp` - **Transport:** Streamable HTTP, JSON responses. Protocol versions `2025-11-25`, `2025-06-18`, `2025-03-26`. - Every tool wraps one or more [API](https://verifiedinbox.ai/docs/reference/api/v1.md) operations, and its arguments are generated from the [OpenAPI schema](https://verifiedinbox.ai/openapi.json), so they always match the API. ## Connecting | Mode | How | Can do | |---|---|---| | Anonymous | Connect with no credentials | All free tools. `start_application` returns a `guest_token` to pass to later calls. | | OAuth | Clients that support MCP sign-in open a sign-in window when an account tool is first used | Everything your role allows | | API key | `Authorization: Bearer vi_live_…` or `vi_test_…` ([API keys](https://verifiedinbox.ai/docs/accounts.md#api-keys)) | One organization and environment | ### Claude claude.ai and Claude Desktop: **Settings → Connectors → Add custom connector**, URL `https://api.verifiedinbox.ai/mcp`. Claude Code: ```bash claude mcp add --transport http verifiedinbox https://api.verifiedinbox.ai/mcp ``` ### Cursor `~/.cursor/mcp.json` (or `.cursor/mcp.json` in a project): ```json { "mcpServers": { "verifiedinbox": { "url": "https://api.verifiedinbox.ai/mcp" } } } ``` With an API key, add `"headers": { "Authorization": "Bearer vi_test_…" }`. ### ChatGPT Add a custom connector with the URL `https://api.verifiedinbox.ai/mcp` (Settings → Apps & Connectors; some plans require developer mode). More clients and details: [get-started.md](https://verifiedinbox.ai/get-started.md#connect-to-the-mcp-server). ## Your progress never resets - Everything is saved on our servers under a stable `application_id`. - Without an account, your assistant keeps the `guest_token` (valid 30 days from last use). In a new conversation it calls `list_applications` or `get_application` and continues. - `create_resume_link` gives you a one-time link (24 hours) to continue in your browser. - After you sign in, `claim_application` moves the draft into your account. - Every response includes `next_actions`, so your assistant always knows what's next. ## Tools Each tool does the same thing as one or more [API](https://verifiedinbox.ai/docs/reference/api/v1.md) operations. The table is generated from the [OpenAPI schema](https://verifiedinbox.ai/openapi.json) and the server's tool list, so it always matches what your assistant sees. Free tools accept `guest_token` (to continue an anonymous draft); account tools accept `environment` (`live` or `test`). ### `get_started` Explains the whole process (free checks, paid setup, certificate, go-live), pricing, what the user must do themselves, and how to continue later. Call this first if you're new to VerifiedInbox. | Field | Value | |---|---| | **Arguments** | None | | **Access** | Free | | **API operations** | `GET /v1/meta` | ### `check_domain` Free, instant check of an email domain: can it get a verified logo? Detects shared providers that can't be used (gmail.com, outlook.com…), unregistered domains, the mailbox provider and a DMARC/BIMI summary. Accepts a domain, URL or email address. | Field | Value | |---|---| | **Arguments** | `domain`* | | **Access** | Free | | **API operations** | `POST /v1/checks/domain` | ### `check_email_security` Free full inspection of SPF, DKIM, DMARC and BIMI with plain-English findings and exact fixes. Uses the domain's authoritative nameservers (sees changes within seconds). | Field | Value | |---|---| | **Arguments** | `domain`*, `dkim_selectors` | | **Access** | Free | | **API operations** | `POST /v1/checks/email-auth` | ### `start_application` Starts the verification for a domain, or returns the existing one. Anonymous callers receive a guest_token — tell the user to keep it (with the application_id) to continue in a later conversation. Background checks start immediately. Returns the full state and next_actions. | Field | Value | |---|---| | **Arguments** | `domain`*, `environment`, `guest_token` | | **Access** | Free | | **API operations** | `POST /v1/applications` | ### `list_applications` Lists the verifications visible to the caller (guest drafts for a guest_token, or the account's domains). | Field | Value | |---|---| | **Arguments** | `environment`, `guest_token` | | **Access** | Free | | **API operations** | `GET /v1/applications` | ### `get_application` Full current state: separate outcomes (eligibility, payment, business, ownership, logo, trademark, certificate, BIMI, inbox display), the 11 wizard steps with status, running background work, and next_actions (what to do next and which tool to use). Call this whenever you need to know where things stand. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token` | | **Access** | Free | | **API operations** | `GET /v1/applications/{application_id}`, `GET /v1/applications/{application_id}/next-steps` | ### `complete_step` Moves the verification to the next step once the current step's required items are met (each step lists them in `requirements`; status `ready` means it can be completed). Background work never completes steps; this does, on the user's behalf. Also re-confirms an earlier step marked `attention` after it was fixed. Confirm with the user before completing a step they haven't reviewed. | Field | Value | |---|---| | **Arguments** | `application_id`*, `step`*, `guest_token` | | **Access** | Free | | **API operations** | `POST /v1/applications/{application_id}/steps/{step}/complete` | ### `set_email_usage` Ask the user how email is used on their domain and record the answer: none (new domain, no email yet), low (only their mailbox provider, e.g. Google Workspace, sends email) or active (several tools send email: newsletters, billing, support…). none/low can block fake emails right away; active should follow the staged rollout, though the user may skip ahead. The email security step needs DMARC published with a report address (watch mode is enough); filtering or blocking is required by the DNS step. Returns the email security report with the recommended DMARC record. | Field | Value | |---|---| | **Arguments** | `application_id`*, `email_usage`*, `guest_token` | | **Access** | Free | | **API operations** | `PUT /v1/applications/{application_id}/email-usage` | ### `set_dmarc_report_address` Sets the email address for daily DMARC reports, used in every suggested DMARC record (rua=). Ask the user for a mailbox they read (or a DMARC reporting service); by default reports go to dmarc@, which must exist. Pass null to go back to the default. The user still publishes the record. | Field | Value | |---|---| | **Arguments** | `application_id`*, `email`*, `guest_token` | | **Access** | Free | | **API operations** | `PUT /v1/applications/{application_id}/dmarc-reports` | ### `recheck_email_security` Re-checks SPF, DKIM and DMARC for the application right now (after the user published records) and returns the report plus the DMARC rollout plan: every stage's exact record, what to monitor and what happens to email at that stage. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token` | | **Access** | Free | | **API operations** | `POST /v1/applications/{application_id}/email-security/check` | ### `submit_logo` Provide the company logo: SVG text (preferred), a URL to an SVG, or use_discovered=true to use the logo found on the company website. The original is kept untouched; an inbox-ready SVG Tiny P/S version is produced automatically and a US trademark search starts. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token`, `svg`, `url`, `use_discovered` | | **Access** | Free | | **API operations** | `PUT /v1/applications/{application_id}/logo` | ### `get_logo_status` Logo processing status, compliance issues, the list of changes made for the inbox version (lossy ones flagged), visual difference, and preview URLs. Also lists logos discovered on the website. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token` | | **Access** | Free | | **API operations** | `GET /v1/applications/{application_id}/logo`, `GET /v1/applications/{application_id}/logo-candidates` | ### `search_trademark` Starts (or re-runs) the USPTO trademark search, or verifies a specific US serial/registration number the user provides. A registered trademark matching the logo unlocks the blue checkmark (VMC). Rate-limited for anonymous users. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token`, `mark_text`, `owner_name`, `serial_or_registration_number` | | **Access** | Free | | **API operations** | `POST /v1/applications/{application_id}/trademark/search`, `POST /v1/applications/{application_id}/trademark/claim` | ### `get_trademark_status` Trademark search result: matched registration (with similarity to the logo), candidates, and what it means. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token` | | **Access** | Free | | **API operations** | `GET /v1/applications/{application_id}/trademark` | ### `get_eligibility` Outcome: vmc_eligible (logo + blue checkmark), cmc_eligible (logo only, no checkmark), not_currently_eligible (with evidence, estimated date and next steps such as the trademark filing service at trademark@getverifiedinbox.com), or assessing. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token` | | **Access** | Free | | **API operations** | `GET /v1/applications/{application_id}/eligibility` | ### `get_estimate` Itemized yearly price before payment: plan (CMC $899 or VMC $1,499 per domain) plus the certificate issuer's fee (pass-through, no markup, ~$750–$1,750). Full refund if issuance is rejected. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token`, `product` | | **Access** | Free | | **API operations** | `GET /v1/applications/{application_id}/estimate` | ### `watch_eligibility` Emails the user when the domain becomes eligible (we re-check daily). Use for not_currently_eligible outcomes. | Field | Value | |---|---| | **Arguments** | `application_id`*, `email`*, `guest_token` | | **Access** | Free | | **API operations** | `POST /v1/applications/{application_id}/eligibility/watch` | ### `create_resume_link` Creates a one-time link (valid 24h) that opens this verification in the web app — useful for visual steps like reviewing the logo or copying DNS records. Optionally emails it. | Field | Value | |---|---| | **Arguments** | `application_id`*, `email`, `guest_token` | | **Access** | Free | | **API operations** | `POST /v1/applications/{application_id}/resume-links` | ### `explain_term` Plain-English explanation of a term such as BIMI, VMC, CMC, DMARC, SPF, DKIM, selector, alignment, SVG Tiny P/S. | Field | Value | |---|---| | **Arguments** | `term`* | | **Access** | Free | | **API operations** | `GET /v1/glossary/{term}` | ### `contact_support` Sends a message to the VerifiedInbox team (a human replies by email within one business day). Include the user's email. | Field | Value | |---|---| | **Arguments** | `message`*, `application_id`, `email`, `guest_token`, `kind`, `name` | | **Access** | Free | | **API operations** | `POST /v1/support` | ### `send_feedback` Sends feedback or a feature request to the VerifiedInbox team. | Field | Value | |---|---| | **Arguments** | `message`*, `email`, `guest_token`, `kind` | | **Access** | Free | | **API operations** | `POST /v1/support` | ### `get_account` Organizations, environment (live/test), every domain with its status and pending actions, and recent orders. | Field | Value | |---|---| | **Arguments** | `environment` | | **Access** | Account | | **API operations** | `GET /v1/me`, `GET /v1/applications`, `GET /v1/orders` | ### `claim_application` Atomically moves a guest draft (application_id + guest_token) into the signed-in account. If the account already has the domain, keeps the more advanced one and reports which. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token`*, `environment` | | **Access** | Account (`applications:write`) | | **API operations** | `POST /v1/applications/{application_id}/claim` | ### `create_checkout` Creates a Stripe checkout link for the chosen plan. Only a human can pay: give the link to the user, and confirm the price first (see get_estimate). | Field | Value | |---|---| | **Arguments** | `application_id`*, `product`*, `environment`, `idempotency_key` | | **Access** | Account (`billing:write`) | | **API operations** | `POST /v1/applications/{application_id}/checkout` | ### `submit_business_details` Legal company details + the applicant (starts business registration verification). Requires payment. jurisdiction is the US state code like us_de. | Field | Value | |---|---| | **Arguments** | `address`*, `applicant`*, `application_id`*, `jurisdiction`*, `legal_name`*, `authority_attested`, `environment`, `registration_number` | | **Access** | Account (`applications:write`) | | **API operations** | `PUT /v1/applications/{application_id}/business` | ### `get_business_status` Business verification status, matched registry record and applicant authority. | Field | Value | |---|---| | **Arguments** | `application_id`*, `environment` | | **Access** | Account | | **API operations** | `GET /v1/applications/{application_id}/business` | ### `approve_logo` Approves the exact inbox-ready logo the user reviewed (pass converted_sha256 from get_logo_status). Ask the user to look at the preview first (create_resume_link helps). | Field | Value | |---|---| | **Arguments** | `application_id`*, `converted_sha256`*, `environment` | | **Access** | Account (`applications:write`) | | **API operations** | `POST /v1/applications/{application_id}/logo/approve` | ### `get_dns_records` Every DNS record the user must add (ownership TXT, DMARC at the right rollout stage, BIMI) with copy-paste values and live status from the authoritative nameservers. The user adds them at their DNS provider. | Field | Value | |---|---| | **Arguments** | `application_id`*, `environment` | | **Access** | Account | | **API operations** | `GET /v1/applications/{application_id}/dns` | ### `verify_dns` Re-checks the DNS records right now (and watches closely for the next minutes). | Field | Value | |---|---| | **Arguments** | `application_id`*, `environment` | | **Access** | Account | | **API operations** | `POST /v1/applications/{application_id}/dns/verify` | ### `verify_dkim_selectors` Checks DKIM selectors the user provides. Needed only when the common selectors we try automatically found no key; the user's email provider shows the selector in its DKIM settings. | Field | Value | |---|---| | **Arguments** | `application_id`*, `selectors`*, `guest_token` | | **Access** | Free | | **API operations** | `POST /v1/applications/{application_id}/dkim-selectors` | ### `create_test_message` Returns an address the user sends a real email to; we then report SPF/DKIM/DMARC alignment on that actual message. Free. Also finds the DKIM key when the common selectors didn't: an aligned signature on the test email is added to the email security report. | Field | Value | |---|---| | **Arguments** | `application_id`*, `guest_token` | | **Access** | Free | | **API operations** | `POST /v1/applications/{application_id}/test-messages` | ### `get_test_message_result` Result of the test email: SPF, DKIM, DMARC alignment and findings. | Field | Value | |---|---| | **Arguments** | `application_id`*, `test_message_id`*, `guest_token` | | **Access** | Free | | **API operations** | `GET /v1/applications/{application_id}/test-messages/{test_message_id}` | ### `get_certificate` Certificate status timeline, prerequisites (with the step that fixes each), the latest final check, actions the issuer needs from the user, validity and renewal dates. | Field | Value | |---|---| | **Arguments** | `application_id`*, `environment` | | **Access** | Account | | **API operations** | `GET /v1/applications/{application_id}/certificate` | ### `run_final_check` Re-verifies live everything the certificate issuer checks (domain, email protection, logo, company, trademark, DNS records). Runs automatically before the order is submitted and daily during the issuer's review; anything that fails marks its step `attention` and holds the order. | Field | Value | |---|---| | **Arguments** | `application_id`*, `environment` | | **Access** | Account | | **API operations** | `POST /v1/applications/{application_id}/certificate/check` | ### `order_certificate` Submits the certificate order (normally automatic once prerequisites are met). Safe to call repeatedly. | Field | Value | |---|---| | **Arguments** | `application_id`*, `environment`, `idempotency_key` | | **Access** | Account (`certificates:write`) | | **API operations** | `POST /v1/applications/{application_id}/certificate/order` | ### `reissue_certificate` Requests a reissue (e.g. after a logo change). Re-checks domain ownership first. | Field | Value | |---|---| | **Arguments** | `application_id`*, `reason`*, `environment`, `idempotency_key` | | **Access** | Account (`certificates:write`) | | **API operations** | `POST /v1/applications/{application_id}/certificate/reissue` | ### `renew_certificate` Starts the renewal early (renewal is otherwise automatic 45 days before expiry). | Field | Value | |---|---| | **Arguments** | `application_id`*, `environment`, `idempotency_key` | | **Access** | Account (`certificates:write`) | | **API operations** | `POST /v1/applications/{application_id}/certificate/renew` | ### `revoke_certificate` PERMANENTLY revokes the certificate; the logo disappears from inboxes. Only with explicit user confirmation: confirm must be exactly "REVOKE ". | Field | Value | |---|---| | **Arguments** | `application_id`*, `confirm`*, `reason`*, `environment`, `idempotency_key` | | **Access** | Account (`certificates:write`) | | **API operations** | `POST /v1/applications/{application_id}/certificate/revoke` | ### `confirm_inbox_display` Records that the user saw their logo (and checkmark for VMC) in a mailbox such as Gmail — the final 'display observed' outcome. | Field | Value | |---|---| | **Arguments** | `application_id`*, `environment`, `mailbox`, `observed` | | **Access** | Account (`applications:write`) | | **API operations** | `POST /v1/applications/{application_id}/inbox-display` | ### `list_orders` Orders and payments for the account. | Field | Value | |---|---| | **Arguments** | `environment` | | **Access** | Account | | **API operations** | `GET /v1/orders`, `GET /v1/orders/{order_id}` | \* required. ## Resources and prompts - `verifiedinbox://get-started`: the full get-started guide. - `verifiedinbox://glossary`: the glossary. - Prompt `verify_my_domain(domain)`: starts the guided flow. ## Errors Tool failures return `isError: true` with a clear message and `next_actions` (for example: sign in, pay, add a DNS record). Rate limits include `retry_after_seconds`. ## What your assistant won't do By design, assistants can't pay, can't change your DNS, and need your explicit confirmation to revoke a certificate. You approve your logo yourself. See [Security](https://verifiedinbox.ai/docs/security.md). --- # Glossary > Every term we use, explained clearly, with the technical detail for those who want it. Underlined terms open a definition in a side panel when you click them. Here are all the terms in one place. ### Blue checkmark **The verified badge Gmail shows next to a sender's name.** When your company verifies its domain and registered trademark logo with a VMC, Gmail shows your logo and a blue checkmark next to your sender name. Hovering or tapping the checkmark tells recipients that the email genuinely comes from your organization. **Technical detail:** - **Trigger:** Displayed by Gmail when an incoming message passes DMARC at enforcement (`p=quarantine` or `p=reject` at `pct=100`). - **Certificate:** Requires a `default._bimi.` TXT record whose `a=` tag points to a valid Verified Mark Certificate (`VMC`) PEM chain. *Related:* [BIMI](https://verifiedinbox.ai/docs/glossary.md#bimi), [VMC (Verified Mark Certificate)](https://verifiedinbox.ai/docs/glossary.md#vmc) ### BIMI **The open standard that lets inboxes display your official company logo.** BIMI (Brand Indicators for Message Identification) is a public setting on your domain that tells Gmail, Apple Mail, Yahoo Mail, Fastmail, and other supporting inboxes where to find your official logo and mark certificate. Inboxes only display your logo on emails that pass DMARC authentication. **Technical detail:** - **DNS host:** `default._bimi.` (or `._bimi.` when paired with a `BIMI-Selector` header). - **Record syntax:** See [BIMI record tags](https://verifiedinbox.ai/docs/bimi#bimi-dns-record-tags). - **Prerequisite:** DMARC at enforcement (`p=quarantine` with `pct=100`, or `p=reject`) and no `sp=none` on the organizational domain. *Related:* [DMARC](https://verifiedinbox.ai/docs/glossary.md#dmarc), [VMC (Verified Mark Certificate)](https://verifiedinbox.ai/docs/glossary.md#vmc), [CMC (Common Mark Certificate)](https://verifiedinbox.ai/docs/glossary.md#cmc), [SVG Tiny P/S](https://verifiedinbox.ai/docs/glossary.md#svg-tiny-ps) ### VMC (Verified Mark Certificate) **The mark certificate that unlocks your logo and Gmail's blue checkmark.** A digital certificate issued by an accredited authority (such as DigiCert) that proves your logo is a registered trademark owned by your company. In Gmail, a VMC unlocks both your inbox logo and the blue verified checkmark. **Technical detail:** - **Format:** X.509 certificate with the BIMI extended key usage and your SVG Tiny P/S logo embedded in the `logotype` extension (`RFC 3709`). - **Validation:** Requires an active registered design trademark with a recognized intellectual property office (such as the USPTO) plus organization and contact validation. - **Validity:** Up to `398` days. *Related:* [CMC (Common Mark Certificate)](https://verifiedinbox.ai/docs/glossary.md#cmc), [Registered trademark](https://verifiedinbox.ai/docs/glossary.md#trademark), [Certificate issuer](https://verifiedinbox.ai/docs/glossary.md#certificate-issuer) ### CMC (Common Mark Certificate) **A mark certificate that displays your logo without requiring a trademark.** A digital certificate for companies whose logo is not a registered trademark yet, provided the logo has been used publicly on your website for at least 12 months. Gmail and Apple Mail display your logo beside your emails, without the blue checkmark. **Technical detail:** - **Validation:** Validated on prior use (at least `12` months of public display, verified via web archive captures) rather than trademark registration. - **Requirements:** Uses the exact same `default._bimi` DNS record, SVG Tiny P/S profile, and DMARC enforcement rules as a VMC. - **Validity:** Up to `398` days. *Related:* [VMC (Verified Mark Certificate)](https://verifiedinbox.ai/docs/glossary.md#vmc), [Prior use (12 months)](https://verifiedinbox.ai/docs/glossary.md#prior-use) ### DMARC **Your domain's rule for blocking fake emails and receiving daily sender reports.** DMARC tells inboxes what to do when an email claims to come from your domain but fails SPF and DKIM checks: monitor it (none), send it to spam (quarantine), or block it outright (reject). It also emails you daily reports showing every service sending as your domain. Inboxes only show your logo when DMARC is set to quarantine or reject. **Technical detail:** - **DNS host:** `_dmarc.` (`TXT` record, `RFC 7489`). - **Record syntax:** See [DMARC tags and example records](https://verifiedinbox.ai/docs/dmarc#technical-reference-dmarc-syntax-and-example-records). - **Evaluation:** Passes when at least one of SPF or DKIM passes **and** aligns with the visible `From:` domain. - **BIMI rule:** Requires `p=quarantine` (at `pct=100`) or `p=reject`, and forbids `sp=none` on the organizational domain. *Related:* [Safe DMARC rollout](https://verifiedinbox.ai/docs/glossary.md#dmarc-rollout), [SPF](https://verifiedinbox.ai/docs/glossary.md#spf), [DKIM](https://verifiedinbox.ai/docs/glossary.md#dkim), [Alignment](https://verifiedinbox.ai/docs/glossary.md#alignment) ### Safe DMARC rollout **Tightening email protection in stages so legitimate company emails are never blocked.** If your company sends email every day across several tools (newsletters, billing, CRM, support), turning on strict DMARC blocking all at once could send a misconfigured tool's emails to spam. A safe rollout starts in watch mode (p=none) so reports reveal every sender, then moves to partial quarantine (25%), full quarantine (100%, which unlocks your inbox logo), and finally reject. Brand-new domains can skip the rollout and go straight to reject. **Technical detail:** - **Stage 1 (Watch):** `v=DMARC1; p=none; rua=mailto:...` (suggested 14 days to inventory senders). - **Stage 2 (Partial filter):** See [example record](https://verifiedinbox.ai/docs/dmarc#stage-2-quarantine-25) (~7 days). - **Stage 3 (Full quarantine, BIMI-ready):** See [example record](https://verifiedinbox.ai/docs/dmarc#stage-3-quarantine-100) (`pct=100` implied). - **Stage 4 (Strict block, recommended):** `v=DMARC1; p=reject; rua=mailto:...` *Related:* [DMARC](https://verifiedinbox.ai/docs/glossary.md#dmarc), [Alignment](https://verifiedinbox.ai/docs/glossary.md#alignment), [DMARC policy (watch, filter, block)](https://verifiedinbox.ai/docs/glossary.md#dmarc-policy), [DMARC reports](https://verifiedinbox.ai/docs/glossary.md#dmarc-reports) ### DMARC policy (watch, filter, block) **What inboxes do with emails that fail the checks: watch them, send them to spam, or refuse them.** Your DMARC record sets one of three policies. Watch mode (none) changes nothing and only sends you reports. Filter (quarantine) sends failing emails to spam. Block (reject) refuses them outright. Filtering all of your email or blocking is called enforcement, and inboxes only show your logo at enforcement. **Technical detail:** - **Watch:** `p=none` (monitoring, no effect on delivery). - **Filter:** `p=quarantine`; `pct` below `100` filters only part of the failing mail and doesn't count for BIMI. - **Block:** `p=reject` (strongest). - **Subdomains:** `sp=` sets the policy for subdomains; BIMI forbids `sp=none`. - **Details:** See [what quarantine does](https://verifiedinbox.ai/docs/dmarc#what-quarantine-does). *Related:* [DMARC](https://verifiedinbox.ai/docs/glossary.md#dmarc), [Safe DMARC rollout](https://verifiedinbox.ai/docs/glossary.md#dmarc-rollout), [DMARC reports](https://verifiedinbox.ai/docs/glossary.md#dmarc-reports) ### DMARC reports **Daily summaries from inboxes showing every service that sends email as your domain.** Gmail, Yahoo, Outlook and other inboxes email a daily summary to the report address in your DMARC record. Each one lists the servers that sent email as your domain and whether they passed SPF and DKIM, so you can spot a tool you forgot to set up before you filter or block anything. The address must be a mailbox that exists: use a dedicated one (like dmarc@yourcompany.com) or a DMARC reporting service, because busy domains receive many reports. **Technical detail:** - **Tag:** `rua=mailto:dmarc@`; separate several addresses with commas, each with its own `mailto:`. - **Format:** Aggregate XML reports, usually zipped, about once a day (`ri=86400`). - **Other domains:** An address on another domain needs an authorization `TXT` record on that domain. - **Reading them:** See [reading DMARC reports](https://verifiedinbox.ai/docs/dmarc#reading-dmarc-reports-and-troubleshooting-quarantine-issues). *Related:* [DMARC](https://verifiedinbox.ai/docs/glossary.md#dmarc), [Safe DMARC rollout](https://verifiedinbox.ai/docs/glossary.md#dmarc-rollout), [DMARC policy (watch, filter, block)](https://verifiedinbox.ai/docs/glossary.md#dmarc-policy) ### SPF **The public guest list of servers and services allowed to send email for your domain.** SPF (Sender Policy Framework) is a public list on your domain naming the mail servers and services (such as Google Workspace, Microsoft 365, or Postmark) authorized to send email on your behalf. Receiving servers check the sending server's IP address against your list. **Technical detail:** - **DNS host:** `@` (domain apex) or sending subdomain (`TXT` record only, `RFC 7208`). - **Example:** `v=spf1 include:_spf.google.com ~all` - **Identity checked:** Evaluates the `Return-Path` (`RFC5321.MailFrom`) and `HELO`/`EHLO` domain against the connecting client IP. - **Limits:** At most **1** `v=spf1` record per domain, **10** DNS-querying mechanisms (`include`, `a`, `mx`, `ptr`, `exists`, `redirect`), and **2** void lookups (`0` answers or `NXDOMAIN`). Exceeding any limit returns `PermError`. *Related:* [DMARC](https://verifiedinbox.ai/docs/glossary.md#dmarc), [DKIM](https://verifiedinbox.ai/docs/glossary.md#dkim), [Alignment](https://verifiedinbox.ai/docs/glossary.md#alignment) ### DKIM **A tamper-proof digital signature that proves an email came from your domain and wasn't altered.** DKIM (DomainKeys Identified Mail) stamps an invisible cryptographic signature onto every email you send. Inboxes verify that signature using a public key in your domain's DNS to confirm the email really came from you and that neither the headers nor the body were changed in transit. Unlike SPF, a DKIM signature survives when a recipient forwards your email. **Technical detail:** - **DNS host:** `._domainkey.` (`TXT` record, or `CNAME` to a provider-managed `TXT` record; `RFC 6376`). - **DNS value:** See [DKIM TXT record tags](https://verifiedinbox.ai/docs/dkim#dkim-dns-txt-record-tags) (2048-bit RSA recommended; an empty `p=` revokes the key). - **Message header:** `DKIM-Signature` with `d=` (signing domain), `s=` (selector), `a=` (`rsa-sha256`), `h=` (signed headers), `bh=` (body hash), and `b=` (signature). *Related:* [DKIM selector](https://verifiedinbox.ai/docs/glossary.md#dkim-selector), [DMARC](https://verifiedinbox.ai/docs/glossary.md#dmarc), [Alignment](https://verifiedinbox.ai/docs/glossary.md#alignment) ### DKIM selector **The short label that identifies a specific DKIM key on your domain.** Because your domain can use several email services at the same time, each service publishes its DKIM public key under its own short name called a selector (for example “google”, “s1”, or “selector1”). The selector tells receiving inboxes which key record to look up in your DNS. **Technical detail:** - **Header tag:** `s=` inside the `DKIM-Signature` header. - **DNS lookup path:** `._domainkey.` - **Discovery:** DNS zones cannot be enumerated automatically; inspect `DKIM-Signature` via **Show original** in a test email or check your provider's settings. *Related:* [DKIM](https://verifiedinbox.ai/docs/glossary.md#dkim) ### Alignment **Matching the authenticated SPF or DKIM domain to the 'From' address people see.** Passing SPF or DKIM is only half the check: the domain that passed the check must also match the domain shown in the visible 'From:' address. That match is called alignment. It stops an attacker from passing SPF or DKIM with their own domain while putting your company's domain in the 'From:' line. **Technical detail:** - **SPF alignment (`aspf`):** Compares the `Return-Path` (`RFC5321.MailFrom`) domain to the `From:` (`RFC5322.From`) domain. - **DKIM alignment (`adkim`):** Compares the `DKIM-Signature` `d=` domain to the `From:` (`RFC5322.From`) domain. - **Modes:** Relaxed (`r`, default) allows any subdomain of the same organizational domain; strict (`s`) requires an exact character-for-character domain match. *Related:* [DMARC](https://verifiedinbox.ai/docs/glossary.md#dmarc), [SPF](https://verifiedinbox.ai/docs/glossary.md#spf), [DKIM](https://verifiedinbox.ai/docs/glossary.md#dkim) ### DNS **The internet's public address book for your domain.** DNS (Domain Name System) is where your domain's public settings live: where your website is hosted, which service receives your email, and the security settings needed for your inbox logo. You manage DNS where your domain is hosted (such as Cloudflare, GoDaddy, Namecheap, or Route 53). **Technical detail:** - **Resolution:** Records are published on your zone's authoritative nameservers (`NS`) and cached by recursive resolvers up to each record's `TTL`. *Related:* [DNS record](https://verifiedinbox.ai/docs/glossary.md#dns-record), [Authoritative nameserver](https://verifiedinbox.ai/docs/glossary.md#authoritative-dns) ### DNS record **One setting in your domain's public address book.** A single entry in your domain's DNS settings, consisting of a type (such as TXT, CNAME, or MX), a host name, and a value. We show you the exact values to copy and paste. **Technical detail:** - **Fields:** Owner name (`Host`), record type (`TXT`, `CNAME`, `MX`, `A`, `AAAA`), data (`Value` / `RDATA`), and `TTL`. *Related:* [DNS](https://verifiedinbox.ai/docs/glossary.md#dns), [TXT record](https://verifiedinbox.ai/docs/glossary.md#txt-record) ### TXT record **A DNS record that holds structured text for verification and email security.** A type of domain setting that stores a line of text. SPF, DKIM, DMARC, BIMI, and domain ownership checks all use TXT records. **Technical detail:** - **Type:** DNS `TXT` resource record (`RR` type `16`). - **Length rule:** Individual character-strings inside a `TXT` record are limited to `255` octets; longer values (such as 2048-bit DKIM keys) are split into multiple quoted strings and concatenated without spaces by DNS resolvers. *Related:* [DNS record](https://verifiedinbox.ai/docs/glossary.md#dns-record), [SPF](https://verifiedinbox.ai/docs/glossary.md#spf), [DKIM](https://verifiedinbox.ai/docs/glossary.md#dkim), [DMARC](https://verifiedinbox.ai/docs/glossary.md#dmarc), [BIMI](https://verifiedinbox.ai/docs/glossary.md#bimi) ### MX record **Tells the internet which mail servers receive incoming email for your domain.** An MX (Mail Exchanger) record directs incoming email for your domain to your mailbox provider, such as Google Workspace, Microsoft 365, or Fastmail. It controls receiving email, while SPF and DKIM control sending email. **Technical detail:** - **Format:** ` ` (for example `1 smtp.google.com`). - **Priority:** Lower numbers are tried first, with higher numbers acting as fallback servers. *Related:* [DNS record](https://verifiedinbox.ai/docs/glossary.md#dns-record), [Mailbox provider](https://verifiedinbox.ai/docs/glossary.md#mailbox-provider) ### Mailbox provider **The service that hosts your company's email inboxes.** The service where your team reads and sends daily email, such as Google Workspace, Microsoft 365, Fastmail, Zoho Mail, or Proton Mail. *Related:* [MX record](https://verifiedinbox.ai/docs/glossary.md#mx-record) ### Authoritative nameserver **The primary server that holds the official copy of your domain's DNS records.** The server that stores the original, source-of-truth copy of your domain settings. VerifiedInbox checks your authoritative nameservers directly, so we see your DNS changes within seconds instead of waiting hours for cached copies around the internet to refresh. **Technical detail:** - **Discovery:** Resolved from the `NS` records of your domain's zone. - **Verification:** Queried directly with recursion disabled (`RD=0`) and zero caching, with automatic fallback to DNS-over-HTTPS (`DoH`). *Related:* [DNS](https://verifiedinbox.ai/docs/glossary.md#dns), [TTL](https://verifiedinbox.ai/docs/glossary.md#ttl) ### TTL **How long other servers cache a domain setting before checking for updates.** Short for “time to live”. It tells servers across the internet how long (in seconds) to remember a DNS record before asking your domain's nameservers for a fresh copy. **Technical detail:** - **Unit:** Seconds (for example `3600` = 1 hour). - **Effect:** Controls how long recursive DNS resolvers cache a record before re-querying your authoritative nameservers. *Related:* [Authoritative nameserver](https://verifiedinbox.ai/docs/glossary.md#authoritative-dns) ### Domain **Your company's internet name, like yourcompany.com.** The name after the @ in your email address. To show your company logo in the inbox, you need a domain you own and control; shared email domains like gmail.com or outlook.com belong to the provider. *Related:* [Subdomain](https://verifiedinbox.ai/docs/glossary.md#subdomain), [Organizational domain](https://verifiedinbox.ai/docs/glossary.md#organizational-domain) ### Subdomain **A prefix name under your main domain, like news.yourcompany.com.** A name that lives under your main domain, such as news.yourcompany.com or send.yourcompany.com. Many companies use separate subdomains for newsletters or automated receipts so each stream has its own SPF and DKIM settings. **Technical detail:** - **SPF:** Each subdomain has its own independent `10`-lookup SPF budget. - **DMARC & BIMI:** Subdomains inherit the organizational domain's `_dmarc` (`sp=`) and `default._bimi` records unless overridden on the subdomain. *Related:* [Domain](https://verifiedinbox.ai/docs/glossary.md#domain), [Organizational domain](https://verifiedinbox.ai/docs/glossary.md#organizational-domain) ### Organizational domain **Your main registered root domain, without any subdomain prefixes.** Your primary registered domain (such as yourcompany.com, as opposed to mail.yourcompany.com). DMARC and BIMI settings published on your organizational domain automatically cover your subdomains unless a subdomain publishes its own record. **Technical detail:** - **Definition:** The registrable domain computed from the Public Suffix List (public suffix + one label). - **Fallback:** DMARC and BIMI evaluators query the subdomain first and fall back to the organizational domain if no subdomain record exists. *Related:* [Domain](https://verifiedinbox.ai/docs/glossary.md#domain), [Subdomain](https://verifiedinbox.ai/docs/glossary.md#subdomain) ### SVG Tiny P/S **The strict, secure vector logo format required by BIMI.** Inboxes require your logo in a specialized, locked-down vector format called SVG Tiny Portable/Secure, with a square canvas and a solid background. You don't have to build it by hand: give us your normal logo and we convert and validate it automatically. **Technical detail:** - **Root attributes:** `version="1.2"` and `baseProfile="tiny-ps"` on ``, with no `x=` or `y=` attributes. - **Dimensions:** Square `viewBox` with explicit pixel dimensions (`width="96" height="96"` or larger for Gmail; never `100%`) and file size `<= 32 KB`. - **Required elements:** `` with your organization name and a solid background path. - **Disallowed:** Scripts, animations, external references/fonts, and embedded raster images (``). *Related:* [BIMI](https://verifiedinbox.ai/docs/glossary.md#bimi), [Logo & certificate hosting](https://verifiedinbox.ai/docs/glossary.md#logo-hosting) ### Registered trademark **Legal registration of your logo with a government trademark office.** An official government registration proving your logo belongs to your company. Gmail's blue checkmark (a VMC) requires an active registered trademark for your logo. We search the US Patent and Trademark Office (USPTO) for you automatically. **Technical detail:** - **Eligible marks:** Active registered design marks or combined word-and-design marks in an intellectual property office recognized by the AuthIndicators Working Group (first release: `USPTO`). *Related:* [USPTO](https://verifiedinbox.ai/docs/glossary.md#uspto), [VMC (Verified Mark Certificate)](https://verifiedinbox.ai/docs/glossary.md#vmc), [Trademark filing service](https://verifiedinbox.ai/docs/glossary.md#trademark-service) ### USPTO **The United States Patent and Trademark Office.** The US government agency that registers trademarks. We look up your company's logo there automatically. *Related:* [Registered trademark](https://verifiedinbox.ai/docs/glossary.md#trademark) ### Trademark filing service **Help registering your logo as a trademark.** If your logo isn't registered yet, we can help you file it. Email trademark@verifiedinbox.ai. Registration usually takes several months; meanwhile you may qualify for a CMC. *Related:* [Registered trademark](https://verifiedinbox.ai/docs/glossary.md#trademark), [CMC (Common Mark Certificate)](https://verifiedinbox.ai/docs/glossary.md#cmc) ### Prior use (12 months) **Proof your logo has been displayed publicly for at least a year.** To qualify for a CMC (logo without a trademark), you need to show that your logo has been used publicly for at least 12 months, such as on your company website. We check public web archives automatically to find that evidence for you. **Technical detail:** - **Evidence:** Historical captures (such as Internet Archive Wayback Machine snapshots) showing the mark on the applicant's domain at least `12` months prior to validation. *Related:* [CMC (Common Mark Certificate)](https://verifiedinbox.ai/docs/glossary.md#cmc) ### Certificate issuer **The accredited authority that verifies your company and issues your mark certificate.** An independent Mark Verifying Authority (we partner with DigiCert) that verifies your legal business, your authority to apply, and your logo before issuing your VMC or CMC. Their annual fee is passed through without markup. **Technical detail:** - **Role:** Certificate Authority (`CA`) / Mark Verifying Authority (`MVA`) accredited under the AuthIndicators Working Group Mark Certificate Requirements. *Related:* [VMC (Verified Mark Certificate)](https://verifiedinbox.ai/docs/glossary.md#vmc), [CMC (Common Mark Certificate)](https://verifiedinbox.ai/docs/glossary.md#cmc), [Issuer fee](https://verifiedinbox.ai/docs/glossary.md#issuer-fee) ### Issuer fee **The certificate issuer's own yearly validation and issuance charge.** The certificate issuer charges its own yearly fee for verifying your business and issuing the certificate. We collect it at checkout without markup and show it as a separate line item. If issuance is rejected, you receive a full refund. *Related:* [Certificate issuer](https://verifiedinbox.ai/docs/glossary.md#certificate-issuer) ### Business verification **Confirming your company is legally registered and you are authorized to represent it.** We verify that your company is an active registered legal entity and that the applicant is authorized to act on its behalf, before submitting your order to the certificate issuer. **Technical detail:** - **Checks:** Official company registry lookup (`OpenCorporates`) plus applicant authority verification, followed by the issuer's organization and verified-contact validation. *Related:* [Certificate issuer](https://verifiedinbox.ai/docs/glossary.md#certificate-issuer) ### Domain ownership check **Proving you control the domain by adding a one-time verification code.** You add a unique TXT record we give you to your domain's DNS settings. It proves you control the domain so nobody else can order, reissue, or revoke a certificate for it. **Technical detail:** - **DNS host:** `_verifiedinbox.` (`TXT` record). - **Value:** Your unique verification code, see [domain ownership record](https://verifiedinbox.ai/docs/bimi#1-domain-ownership). *Related:* [DNS record](https://verifiedinbox.ai/docs/glossary.md#dns-record) ### Logo & certificate hosting **We keep your SVG logo and PEM certificate chain online for inboxes to fetch.** Supporting inboxes download your logo and certificate from the internet over HTTPS. We host both on fast, reliable servers and swap in your renewed certificate automatically each year, so you never have to manage files or update your DNS. **Technical detail:** - **Logo URL (`l=`):** `logo.svg` at [your hosted address](https://verifiedinbox.ai/docs/hosting#where-your-files-live), served as `image/svg+xml`. - **Certificate URL (`a=`):** `certificate.pem` at [your hosted address](https://verifiedinbox.ai/docs/hosting#where-your-files-live), served as `application/x-pem-file`. *Related:* [BIMI](https://verifiedinbox.ai/docs/glossary.md#bimi), [PEM file](https://verifiedinbox.ai/docs/glossary.md#pem) ### Renewal **Mark certificates last up to 398 days; we renew them automatically every year.** BIMI mark certificates are valid for about 13 months (398 days). We track the expiration date, send reminders ahead of time, renew your certificate automatically 45 days before expiry, and update the hosted PEM file in place so your logo never disappears. **Technical detail:** - **Maximum validity:** `398` days under the AuthIndicators Mark Certificate Requirements (unaffected by the 47-day Web PKI TLS schedule). - **Schedule:** Automated renewal starts `45` days before `not_after`, with reminders at `60`, `30`, `14`, `7`, and `1` days. *Related:* [VMC (Verified Mark Certificate)](https://verifiedinbox.ai/docs/glossary.md#vmc), [CMC (Common Mark Certificate)](https://verifiedinbox.ai/docs/glossary.md#cmc) ### Reissue **Replacing your active certificate after a logo or company name change.** Because your exact logo is embedded inside your certificate, changing your logo requires a fresh certificate. A reissue requests an updated certificate within your current paid year. *Related:* [Renewal](https://verifiedinbox.ai/docs/glossary.md#renewal), [Revocation](https://verifiedinbox.ai/docs/glossary.md#revocation) ### Revocation **Permanently cancelling an issued certificate.** Cancels a certificate permanently so inboxes stop trusting it and stop showing your logo. Only revoke a certificate if your domain is no longer used or the certificate details are no longer valid. *Related:* [Reissue](https://verifiedinbox.ai/docs/glossary.md#reissue) ### PEM file **The standard certificate chain file hosted for your BIMI record.** A text file containing your issued mark certificate (with your SVG logo embedded inside it) plus the issuer's intermediate and root certificates. We assemble and host the complete PEM chain for you. **Technical detail:** - **Format:** Base64-encoded DER certificates between `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` markers. - **Chain order:** Leaf entity certificate (containing the embedded SVG logotype), followed by intermediate CA certificate(s) and root CA certificate. *Related:* [Logo & certificate hosting](https://verifiedinbox.ai/docs/glossary.md#logo-hosting), [BIMI](https://verifiedinbox.ai/docs/glossary.md#bimi) ### Test mode (sandbox) **A safe practice environment where nothing is charged or issued.** Test mode lets you rehearse every step, including checkout and certificate issuance, without paying or ordering a real certificate. Switch it on from your dashboard; test data is kept completely separate from live domains. **Technical detail:** - **Environment:** `test` (selected via dashboard toggle, `vi_test_…` API key, or `VI-Environment: test` header). - **Integrations:** Uses Stripe test mode, the simulated certificate issuer, and simulated DNS for `example.com` domains. *Related:* [API key](https://verifiedinbox.ai/docs/glossary.md#api-key) ### API key **A secret token for software and AI agents to use your account.** A secret credential that lets your scripts, backend servers, or AI assistant work with your organization's domains. Each key is bound to either live or test mode and carries specific permissions (scopes). **Technical detail:** - **Header:** `Authorization: Bearer vi_live_…` (live) or `Authorization: Bearer vi_test_…` (test). - **Scopes:** `applications:read`, `applications:write`, `billing:write`, `certificates:write`, `support:write`. *Related:* [MCP](https://verifiedinbox.ai/docs/glossary.md#mcp), [Test mode (sandbox)](https://verifiedinbox.ai/docs/glossary.md#sandbox) ### MCP **The standard that lets AI assistants use VerifiedInbox tools for you.** MCP (Model Context Protocol) lets AI assistants like Claude, ChatGPT, or Cursor check your domain, inspect your email security, prepare your logo, and move your verification forward step by step. **Technical detail:** - **Endpoint:** See [MCP server endpoint](https://verifiedinbox.ai/docs/mcp#endpoint) (Streamable HTTP, JSON-RPC 2.0). - **Auth:** Works anonymously with a `guest_token`, or with WorkOS OAuth / an API key for account tools. *Related:* [API key](https://verifiedinbox.ai/docs/glossary.md#api-key) ### Looks identical **How we confirm the inbox-ready logo looks the same as your original.** We draw your original file and the inbox-ready copy at the same size on a white background and compare them pixel by pixel. If less than half a percent of the logo changed visibly, they look identical. Up to 3% counts as small differences; more than that is flagged so you can check carefully. **Technical detail:** - **Method:** Both SVGs are rasterized at `256×256` px on a white canvas; a pixel counts as changed when any color channel differs by more than `32/255`. - **Thresholds:** `identical < 0.5%`, `minor < 3%`, otherwise `significant`. *Related:* [SVG Tiny P/S](https://verifiedinbox.ai/docs/glossary.md#svg-tiny-ps) ### Matches your logo **How we decide a registered trademark shows your logo.** For each registered mark we find, we download its image from the US trademark office and compare it with your logo. An AI image check does the comparison, with a visual fingerprint as a backup. A score of 80% or more counts as a match; lower scores are shown as similar so you or our team can confirm. **Technical detail:** - **Method:** Compares the USPTO mark image against a `256` px rendering of your logo using Claude vision (with perceptual-hash similarity as a fallback). - **Thresholds:** `similarity >= 0.80` -> `match_found`; `0.60–0.80` -> `similar` (manual review). *Related:* [Registered trademark](https://verifiedinbox.ai/docs/glossary.md#trademark), [USPTO](https://verifiedinbox.ai/docs/glossary.md#uspto) --- # Frequently asked questions > Short answers to the questions we hear most. ## The basics ### What does VerifiedInbox do? We get your company's logo shown next to your emails in Gmail and other inboxes that support [BIMI](https://verifiedinbox.ai/docs/glossary.md#bimi), and, with a registered trademark, Gmail's blue checkmark. We handle the checks, the logo file, the certificate, the setup, hosting, monitoring and renewals. ### Do I need to be technical? No. Every step is explained clearly, and the settings you add to your domain are copy-and-paste. You can share the DNS step with whoever manages your domain. ### Which inboxes show my logo? Gmail shows your logo, plus the blue checkmark with a [VMC](https://verifiedinbox.ai/docs/glossary.md#vmc). Other inboxes that support BIMI, including Apple Mail and Yahoo Mail, can show your logo too; each provider sets its own rules. ### Is the checkmark guaranteed? No one can guarantee it: Gmail decides what to show. We make sure everything on your side is correct and stays that way. ### Can I use my @gmail.com (or Outlook, iCloud…) address? No. Shared email domains belong to the provider, and the logo shown for them is the provider's. You need your own domain, like yourcompany.com. ## Eligibility and trademarks ### What do I need for the blue checkmark? Your own domain, a registered trademark for your logo (US Patent and Trademark Office in our first release) and email security settings at enforcement. See [VMC vs CMC](https://verifiedinbox.ai/docs/vmc-vs-cmc.md). ### My logo isn't trademarked. What can I do? If your logo has been public for at least 12 months, you can get the logo without the checkmark ([CMC](https://verifiedinbox.ai/docs/glossary.md#cmc)). You can also file a trademark through our service: email [trademark@verifiedinbox.ai](mailto:trademark@verifiedinbox.ai). ### We're a new company with a new logo. You may not be eligible yet. We'll show you what's missing, tell you when you're likely to qualify and email you when you do. See [Eligibility](https://verifiedinbox.ai/docs/eligibility.md). ## Setup ### How long does it take? The free checks take about a minute. After payment, the certificate issuer's checks usually take from a few days to a few weeks. ### Will this break my email? Not if you follow our [safe DMARC rollout](https://verifiedinbox.ai/docs/dmarc.md#configuring-a-safe-rollout-to-quarantine-and-reject). We tighten protection in steps, only when your real email is passing. ### Can you change our DNS for us? Not yet. You copy and paste a few records; we check them within seconds. We never ask for your DNS login. ### Do I have to upload files every year? No. We host your logo and certificate and swap in the renewed certificate automatically. ## Pricing and billing ### How much does it cost? Checks are free. The logo-only plan is $899 and the logo + checkmark plan is $1,499, per domain per year, plus the certificate issuer's fee (about $750–$1,750 per year), shown separately before you pay. See [Pricing](https://verifiedinbox.ai/pricing.md). ### What if I'm rejected? If the certificate issuer rejects your application, you get a full refund. ### Can I try it first? Yes. All checks are free and need no account. Signed-in users can use [test mode](https://verifiedinbox.ai/docs/test-mode.md) to go through every step without paying. ## AI assistants ### Can my AI assistant do this for me? Yes. Paste *"Add a verified VMC mark to my email domain using verifiedinbox.ai/get-started.md"* into Claude, ChatGPT or Cursor. It can run checks, prepare your logo and track progress. You step in to pay, add DNS records and approve your logo. See [MCP](https://verifiedinbox.ai/docs/mcp.md). ### Is it safe to let an assistant do this? Assistants can't pay, can't change your DNS and need your confirmation for anything irreversible. See [Security](https://verifiedinbox.ai/docs/security.md). --- # Support > Everything can be done self-service; humans are here if you want one. ## Talk to a human Use the [contact form](https://verifiedinbox.ai/support?kind=contact_human) or email [support@verifiedinbox.ai](mailto:support@verifiedinbox.ai). A person replies by email, usually within one business day. Include your domain, and your application id if you have one (it's in the wizard's address bar: `/verify//…`). ## Get help with a step [Ask for help](https://verifiedinbox.ai/support?kind=support) and tell us which step you're on and what you see. Screenshots help. ## Feedback and feature requests We read everything: [send feedback](https://verifiedinbox.ai/support?kind=feedback) or [request a feature](https://verifiedinbox.ai/support?kind=feature_request). ## Partnerships Certificate issuers, registrars, email platforms and agencies: see [Partners](https://verifiedinbox.ai/partners.md) or [get in touch](https://verifiedinbox.ai/support?kind=partnership). ## Trademark filing Email [trademark@verifiedinbox.ai](mailto:trademark@verifiedinbox.ai). ## From your AI assistant Your assistant can contact us for you with the `contact_support` and `send_feedback` MCP tools. --- # VerifiedInbox API Reference (v1) > Everything you can do on verifiedinbox.ai, you can do through the REST API. Free checks and anonymous drafts work without an account. ## Base URL & rules - **Base URL:** `https://api.verifiedinbox.ai/v1` - **Content type:** `application/json` (UTF-8). - **Timestamps & money:** RFC 3339 UTC strings (`2026-10-31T12:00:00Z`); money in integer USD cents (`149900` = $1,499.00). - **Versioning:** `v1` only receives backward-compatible additions. - **OpenAPI 3.1 contract:** `/openapi.json` ## Security & authentication | Access level | Credential | Header | |---|---|---| | Public | None required | — | | Guest | Guest token (from `startApplication`) | `VI-Guest-Token: vi_guest_…` or `Authorization: Bearer vi_guest_…` | | Account / Paid | API key or OAuth token | `Authorization: Bearer vi_live_…` (or `vi_test_…`) | ## Operations ### Checks - [Check Domain](https://verifiedinbox.ai/docs/reference/api/v1/check-domain.md) (`POST /v1/checks/domain`): Quick domain eligibility check - [Check Email Auth](https://verifiedinbox.ai/docs/reference/api/v1/check-email-auth.md) (`POST /v1/checks/email-auth`): Full SPF/DKIM/DMARC/BIMI inspection - [List Blocked Providers](https://verifiedinbox.ai/docs/reference/api/v1/list-blocked-providers.md) (`GET /v1/providers/blocked`): Shared mailbox domains that can't be used ### Applications - [Create Application](https://verifiedinbox.ai/docs/reference/api/v1/create-application.md) (`POST /v1/applications`): Start (or resume) a verification - [Get Application](https://verifiedinbox.ai/docs/reference/api/v1/get-application.md) (`GET /v1/applications/{application_id}`): Get a verification - [List Applications](https://verifiedinbox.ai/docs/reference/api/v1/list-applications.md) (`GET /v1/applications`): List my verifications - [Get Next Steps](https://verifiedinbox.ai/docs/reference/api/v1/get-next-steps.md) (`GET /v1/applications/{application_id}/next-steps`): Current step and next actions - [Complete Step](https://verifiedinbox.ai/docs/reference/api/v1/complete-step.md) (`POST /v1/applications/{application_id}/steps/{step}/complete`): Complete a wizard step and move on - [Update Application](https://verifiedinbox.ai/docs/reference/api/v1/update-application.md) (`PATCH /v1/applications/{application_id}`): Choose product / contact email - [Refresh Application](https://verifiedinbox.ai/docs/reference/api/v1/refresh-application.md) (`POST /v1/applications/{application_id}/refresh`): Re-run checks now - [Create Resume Link](https://verifiedinbox.ai/docs/reference/api/v1/create-resume-link.md) (`POST /v1/applications/{application_id}/resume-links`): One-time link to continue in a browser - [List Events](https://verifiedinbox.ai/docs/reference/api/v1/list-events.md) (`GET /v1/applications/{application_id}/events`): Timeline of the verification - [Archive Application](https://verifiedinbox.ai/docs/reference/api/v1/archive-application.md) (`POST /v1/applications/{application_id}/archive`): Archive (unpaid only) - [Claim Application](https://verifiedinbox.ai/docs/reference/api/v1/claim-application.md) (`POST /v1/applications/{application_id}/claim`): Move a guest draft into the account ### Email security - [Get Email Security](https://verifiedinbox.ai/docs/reference/api/v1/get-email-security.md) (`GET /v1/applications/{application_id}/email-security`): Latest email-security report + DMARC rollout plan - [Recheck Email Security](https://verifiedinbox.ai/docs/reference/api/v1/recheck-email-security.md) (`POST /v1/applications/{application_id}/email-security/check`): Re-check email security now - [Set Email Usage](https://verifiedinbox.ai/docs/reference/api/v1/set-email-usage.md) (`PUT /v1/applications/{application_id}/email-usage`): How email is used on the domain - [Verify DKIM Selectors](https://verifiedinbox.ai/docs/reference/api/v1/verify-dkim-selectors.md) (`POST /v1/applications/{application_id}/dkim-selectors`): Verify DKIM selectors you supply - [Set DMARC Reports](https://verifiedinbox.ai/docs/reference/api/v1/set-dmarc-reports.md) (`PUT /v1/applications/{application_id}/dmarc-reports`): Where daily DMARC reports go ### Logo - [Submit Logo](https://verifiedinbox.ai/docs/reference/api/v1/submit-logo.md) (`PUT /v1/applications/{application_id}/logo`): Submit the logo - [Get Logo](https://verifiedinbox.ai/docs/reference/api/v1/get-logo.md) (`GET /v1/applications/{application_id}/logo`): Logo status, analysis and changes - [List Logo Candidates](https://verifiedinbox.ai/docs/reference/api/v1/list-logo-candidates.md) (`GET /v1/applications/{application_id}/logo-candidates`): Logos found on the website - [Get Logo Converted](https://verifiedinbox.ai/docs/reference/api/v1/get-logo-converted.md) (`GET /v1/applications/{application_id}/logo/converted.svg`): Inbox-ready SVG Tiny P/S file - [Get Logo Original](https://verifiedinbox.ai/docs/reference/api/v1/get-logo-original.md) (`GET /v1/applications/{application_id}/logo/original.svg`): Original logo file - [Get Logo Preview](https://verifiedinbox.ai/docs/reference/api/v1/get-logo-preview.md) (`GET /v1/applications/{application_id}/logo/preview.png`): Rendered PNG preview - [Approve Logo](https://verifiedinbox.ai/docs/reference/api/v1/approve-logo.md) (`POST /v1/applications/{application_id}/logo/approve`): Approve the reviewed inbox-ready logo ### Trademark - [Get Trademark](https://verifiedinbox.ai/docs/reference/api/v1/get-trademark.md) (`GET /v1/applications/{application_id}/trademark`): Trademark result - [Search Trademark](https://verifiedinbox.ai/docs/reference/api/v1/search-trademark.md) (`POST /v1/applications/{application_id}/trademark/search`): (Re)start the USPTO search - [Claim Trademark](https://verifiedinbox.ai/docs/reference/api/v1/claim-trademark.md) (`POST /v1/applications/{application_id}/trademark/claim`): Verify a specific USPTO number ### Eligibility - [Get Eligibility](https://verifiedinbox.ai/docs/reference/api/v1/get-eligibility.md) (`GET /v1/applications/{application_id}/eligibility`): VMC / CMC / not-yet-eligible with evidence - [Get Estimate](https://verifiedinbox.ai/docs/reference/api/v1/get-estimate.md) (`GET /v1/applications/{application_id}/estimate`): Itemized yearly price - [Watch Eligibility](https://verifiedinbox.ai/docs/reference/api/v1/watch-eligibility.md) (`POST /v1/applications/{application_id}/eligibility/watch`): Email me when this domain qualifies ### Guest sessions - [Create Guest Session](https://verifiedinbox.ai/docs/reference/api/v1/create-guest-session.md) (`POST /v1/guest-sessions`): Create a guest credential - [Resume](https://verifiedinbox.ai/docs/reference/api/v1/resume.md) (`POST /v1/resume`): Exchange a one-time resume token - [Revoke Guest Session](https://verifiedinbox.ai/docs/reference/api/v1/revoke-guest-session.md) (`DELETE /v1/guest-sessions/current`): Revoke the current guest session ### Support - [Create Support Request](https://verifiedinbox.ai/docs/reference/api/v1/create-support-request.md) (`POST /v1/support`): Contact a human, feedback, feature request, partnership ### Glossary - [List Glossary](https://verifiedinbox.ai/docs/reference/api/v1/list-glossary.md) (`GET /v1/glossary`): All glossary terms - [Get Glossary Term](https://verifiedinbox.ai/docs/reference/api/v1/get-glossary-term.md) (`GET /v1/glossary/{term}`): One glossary term ### Hosting - [Get Hosted Asset](https://verifiedinbox.ai/docs/reference/api/v1/get-hosted-asset.md) (`GET /bimi/{domain}/{file}`): Hosted BIMI logo / certificate - [Get Hosted Test Asset](https://verifiedinbox.ai/docs/reference/api/v1/get-hosted-test-asset.md) (`GET /bimi/test/{domain}/{file}`): Hosted BIMI assets (test mode) ### Checkout - [Create Checkout](https://verifiedinbox.ai/docs/reference/api/v1/create-checkout.md) (`POST /v1/applications/{application_id}/checkout`): Create a Stripe checkout link ### Business - [Get Business](https://verifiedinbox.ai/docs/reference/api/v1/get-business.md) (`GET /v1/applications/{application_id}/business`): Company verification status - [Submit Business](https://verifiedinbox.ai/docs/reference/api/v1/submit-business.md) (`PUT /v1/applications/{application_id}/business`): Submit legal company details ### DNS - [Verify DNS](https://verifiedinbox.ai/docs/reference/api/v1/verify-dns.md) (`POST /v1/applications/{application_id}/dns/verify`): Re-check DNS now - [Get DNS Records](https://verifiedinbox.ai/docs/reference/api/v1/get-dns-records.md) (`GET /v1/applications/{application_id}/dns`): Every DNS record to add, with live status ### Certificate - [Get Certificate](https://verifiedinbox.ai/docs/reference/api/v1/get-certificate.md) (`GET /v1/applications/{application_id}/certificate`): Certificate status, prerequisites, final check and history - [Run Final Check](https://verifiedinbox.ai/docs/reference/api/v1/run-final-check.md) (`POST /v1/applications/{application_id}/certificate/check`): Re-check everything the issuer will verify - [Order Certificate](https://verifiedinbox.ai/docs/reference/api/v1/order-certificate.md) (`POST /v1/applications/{application_id}/certificate/order`): Submit the order (normally automatic) - [Renew Certificate](https://verifiedinbox.ai/docs/reference/api/v1/renew-certificate.md) (`POST /v1/applications/{application_id}/certificate/renew`): Renew early - [Reissue Certificate](https://verifiedinbox.ai/docs/reference/api/v1/reissue-certificate.md) (`POST /v1/applications/{application_id}/certificate/reissue`): Reissue the certificate - [Revoke Certificate](https://verifiedinbox.ai/docs/reference/api/v1/revoke-certificate.md) (`POST /v1/applications/{application_id}/certificate/revoke`): Revoke permanently ### Test messages - [Create Test Message](https://verifiedinbox.ai/docs/reference/api/v1/create-test-message.md) (`POST /v1/applications/{application_id}/test-messages`): Get an address to send a real test email to - [Get Test Message](https://verifiedinbox.ai/docs/reference/api/v1/get-test-message.md) (`GET /v1/applications/{application_id}/test-messages/{test_message_id}`): Authentication results of the test email ### Go live - [Get Go Live](https://verifiedinbox.ai/docs/reference/api/v1/get-go-live.md) (`GET /v1/applications/{application_id}/go-live`): BIMI publication, test email, display, monitoring, renewal - [Confirm Inbox Display](https://verifiedinbox.ai/docs/reference/api/v1/confirm-inbox-display.md) (`POST /v1/applications/{application_id}/inbox-display`): Record that the logo was seen in a mailbox ### Account - [Get Me](https://verifiedinbox.ai/docs/reference/api/v1/get-me.md) (`GET /v1/me`): Who am I (works anonymously too) - [List Organizations](https://verifiedinbox.ai/docs/reference/api/v1/list-organizations.md) (`GET /v1/organizations`): My organizations - [Create Organization](https://verifiedinbox.ai/docs/reference/api/v1/create-organization.md) (`POST /v1/organizations`): Create an organization - [Get Organization](https://verifiedinbox.ai/docs/reference/api/v1/get-organization.md) (`GET /v1/organizations/{organization_id}`): One organization - [List Members](https://verifiedinbox.ai/docs/reference/api/v1/list-members.md) (`GET /v1/organizations/{organization_id}/members`): Organization members - [List API Keys](https://verifiedinbox.ai/docs/reference/api/v1/list-api-keys.md) (`GET /v1/api-keys`): API keys - [Create API Key](https://verifiedinbox.ai/docs/reference/api/v1/create-api-key.md) (`POST /v1/api-keys`): Create an API key (web session) - [Revoke API Key](https://verifiedinbox.ai/docs/reference/api/v1/revoke-api-key.md) (`DELETE /v1/api-keys/{api_key_id}`): Revoke an API key - [List Orders](https://verifiedinbox.ai/docs/reference/api/v1/list-orders.md) (`GET /v1/orders`): Orders and payments - [Get Order](https://verifiedinbox.ai/docs/reference/api/v1/get-order.md) (`GET /v1/orders/{order_id}`): One order - [Create Billing Portal](https://verifiedinbox.ai/docs/reference/api/v1/create-billing-portal.md) (`POST /v1/billing/portal`): Stripe billing portal link - [List Notifications](https://verifiedinbox.ai/docs/reference/api/v1/list-notifications.md) (`GET /v1/notifications`): Notification log - [Rename Organization](https://verifiedinbox.ai/docs/reference/api/v1/rename-organization.md) (`PATCH /v1/organizations/{organization_id}`): Rename an organization - [Set Environment](https://verifiedinbox.ai/docs/reference/api/v1/set-environment.md) (`POST /v1/me/environment`): Switch live/test mode (web session) - [Set Organization](https://verifiedinbox.ai/docs/reference/api/v1/set-organization.md) (`POST /v1/me/organization`): Switch organization (web session) ### Test mode - [Complete Mock Checkout](https://verifiedinbox.ai/docs/reference/api/v1/complete-mock-checkout.md) (`POST /v1/dev/mock-checkout/{order_id}/complete`): Simulate payment of a mock checkout ### Meta - [Get Open API](https://verifiedinbox.ai/docs/reference/api/v1/get-open-api.md) (`GET /openapi.json`): This OpenAPI document - [Get Health](https://verifiedinbox.ai/docs/reference/api/v1/get-health.md) (`GET /health`): Liveness check - [Get Meta](https://verifiedinbox.ai/docs/reference/api/v1/get-meta.md) (`GET /v1/meta`): Pricing, steps and live/mocked integrations - [Get Ready](https://verifiedinbox.ai/docs/reference/api/v1/get-ready.md) (`GET /ready`): Readiness check ### MCP - [MCP](https://verifiedinbox.ai/docs/reference/api/v1/mcp.md) (`POST /mcp`): MCP endpoint (Streamable HTTP, JSON responses) - [Get Protected Resource Metadata](https://verifiedinbox.ai/docs/reference/api/v1/get-protected-resource-metadata.md) (`GET /.well-known/oauth-protected-resource`): OAuth protected-resource metadata (RFC 9728) - [Get Protected Resource Metadata MCP](https://verifiedinbox.ai/docs/reference/api/v1/get-protected-resource-metadata-mcp.md) (`GET /.well-known/oauth-protected-resource/mcp`): OAuth protected-resource metadata for /mcp --- # Get Protected Resource Metadata > OAuth protected-resource metadata (RFC 9728) **Endpoint:** `GET /.well-known/oauth-protected-resource` **Access:** Public (no credentials needed) ## cURL example ```bash curl "https://api.verifiedinbox.ai/.well-known/oauth-protected-resource" ``` ## Responses ### 200 OK Metadata. ```json { "authorization_servers": [ "string" ], "bearer_methods_supported": [ "string" ], "resource": "string", "resource_documentation": "string", "resource_name": "string", "scopes_supported": [ "string" ] } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Protected Resource Metadata MCP > OAuth protected-resource metadata for /mcp **Endpoint:** `GET /.well-known/oauth-protected-resource/mcp` **Access:** Public (no credentials needed) ## cURL example ```bash curl "https://api.verifiedinbox.ai/.well-known/oauth-protected-resource/mcp" ``` ## Responses ### 200 OK Metadata. ```json { "authorization_servers": [ "string" ], "bearer_methods_supported": [ "string" ], "resource": "string", "resource_documentation": "string", "resource_name": "string", "scopes_supported": [ "string" ] } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Hosted Test Asset > Hosted BIMI assets (test mode) **Endpoint:** `GET /bimi/test/{domain}/{file}` **Access:** Public (no credentials needed) ## cURL example ```bash curl "https://api.verifiedinbox.ai/bimi/test/acme.com/logo.svg" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `domain` (path, required) | `string` | Customer domain, e.g. `acme.com`. | | `file` (path, required) | `string` | `logo.svg` or `certificate.pem` (`vmc.pem`/`cmc.pem` are accepted aliases). | ## Responses ### 200 OK Test-mode logo or certificate. ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Hosted Asset > Hosted BIMI logo / certificate **Endpoint:** `GET /bimi/{domain}/{file}` **Access:** Public (no credentials needed) Also served host-based at https://bimi.getverifiedinbox.com/{domain}/{file}. 404 until active. ## cURL example ```bash curl "https://api.verifiedinbox.ai/bimi/acme.com/logo.svg" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `domain` (path, required) | `string` | Customer domain, e.g. `acme.com`. | | `file` (path, required) | `string` | `logo.svg` or `certificate.pem` (`vmc.pem`/`cmc.pem` are accepted aliases). | ## Responses ### 200 OK `logo.svg` (SVG) or `certificate.pem` (`application/x-pem-file`). ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Health > Liveness check **Endpoint:** `GET /health` **Access:** Public (no credentials needed) ## cURL example ```bash curl "https://api.verifiedinbox.ai/health" ``` ## Responses ### 200 OK Alive. ```json { "status": "ok", "version": "string" } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # MCP > MCP endpoint (Streamable HTTP, JSON responses) **Endpoint:** `POST /mcp` **Access:** Public (no credentials needed) JSON-RPC 2.0. Anonymous tools work without credentials; account tools take an API key or WorkOS OAuth token (401 + `WWW-Authenticate` triggers OAuth). Guide: https://getverifiedinbox.com/docs/mcp ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/mcp" \ -H "Content-Type: application/json" \ -d '{}' ``` ## Request body ```json {} ``` ## Responses ### 200 OK JSON-RPC response(s). ```json {} ``` ### 202 OK Notifications accepted. ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Open API > This OpenAPI document **Endpoint:** `GET /openapi.json` **Access:** Public (no credentials needed) The public OpenAPI 3.1 contract (also at https://getverifiedinbox.com/openapi.json). ## cURL example ```bash curl "https://api.verifiedinbox.ai/openapi.json" ``` ## Responses ### 200 OK OpenAPI document. ```json {} ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Ready > Readiness check **Endpoint:** `GET /ready` **Access:** Public (no credentials needed) Database reachable and migrations applied. 503 when not ready. ## cURL example ```bash curl "https://api.verifiedinbox.ai/ready" ``` ## Responses ### 200 OK Ready. ```json { "migrations": 1, "status": "ready" } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # List API Keys > API keys **Endpoint:** `GET /v1/api-keys` **Access:** Account required ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/api-keys" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Keys (no tokens). ```json { "data": [ { "created_at": "2026-10-31T12:00:00Z", "environment": "live", "id": "0192a5e0-1234-7000-8000-000000000001", "last_used_at": "2026-10-31T12:00:00Z", "name": "Acme Inc.", "prefix": "string", "revoked_at": "2026-10-31T12:00:00Z", "scopes": [] } ], "next_cursor": "string" } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Create API Key > Create an API key (web session) **Endpoint:** `POST /v1/api-keys` **Access:** Account required Bound to the current environment. The token is shown once. ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/api-keys" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Acme Inc.", "scopes": [ "applications:read" ] }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Request body | Field | Type | Description | |---|---|---| | `name` *(required)* | `string` | — | | `scopes` | `string[]` | Default: all scopes. | ```json { "name": "Acme Inc.", "scopes": [ "applications:read" ] } ``` ## Responses ### 201 OK Created. ```json { "api_key": { "created_at": "2026-10-31T12:00:00Z", "environment": "live", "id": "0192a5e0-1234-7000-8000-000000000001", "last_used_at": "2026-10-31T12:00:00Z", "name": "Acme Inc.", "prefix": "string", "revoked_at": "2026-10-31T12:00:00Z", "scopes": [] }, "token": "string" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Revoke API Key > Revoke an API key **Endpoint:** `DELETE /v1/api-keys/{api_key_id}` **Access:** Account required ## cURL example ```bash curl -X DELETE "https://api.verifiedinbox.ai/v1/api-keys/0192a5e0-1234-7000-8000-000000000004" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `api_key_id` (path, required) | `string (uuid)` | API key id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 204 OK Revoked. ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # List Applications > List my verifications **Endpoint:** `GET /v1/applications` **Access:** Free (guest token or account) **MCP tool:** `list_applications`, `get_account` Guest drafts for a guest credential, or the current organization + environment for accounts. ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/applications" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Applications. ```json { "data": [ { "background": [], "brand_name": "string", "contact_email": "string", "created_at": "2026-10-31T12:00:00Z", "current_step": "domain", "domain": "acme.com", "domain_display": "string", "domain_status": "unknown", "email_usage": "none", "environment": "live", "id": "0192a5e0-1234-7000-8000-000000000001", "next_actions": [], "organizational_domain": "string", "outcomes": { "bimi": "not_published", "business": "not_started", "certificate": "not_ordered", "dmarc_rollout": "missing", "eligibility": "unknown", "inbox_display": "not_observed", "logo": "missing", "ownership": "pending", "payment": "unpaid", "trademark": "not_started" }, "owner": { "type": "guest" }, "product": "vmc", "product_selected": "vmc", "recommended_product": "vmc", "steps": [], "updated_at": "2026-10-31T12:00:00Z", "version": 1, "web_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" } ], "next_cursor": "string" } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Create Application > Start (or resume) a verification **Endpoint:** `POST /v1/applications` **Access:** Public (no credentials needed) **MCP tool:** `start_application` Idempotent per owner + domain (200 existing, 201 new). Creates a guest session when no credential is sent and returns `guest_token` once. Blocked providers → 422 `blocked_provider`. example.com domains always run in test mode with simulated DNS. ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications" \ -H "Content-Type: application/json" \ -d '{ "domain": "acme.com", "environment": "live" }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `Idempotency-Key` (header) | `string` | Unique key per intended action (≤ 255 chars). Same key + same body replays the first response; same key + different body → 409. | ## Request body | Field | Type | Description | |---|---|---| | `domain` *(required)* | `string` | A domain, URL or email address. | | `environment` | `string` | Guests only; example.com domains always use `test`. | ```json { "domain": "acme.com", "environment": "live" } ``` ## Responses ### 200 OK Existing draft returned. ```json { "background": [ { "kind": "domain_scan", "message": "Everything checks out.", "next_run_at": "2026-10-31T12:00:00Z", "state": "string", "status": "pending", "updated_at": "2026-10-31T12:00:00Z" } ], "brand_name": "string", "contact_email": "string", "created": true, "created_at": "2026-10-31T12:00:00Z", "current_step": "domain", "domain": "acme.com", "domain_display": "string", "domain_status": "unknown", "email_usage": "none", "environment": "live", "guest_token": "string", "guest_token_note": "string", "id": "0192a5e0-1234-7000-8000-000000000001", "next_actions": [ { "api": { "method": "string", "path": "string" }, "description": "string", "kind": "string", "mcp_tool": "string", "requires_auth": true, "requires_payment": true, "step": "domain", "title": "string", "url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" } ], "organizational_domain": "string", "outcomes": { "bimi": "not_published", "business": "not_started", "certificate": "not_ordered", "dmarc_rollout": "missing", "eligibility": "unknown", "inbox_display": "not_observed", "logo": "missing", "ownership": "pending", "payment": "unpaid", "trademark": "not_started" }, "owner": { "type": "guest" }, "product": "vmc", "product_selected": "vmc", "recommended_product": "vmc", "steps": [ { "completed_at": "2026-10-31T12:00:00Z", "paid": true, "phase": "check", "requirements": [], "slug": "domain", "status": "locked", "summary": "string", "title": "string" } ], "updated_at": "2026-10-31T12:00:00Z", "version": 1, "web_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" } ``` ### 201 OK Created. ```json { "background": [ { "kind": "domain_scan", "message": "Everything checks out.", "next_run_at": "2026-10-31T12:00:00Z", "state": "string", "status": "pending", "updated_at": "2026-10-31T12:00:00Z" } ], "brand_name": "string", "contact_email": "string", "created": true, "created_at": "2026-10-31T12:00:00Z", "current_step": "domain", "domain": "acme.com", "domain_display": "string", "domain_status": "unknown", "email_usage": "none", "environment": "live", "guest_token": "string", "guest_token_note": "string", "id": "0192a5e0-1234-7000-8000-000000000001", "next_actions": [ { "api": { "method": "string", "path": "string" }, "description": "string", "kind": "string", "mcp_tool": "string", "requires_auth": true, "requires_payment": true, "step": "domain", "title": "string", "url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" } ], "organizational_domain": "string", "outcomes": { "bimi": "not_published", "business": "not_started", "certificate": "not_ordered", "dmarc_rollout": "missing", "eligibility": "unknown", "inbox_display": "not_observed", "logo": "missing", "ownership": "pending", "payment": "unpaid", "trademark": "not_started" }, "owner": { "type": "guest" }, "product": "vmc", "product_selected": "vmc", "recommended_product": "vmc", "steps": [ { "completed_at": "2026-10-31T12:00:00Z", "paid": true, "phase": "check", "requirements": [], "slug": "domain", "status": "locked", "summary": "string", "title": "string" } ], "updated_at": "2026-10-31T12:00:00Z", "version": 1, "web_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 409 (version_conflict) `conflict` / `version_conflict` (current state in `details.application`) / Idempotency-Key reuse. ```json { "error": { "code": "version_conflict", "message": "This application was updated elsewhere. Refresh and try again." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Application > Get a verification **Endpoint:** `GET /v1/applications/{application_id}` **Access:** Free (guest token or account) **MCP tool:** `get_application` Outcomes, the 11 wizard steps, background work and next actions. ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK The application. ```json { "background": [ { "kind": "domain_scan", "message": "Everything checks out.", "next_run_at": "2026-10-31T12:00:00Z", "state": "string", "status": "pending", "updated_at": "2026-10-31T12:00:00Z" } ], "brand_name": "string", "contact_email": "string", "created_at": "2026-10-31T12:00:00Z", "current_step": "domain", "domain": "acme.com", "domain_display": "string", "domain_status": "unknown", "email_usage": "none", "environment": "live", "id": "0192a5e0-1234-7000-8000-000000000001", "next_actions": [ { "api": { "method": "string", "path": "string" }, "description": "string", "kind": "string", "mcp_tool": "string", "requires_auth": true, "requires_payment": true, "step": "domain", "title": "string", "url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" } ], "organizational_domain": "string", "outcomes": { "bimi": "not_published", "business": "not_started", "certificate": "not_ordered", "dmarc_rollout": "missing", "eligibility": "unknown", "inbox_display": "not_observed", "logo": "missing", "ownership": "pending", "payment": "unpaid", "trademark": "not_started" }, "owner": { "type": "guest" }, "product": "vmc", "product_selected": "vmc", "recommended_product": "vmc", "steps": [ { "completed_at": "2026-10-31T12:00:00Z", "paid": true, "phase": "check", "requirements": [], "slug": "domain", "status": "locked", "summary": "string", "title": "string" } ], "updated_at": "2026-10-31T12:00:00Z", "version": 1, "web_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Update Application > Choose product / contact email **Endpoint:** `PATCH /v1/applications/{application_id}` **Access:** Free (guest token or account) ## cURL example ```bash curl -X PATCH "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "contact_email": "string", "product": "vmc" }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | | `If-Match` (header) | `string` | The application `version` you last saw, e.g. `"7"`. Stale → 409 `version_conflict` with the current application. | ## Request body | Field | Type | Description | |---|---|---| | `contact_email` | `string` | — | | `product` | `string` | — | ```json { "contact_email": "string", "product": "vmc" } ``` ## Responses ### 200 OK Updated. ```json { "background": [ { "kind": "domain_scan", "message": "Everything checks out.", "next_run_at": "2026-10-31T12:00:00Z", "state": "string", "status": "pending", "updated_at": "2026-10-31T12:00:00Z" } ], "brand_name": "string", "contact_email": "string", "created_at": "2026-10-31T12:00:00Z", "current_step": "domain", "domain": "acme.com", "domain_display": "string", "domain_status": "unknown", "email_usage": "none", "environment": "live", "id": "0192a5e0-1234-7000-8000-000000000001", "next_actions": [ { "api": { "method": "string", "path": "string" }, "description": "string", "kind": "string", "mcp_tool": "string", "requires_auth": true, "requires_payment": true, "step": "domain", "title": "string", "url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" } ], "organizational_domain": "string", "outcomes": { "bimi": "not_published", "business": "not_started", "certificate": "not_ordered", "dmarc_rollout": "missing", "eligibility": "unknown", "inbox_display": "not_observed", "logo": "missing", "ownership": "pending", "payment": "unpaid", "trademark": "not_started" }, "owner": { "type": "guest" }, "product": "vmc", "product_selected": "vmc", "recommended_product": "vmc", "steps": [ { "completed_at": "2026-10-31T12:00:00Z", "paid": true, "phase": "check", "requirements": [], "slug": "domain", "status": "locked", "summary": "string", "title": "string" } ], "updated_at": "2026-10-31T12:00:00Z", "version": 1, "web_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 409 (version_conflict) `conflict` / `version_conflict` (current state in `details.application`) / Idempotency-Key reuse. ```json { "error": { "code": "version_conflict", "message": "This application was updated elsewhere. Refresh and try again." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Archive Application > Archive (unpaid only) **Endpoint:** `POST /v1/applications/{application_id}/archive` **Access:** Free (guest token or account) ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/archive" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 204 OK Archived. ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Business > Company verification status **Endpoint:** `GET /v1/applications/{application_id}/business` **Access:** Free (guest token or account) **MCP tool:** `get_business_status` ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/business" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Status. ```json { "applicant_authority": "email_domain_match", "id": "0192a5e0-1234-7000-8000-000000000001", "input": { "address": { "city": "string", "country": "string", "line1": "string", "line2": "string", "postal_code": "string", "region": "string" }, "applicant": { "email": "alex@acme.com", "name": "Acme Inc.", "phone": "string", "title": "string" }, "authority_attested": true, "jurisdiction": "us_de", "legal_name": "Acme Inc.", "registration_number": "string" }, "match": { "active": true, "company_number": "string", "company_type": "string", "incorporation_date": "2026-10-31", "jurisdiction_code": "string", "name": "Acme Inc.", "registered_address": "string", "source_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "status": "string" }, "message": "Everything checks out.", "provider": "string", "status": "not_started", "updated_at": "2026-10-31T12:00:00Z" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Submit Business > Submit legal company details **Endpoint:** `PUT /v1/applications/{application_id}/business` **Access:** Paid plan required (scope `applications:write`) **MCP tool:** `submit_business_details` Applicant authority: email at the domain, or `authority_attested: true` (the issuer verifies too). ## cURL example ```bash curl -X PUT "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/business" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "address": { "city": "string", "line1": "string", "postal_code": "string", "region": "string" }, "applicant": { "email": "alex@acme.com", "name": "Acme Inc." }, "jurisdiction": "us_de", "legal_name": "Acme Inc." }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Request body | Field | Type | Description | |---|---|---| | `address` *(required)* | `object` | Registered business address. | | `applicant` *(required)* | `object` | The person applying on the company's behalf; the issuer may contact them. An email at the domain proves authority. | | `authority_attested` | `boolean` | Required when the applicant's email isn't at the domain. | | `jurisdiction` *(required)* | `string` | US state code, e.g. `us_de`. | | `legal_name` *(required)* | `string` | The company's exact legal name, as registered. | | `registration_number` | `string` | State file / registration number, if known (speeds up verification). | ```json { "address": { "city": "string", "line1": "string", "postal_code": "string", "region": "string" }, "applicant": { "email": "alex@acme.com", "name": "Acme Inc." }, "jurisdiction": "us_de", "legal_name": "Acme Inc." } ``` ## Responses ### 200 OK Verification started. ```json { "applicant_authority": "email_domain_match", "id": "0192a5e0-1234-7000-8000-000000000001", "input": { "address": { "city": "string", "country": "string", "line1": "string", "line2": "string", "postal_code": "string", "region": "string" }, "applicant": { "email": "alex@acme.com", "name": "Acme Inc.", "phone": "string", "title": "string" }, "authority_attested": true, "jurisdiction": "us_de", "legal_name": "Acme Inc.", "registration_number": "string" }, "match": { "active": true, "company_number": "string", "company_type": "string", "incorporation_date": "2026-10-31", "jurisdiction_code": "string", "name": "Acme Inc.", "registered_address": "string", "source_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "status": "string" }, "message": "Everything checks out.", "provider": "string", "status": "not_started", "updated_at": "2026-10-31T12:00:00Z" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 402 (payment_required) `payment_required` — this step needs a paid plan. `next_action.kind = checkout`. ```json { "error": { "code": "payment_required", "message": "This step requires an active paid plan for this domain.", "next_action": { "kind": "checkout", "title": "Continue to checkout" } } } ``` ### 403 (insufficient_scope) `forbidden` / `insufficient_scope` — the credential lacks permission. ```json { "error": { "code": "insufficient_scope", "message": "This API key does not have the required scope for this action." } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Certificate > Certificate status, prerequisites, final check and history **Endpoint:** `GET /v1/applications/{application_id}/certificate` **Access:** Free (guest token or account) **MCP tool:** `get_certificate` ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/certificate" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Certificate. ```json { "certificate": { "created_at": "2026-10-31T12:00:00Z", "customer_actions": [], "days_remaining": 1, "id": "0192a5e0-1234-7000-8000-000000000001", "issued_at": "2026-10-31T12:00:00Z", "issuer": "string", "issuer_mode": "string", "issuer_order_id": "0192a5e0-1234-7000-8000-000000000001", "kind": "vmc", "not_after": "2026-10-31T12:00:00Z", "not_before": "2026-10-31T12:00:00Z", "pem_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "prerequisites": [], "reissue_of": "0192a5e0-1234-7000-8000-000000000001", "rejected_at": "2026-10-31T12:00:00Z", "rejection_reason": "string", "renewal_of": "0192a5e0-1234-7000-8000-000000000001", "renewal_starts_on": "2026-10-31", "revocation_reason": "string", "revoked_at": "2026-10-31T12:00:00Z", "serial": "string", "status": "waiting_prerequisites", "status_detail": "string", "submitted_at": "2026-10-31T12:00:00Z", "test_mode": true, "timeline": [], "updated_at": "2026-10-31T12:00:00Z" }, "final_check": { "created_at": "2026-10-31T12:00:00Z", "id": "0192a5e0-1234-7000-8000-000000000001", "ok": true, "results": [], "trigger": "submission" }, "history": [ { "created_at": "2026-10-31T12:00:00Z", "customer_actions": [], "days_remaining": 1, "id": "0192a5e0-1234-7000-8000-000000000001", "issued_at": "2026-10-31T12:00:00Z", "issuer": "string", "issuer_mode": "string", "issuer_order_id": "0192a5e0-1234-7000-8000-000000000001", "kind": "vmc", "not_after": "2026-10-31T12:00:00Z", "not_before": "2026-10-31T12:00:00Z", "pem_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "prerequisites": [], "reissue_of": "0192a5e0-1234-7000-8000-000000000001", "rejected_at": "2026-10-31T12:00:00Z", "rejection_reason": "string", "renewal_of": "0192a5e0-1234-7000-8000-000000000001", "renewal_starts_on": "2026-10-31", "revocation_reason": "string", "revoked_at": "2026-10-31T12:00:00Z", "serial": "string", "status": "waiting_prerequisites", "status_detail": "string", "submitted_at": "2026-10-31T12:00:00Z", "test_mode": true, "timeline": [], "updated_at": "2026-10-31T12:00:00Z" } ], "prerequisites": [ { "id": "paid", "label": "string", "met": true, "step": "domain" } ], "prerequisites_met": true } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Run Final Check > Re-check everything the issuer will verify **Endpoint:** `POST /v1/applications/{application_id}/certificate/check` **Access:** Paid plan required **MCP tool:** `run_final_check` Runs automatically right before the order is submitted (it's never submitted while anything fails) and daily while the issuer reviews it. Failures mark the affected steps `attention`. ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/certificate/check" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Certificate state with the new `final_check`. ```json { "certificate": { "created_at": "2026-10-31T12:00:00Z", "customer_actions": [], "days_remaining": 1, "id": "0192a5e0-1234-7000-8000-000000000001", "issued_at": "2026-10-31T12:00:00Z", "issuer": "string", "issuer_mode": "string", "issuer_order_id": "0192a5e0-1234-7000-8000-000000000001", "kind": "vmc", "not_after": "2026-10-31T12:00:00Z", "not_before": "2026-10-31T12:00:00Z", "pem_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "prerequisites": [], "reissue_of": "0192a5e0-1234-7000-8000-000000000001", "rejected_at": "2026-10-31T12:00:00Z", "rejection_reason": "string", "renewal_of": "0192a5e0-1234-7000-8000-000000000001", "renewal_starts_on": "2026-10-31", "revocation_reason": "string", "revoked_at": "2026-10-31T12:00:00Z", "serial": "string", "status": "waiting_prerequisites", "status_detail": "string", "submitted_at": "2026-10-31T12:00:00Z", "test_mode": true, "timeline": [], "updated_at": "2026-10-31T12:00:00Z" }, "final_check": { "created_at": "2026-10-31T12:00:00Z", "id": "0192a5e0-1234-7000-8000-000000000001", "ok": true, "results": [], "trigger": "submission" }, "history": [ { "created_at": "2026-10-31T12:00:00Z", "customer_actions": [], "days_remaining": 1, "id": "0192a5e0-1234-7000-8000-000000000001", "issued_at": "2026-10-31T12:00:00Z", "issuer": "string", "issuer_mode": "string", "issuer_order_id": "0192a5e0-1234-7000-8000-000000000001", "kind": "vmc", "not_after": "2026-10-31T12:00:00Z", "not_before": "2026-10-31T12:00:00Z", "pem_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "prerequisites": [], "reissue_of": "0192a5e0-1234-7000-8000-000000000001", "rejected_at": "2026-10-31T12:00:00Z", "rejection_reason": "string", "renewal_of": "0192a5e0-1234-7000-8000-000000000001", "renewal_starts_on": "2026-10-31", "revocation_reason": "string", "revoked_at": "2026-10-31T12:00:00Z", "serial": "string", "status": "waiting_prerequisites", "status_detail": "string", "submitted_at": "2026-10-31T12:00:00Z", "test_mode": true, "timeline": [], "updated_at": "2026-10-31T12:00:00Z" } ], "prerequisites": [ { "id": "paid", "label": "string", "met": true, "step": "domain" } ], "prerequisites_met": true } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 402 (payment_required) `payment_required` — this step needs a paid plan. `next_action.kind = checkout`. ```json { "error": { "code": "payment_required", "message": "This step requires an active paid plan for this domain.", "next_action": { "kind": "checkout", "title": "Continue to checkout" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Order Certificate > Submit the order (normally automatic) **Endpoint:** `POST /v1/applications/{application_id}/certificate/order` **Access:** Paid plan required (scope `certificates:write`) **MCP tool:** `order_certificate` ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/certificate/order" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" \ -H "Idempotency-Key: idem-$(date +%s)" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `Idempotency-Key` (header, required) | `string` | Unique key per intended action (≤ 255 chars). Same key + same body replays the first response; same key + different body → 409. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Ordered. ```json { "certificate": { "created_at": "2026-10-31T12:00:00Z", "customer_actions": [], "days_remaining": 1, "id": "0192a5e0-1234-7000-8000-000000000001", "issued_at": "2026-10-31T12:00:00Z", "issuer": "string", "issuer_mode": "string", "issuer_order_id": "0192a5e0-1234-7000-8000-000000000001", "kind": "vmc", "not_after": "2026-10-31T12:00:00Z", "not_before": "2026-10-31T12:00:00Z", "pem_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "prerequisites": [], "reissue_of": "0192a5e0-1234-7000-8000-000000000001", "rejected_at": "2026-10-31T12:00:00Z", "rejection_reason": "string", "renewal_of": "0192a5e0-1234-7000-8000-000000000001", "renewal_starts_on": "2026-10-31", "revocation_reason": "string", "revoked_at": "2026-10-31T12:00:00Z", "serial": "string", "status": "waiting_prerequisites", "status_detail": "string", "submitted_at": "2026-10-31T12:00:00Z", "test_mode": true, "timeline": [], "updated_at": "2026-10-31T12:00:00Z" }, "final_check": { "created_at": "2026-10-31T12:00:00Z", "id": "0192a5e0-1234-7000-8000-000000000001", "ok": true, "results": [], "trigger": "submission" }, "history": [ { "created_at": "2026-10-31T12:00:00Z", "customer_actions": [], "days_remaining": 1, "id": "0192a5e0-1234-7000-8000-000000000001", "issued_at": "2026-10-31T12:00:00Z", "issuer": "string", "issuer_mode": "string", "issuer_order_id": "0192a5e0-1234-7000-8000-000000000001", "kind": "vmc", "not_after": "2026-10-31T12:00:00Z", "not_before": "2026-10-31T12:00:00Z", "pem_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "prerequisites": [], "reissue_of": "0192a5e0-1234-7000-8000-000000000001", "rejected_at": "2026-10-31T12:00:00Z", "rejection_reason": "string", "renewal_of": "0192a5e0-1234-7000-8000-000000000001", "renewal_starts_on": "2026-10-31", "revocation_reason": "string", "revoked_at": "2026-10-31T12:00:00Z", "serial": "string", "status": "waiting_prerequisites", "status_detail": "string", "submitted_at": "2026-10-31T12:00:00Z", "test_mode": true, "timeline": [], "updated_at": "2026-10-31T12:00:00Z" } ], "prerequisites": [ { "id": "paid", "label": "string", "met": true, "step": "domain" } ], "prerequisites_met": true } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 402 (payment_required) `payment_required` — this step needs a paid plan. `next_action.kind = checkout`. ```json { "error": { "code": "payment_required", "message": "This step requires an active paid plan for this domain.", "next_action": { "kind": "checkout", "title": "Continue to checkout" } } } ``` ### 403 (insufficient_scope) `forbidden` / `insufficient_scope` — the credential lacks permission. ```json { "error": { "code": "insufficient_scope", "message": "This API key does not have the required scope for this action." } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 409 (version_conflict) `conflict` / `version_conflict` (current state in `details.application`) / Idempotency-Key reuse. ```json { "error": { "code": "version_conflict", "message": "This application was updated elsewhere. Refresh and try again." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Reissue Certificate > Reissue the certificate **Endpoint:** `POST /v1/applications/{application_id}/certificate/reissue` **Access:** Paid plan required (scope `certificates:write`) **MCP tool:** `reissue_certificate` Re-checks domain ownership first. ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/certificate/reissue" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" \ -H "Idempotency-Key: idem-$(date +%s)" \ -H "Content-Type: application/json" \ -d '{ "reason": "Updated company logo" }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `Idempotency-Key` (header, required) | `string` | Unique key per intended action (≤ 255 chars). Same key + same body replays the first response; same key + different body → 409. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Request body | Field | Type | Description | |---|---|---| | `reason` | `string` | Why, in a sentence (shown to the issuer). | ```json { "reason": "Updated company logo" } ``` ## Responses ### 200 OK Reissue started. ```json { "certificate": { "created_at": "2026-10-31T12:00:00Z", "customer_actions": [], "days_remaining": 1, "id": "0192a5e0-1234-7000-8000-000000000001", "issued_at": "2026-10-31T12:00:00Z", "issuer": "string", "issuer_mode": "string", "issuer_order_id": "0192a5e0-1234-7000-8000-000000000001", "kind": "vmc", "not_after": "2026-10-31T12:00:00Z", "not_before": "2026-10-31T12:00:00Z", "pem_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "prerequisites": [], "reissue_of": "0192a5e0-1234-7000-8000-000000000001", "rejected_at": "2026-10-31T12:00:00Z", "rejection_reason": "string", "renewal_of": "0192a5e0-1234-7000-8000-000000000001", "renewal_starts_on": "2026-10-31", "revocation_reason": "string", "revoked_at": "2026-10-31T12:00:00Z", "serial": "string", "status": "waiting_prerequisites", "status_detail": "string", "submitted_at": "2026-10-31T12:00:00Z", "test_mode": true, "timeline": [], "updated_at": "2026-10-31T12:00:00Z" }, "final_check": { "created_at": "2026-10-31T12:00:00Z", "id": "0192a5e0-1234-7000-8000-000000000001", "ok": true, "results": [], "trigger": "submission" }, "history": [ { "created_at": "2026-10-31T12:00:00Z", "customer_actions": [], "days_remaining": 1, "id": "0192a5e0-1234-7000-8000-000000000001", "issued_at": "2026-10-31T12:00:00Z", "issuer": "string", "issuer_mode": "string", "issuer_order_id": "0192a5e0-1234-7000-8000-000000000001", "kind": "vmc", "not_after": "2026-10-31T12:00:00Z", "not_before": "2026-10-31T12:00:00Z", "pem_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "prerequisites": [], "reissue_of": "0192a5e0-1234-7000-8000-000000000001", "rejected_at": "2026-10-31T12:00:00Z", "rejection_reason": "string", "renewal_of": "0192a5e0-1234-7000-8000-000000000001", "renewal_starts_on": "2026-10-31", "revocation_reason": "string", "revoked_at": "2026-10-31T12:00:00Z", "serial": "string", "status": "waiting_prerequisites", "status_detail": "string", "submitted_at": "2026-10-31T12:00:00Z", "test_mode": true, "timeline": [], "updated_at": "2026-10-31T12:00:00Z" } ], "prerequisites": [ { "id": "paid", "label": "string", "met": true, "step": "domain" } ], "prerequisites_met": true } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 402 (payment_required) `payment_required` — this step needs a paid plan. `next_action.kind = checkout`. ```json { "error": { "code": "payment_required", "message": "This step requires an active paid plan for this domain.", "next_action": { "kind": "checkout", "title": "Continue to checkout" } } } ``` ### 403 (insufficient_scope) `forbidden` / `insufficient_scope` — the credential lacks permission. ```json { "error": { "code": "insufficient_scope", "message": "This API key does not have the required scope for this action." } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 409 (version_conflict) `conflict` / `version_conflict` (current state in `details.application`) / Idempotency-Key reuse. ```json { "error": { "code": "version_conflict", "message": "This application was updated elsewhere. Refresh and try again." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Renew Certificate > Renew early **Endpoint:** `POST /v1/applications/{application_id}/certificate/renew` **Access:** Paid plan required (scope `certificates:write`) **MCP tool:** `renew_certificate` ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/certificate/renew" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" \ -H "Idempotency-Key: idem-$(date +%s)" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `Idempotency-Key` (header, required) | `string` | Unique key per intended action (≤ 255 chars). Same key + same body replays the first response; same key + different body → 409. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Renewal started. ```json { "certificate": { "created_at": "2026-10-31T12:00:00Z", "customer_actions": [], "days_remaining": 1, "id": "0192a5e0-1234-7000-8000-000000000001", "issued_at": "2026-10-31T12:00:00Z", "issuer": "string", "issuer_mode": "string", "issuer_order_id": "0192a5e0-1234-7000-8000-000000000001", "kind": "vmc", "not_after": "2026-10-31T12:00:00Z", "not_before": "2026-10-31T12:00:00Z", "pem_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "prerequisites": [], "reissue_of": "0192a5e0-1234-7000-8000-000000000001", "rejected_at": "2026-10-31T12:00:00Z", "rejection_reason": "string", "renewal_of": "0192a5e0-1234-7000-8000-000000000001", "renewal_starts_on": "2026-10-31", "revocation_reason": "string", "revoked_at": "2026-10-31T12:00:00Z", "serial": "string", "status": "waiting_prerequisites", "status_detail": "string", "submitted_at": "2026-10-31T12:00:00Z", "test_mode": true, "timeline": [], "updated_at": "2026-10-31T12:00:00Z" }, "final_check": { "created_at": "2026-10-31T12:00:00Z", "id": "0192a5e0-1234-7000-8000-000000000001", "ok": true, "results": [], "trigger": "submission" }, "history": [ { "created_at": "2026-10-31T12:00:00Z", "customer_actions": [], "days_remaining": 1, "id": "0192a5e0-1234-7000-8000-000000000001", "issued_at": "2026-10-31T12:00:00Z", "issuer": "string", "issuer_mode": "string", "issuer_order_id": "0192a5e0-1234-7000-8000-000000000001", "kind": "vmc", "not_after": "2026-10-31T12:00:00Z", "not_before": "2026-10-31T12:00:00Z", "pem_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "prerequisites": [], "reissue_of": "0192a5e0-1234-7000-8000-000000000001", "rejected_at": "2026-10-31T12:00:00Z", "rejection_reason": "string", "renewal_of": "0192a5e0-1234-7000-8000-000000000001", "renewal_starts_on": "2026-10-31", "revocation_reason": "string", "revoked_at": "2026-10-31T12:00:00Z", "serial": "string", "status": "waiting_prerequisites", "status_detail": "string", "submitted_at": "2026-10-31T12:00:00Z", "test_mode": true, "timeline": [], "updated_at": "2026-10-31T12:00:00Z" } ], "prerequisites": [ { "id": "paid", "label": "string", "met": true, "step": "domain" } ], "prerequisites_met": true } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 402 (payment_required) `payment_required` — this step needs a paid plan. `next_action.kind = checkout`. ```json { "error": { "code": "payment_required", "message": "This step requires an active paid plan for this domain.", "next_action": { "kind": "checkout", "title": "Continue to checkout" } } } ``` ### 403 (insufficient_scope) `forbidden` / `insufficient_scope` — the credential lacks permission. ```json { "error": { "code": "insufficient_scope", "message": "This API key does not have the required scope for this action." } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 409 (version_conflict) `conflict` / `version_conflict` (current state in `details.application`) / Idempotency-Key reuse. ```json { "error": { "code": "version_conflict", "message": "This application was updated elsewhere. Refresh and try again." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Revoke Certificate > Revoke permanently **Endpoint:** `POST /v1/applications/{application_id}/certificate/revoke` **Access:** Paid plan required (scope `certificates:write`) **MCP tool:** `revoke_certificate` Requires `confirm: "REVOKE "`; re-checks domain ownership. The logo stops showing in inboxes. ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/certificate/revoke" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" \ -H "Idempotency-Key: idem-$(date +%s)" \ -H "Content-Type: application/json" \ -d '{ "confirm": "REVOKE acme.com", "reason": "Updated company logo" }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `Idempotency-Key` (header, required) | `string` | Unique key per intended action (≤ 255 chars). Same key + same body replays the first response; same key + different body → 409. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Request body | Field | Type | Description | |---|---|---| | `confirm` *(required)* | `string` | Exactly `REVOKE `. | | `reason` *(required)* | `string` | Why the certificate is revoked (shown to the issuer). | ```json { "confirm": "REVOKE acme.com", "reason": "Updated company logo" } ``` ## Responses ### 200 OK Revoked. ```json { "certificate": { "created_at": "2026-10-31T12:00:00Z", "customer_actions": [], "days_remaining": 1, "id": "0192a5e0-1234-7000-8000-000000000001", "issued_at": "2026-10-31T12:00:00Z", "issuer": "string", "issuer_mode": "string", "issuer_order_id": "0192a5e0-1234-7000-8000-000000000001", "kind": "vmc", "not_after": "2026-10-31T12:00:00Z", "not_before": "2026-10-31T12:00:00Z", "pem_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "prerequisites": [], "reissue_of": "0192a5e0-1234-7000-8000-000000000001", "rejected_at": "2026-10-31T12:00:00Z", "rejection_reason": "string", "renewal_of": "0192a5e0-1234-7000-8000-000000000001", "renewal_starts_on": "2026-10-31", "revocation_reason": "string", "revoked_at": "2026-10-31T12:00:00Z", "serial": "string", "status": "waiting_prerequisites", "status_detail": "string", "submitted_at": "2026-10-31T12:00:00Z", "test_mode": true, "timeline": [], "updated_at": "2026-10-31T12:00:00Z" }, "final_check": { "created_at": "2026-10-31T12:00:00Z", "id": "0192a5e0-1234-7000-8000-000000000001", "ok": true, "results": [], "trigger": "submission" }, "history": [ { "created_at": "2026-10-31T12:00:00Z", "customer_actions": [], "days_remaining": 1, "id": "0192a5e0-1234-7000-8000-000000000001", "issued_at": "2026-10-31T12:00:00Z", "issuer": "string", "issuer_mode": "string", "issuer_order_id": "0192a5e0-1234-7000-8000-000000000001", "kind": "vmc", "not_after": "2026-10-31T12:00:00Z", "not_before": "2026-10-31T12:00:00Z", "pem_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "prerequisites": [], "reissue_of": "0192a5e0-1234-7000-8000-000000000001", "rejected_at": "2026-10-31T12:00:00Z", "rejection_reason": "string", "renewal_of": "0192a5e0-1234-7000-8000-000000000001", "renewal_starts_on": "2026-10-31", "revocation_reason": "string", "revoked_at": "2026-10-31T12:00:00Z", "serial": "string", "status": "waiting_prerequisites", "status_detail": "string", "submitted_at": "2026-10-31T12:00:00Z", "test_mode": true, "timeline": [], "updated_at": "2026-10-31T12:00:00Z" } ], "prerequisites": [ { "id": "paid", "label": "string", "met": true, "step": "domain" } ], "prerequisites_met": true } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 402 (payment_required) `payment_required` — this step needs a paid plan. `next_action.kind = checkout`. ```json { "error": { "code": "payment_required", "message": "This step requires an active paid plan for this domain.", "next_action": { "kind": "checkout", "title": "Continue to checkout" } } } ``` ### 403 (insufficient_scope) `forbidden` / `insufficient_scope` — the credential lacks permission. ```json { "error": { "code": "insufficient_scope", "message": "This API key does not have the required scope for this action." } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 409 (version_conflict) `conflict` / `version_conflict` (current state in `details.application`) / Idempotency-Key reuse. ```json { "error": { "code": "version_conflict", "message": "This application was updated elsewhere. Refresh and try again." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Create Checkout > Create a Stripe checkout link **Endpoint:** `POST /v1/applications/{application_id}/checkout` **Access:** Account required (scope `billing:write`) **MCP tool:** `create_checkout` Agents can create the link; only a person can pay. Payment is confirmed by webhook. Claims a guest draft first when needed. ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/checkout" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" \ -H "Idempotency-Key: idem-$(date +%s)" \ -H "Content-Type: application/json" \ -d '{ "product": "vmc" }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `Idempotency-Key` (header, required) | `string` | Unique key per intended action (≤ 255 chars). Same key + same body replays the first response; same key + different body → 409. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Request body | Field | Type | Description | |---|---|---| | `product` | `string` | Defaults to the chosen/recommended product. | ```json { "product": "vmc" } ``` ## Responses ### 201 OK Checkout link. ```json { "checkout_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "expires_at": "2026-10-31T12:00:00Z", "order_id": "0192a5e0-1234-7000-8000-000000000001", "reused": true } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 403 (insufficient_scope) `forbidden` / `insufficient_scope` — the credential lacks permission. ```json { "error": { "code": "insufficient_scope", "message": "This API key does not have the required scope for this action." } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 409 (version_conflict) `conflict` / `version_conflict` (current state in `details.application`) / Idempotency-Key reuse. ```json { "error": { "code": "version_conflict", "message": "This application was updated elsewhere. Refresh and try again." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Claim Application > Move a guest draft into the account **Endpoint:** `POST /v1/applications/{application_id}/claim` **Access:** Account required (scope `applications:write`) **MCP tool:** `claim_application` Send the account credential plus the guest credential (`VI-Guest-Token`). Atomic; when the account already has the domain the more advanced application is kept. ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/claim" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Claim result. ```json { "application": { "background": [], "brand_name": "string", "contact_email": "string", "created_at": "2026-10-31T12:00:00Z", "current_step": "domain", "domain": "acme.com", "domain_display": "string", "domain_status": "unknown", "email_usage": "none", "environment": "live", "id": "0192a5e0-1234-7000-8000-000000000001", "next_actions": [], "organizational_domain": "string", "outcomes": { "bimi": "not_published", "business": "not_started", "certificate": "not_ordered", "dmarc_rollout": "missing", "eligibility": "unknown", "inbox_display": "not_observed", "logo": "missing", "ownership": "pending", "payment": "unpaid", "trademark": "not_started" }, "owner": { "type": "guest" }, "product": "vmc", "product_selected": "vmc", "recommended_product": "vmc", "steps": [], "updated_at": "2026-10-31T12:00:00Z", "version": 1, "web_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" }, "kept_application_id": "0192a5e0-1234-7000-8000-000000000001", "result": "claimed" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 403 (insufficient_scope) `forbidden` / `insufficient_scope` — the credential lacks permission. ```json { "error": { "code": "insufficient_scope", "message": "This API key does not have the required scope for this action." } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Verify DKIM Selectors > Verify DKIM selectors you supply **Endpoint:** `POST /v1/applications/{application_id}/dkim-selectors` **Access:** Free (guest token or account) **MCP tool:** `verify_dkim_selectors` ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/dkim-selectors" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "selectors": [ "string" ] }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Request body | Field | Type | Description | |---|---|---| | `selectors` *(required)* | `string[]` | DKIM selectors to check, e.g. `google`, `s1` (from the sending service's setup page). | ```json { "selectors": [ "string" ] } ``` ## Responses ### 200 OK Updated report. ```json { "checked_at": "2026-10-31T12:00:00Z", "dmarc_reports": { "addresses": [], "receives_email": true, "source": "custom" }, "dmarc_rollout": { "direct_record": { "host": "string", "note": "string", "type": "string", "value": "string" }, "email_usage": "none", "enforced": true, "next_stage": "missing", "path": "direct", "plan": [], "ready_to_advance": true, "reason": "Updated company logo", "recommended_record": { "host": "string", "note": "string", "type": "string", "value": "string" }, "removed_tags": [], "stage": "missing" }, "email_usage": "none", "report": { "bimi": { "authority_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "findings": [], "logo_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "record": "string", "status": "pass" }, "checked_at": "2026-10-31T12:00:00Z", "dkim": { "findings": [], "selectors": [], "status": "pass" }, "dmarc": { "findings": [], "inherited_from": "string", "pct": 1, "policy": "none", "record": "string", "rollout_stage": "missing", "rua": [], "status": "pass", "subdomain_policy": "none" }, "domain": "acme.com", "findings": [], "mx": { "provider": { "id": "0192a5e0-1234-7000-8000-000000000001", "name": "Acme Inc." }, "records": [], "status": "pass" }, "resolver": "authoritative", "score": { "passed": 1, "status": "ready", "total": 1 }, "spf": { "all": "string", "findings": [], "lookups": 1, "record": "string", "status": "pass" } }, "suggested_email_usage": "none" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Set DMARC Reports > Where daily DMARC reports go **Endpoint:** `PUT /v1/applications/{application_id}/dmarc-reports` **Access:** Free (guest token or account) **MCP tool:** `set_dmarc_report_address` Sets the address used in every suggested DMARC record (`rua=`). `null` goes back to the published address, or `dmarc@`. The customer still publishes the record; `dmarc_reports` shows the address in use. ## cURL example ```bash curl -X PUT "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/dmarc-reports" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "email": "alex@acme.com" }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Request body | Field | Type | Description | |---|---|---| | `email` *(required)* | `string` | Address for daily DMARC reports, e.g. `dmarc@acme.com` (`mailto:` optional). `null` or empty goes back to the default. | ```json { "email": "alex@acme.com" } ``` ## Responses ### 200 OK Updated guidance. ```json { "checked_at": "2026-10-31T12:00:00Z", "dmarc_reports": { "addresses": [], "receives_email": true, "source": "custom" }, "dmarc_rollout": { "direct_record": { "host": "string", "note": "string", "type": "string", "value": "string" }, "email_usage": "none", "enforced": true, "next_stage": "missing", "path": "direct", "plan": [], "ready_to_advance": true, "reason": "Updated company logo", "recommended_record": { "host": "string", "note": "string", "type": "string", "value": "string" }, "removed_tags": [], "stage": "missing" }, "email_usage": "none", "report": { "bimi": { "authority_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "findings": [], "logo_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "record": "string", "status": "pass" }, "checked_at": "2026-10-31T12:00:00Z", "dkim": { "findings": [], "selectors": [], "status": "pass" }, "dmarc": { "findings": [], "inherited_from": "string", "pct": 1, "policy": "none", "record": "string", "rollout_stage": "missing", "rua": [], "status": "pass", "subdomain_policy": "none" }, "domain": "acme.com", "findings": [], "mx": { "provider": { "id": "0192a5e0-1234-7000-8000-000000000001", "name": "Acme Inc." }, "records": [], "status": "pass" }, "resolver": "authoritative", "score": { "passed": 1, "status": "ready", "total": 1 }, "spf": { "all": "string", "findings": [], "lookups": 1, "record": "string", "status": "pass" } }, "suggested_email_usage": "none" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get DNS Records > Every DNS record to add, with live status **Endpoint:** `GET /v1/applications/{application_id}/dns` **Access:** Paid plan required **MCP tool:** `get_dns_records` ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/dns" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Records. ```json { "all_required_ok": true, "checked_at": "2026-10-31T12:00:00Z", "dns_host": "string", "nameservers": [ "string" ], "records": [ { "current": [], "from_step": "domain", "glossary": "string", "host": "string", "host_relative": "string", "id": "ownership", "note": "string", "purpose": "string", "required": true, "rollout": { "direct_record": { "host": {}, "note": {}, "type": {}, "value": {} }, "email_usage": "none", "enforced": true, "next_stage": "missing", "path": "direct", "plan": [], "ready_to_advance": true, "reason": "Updated company logo", "recommended_record": { "host": {}, "note": {}, "type": {}, "value": {} }, "removed_tags": [], "stage": "missing" }, "rollout_stage": "missing", "status": "ok", "type": "string", "value": "string" } ], "resolver": "authoritative", "zone": "acme.com" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 402 (payment_required) `payment_required` — this step needs a paid plan. `next_action.kind = checkout`. ```json { "error": { "code": "payment_required", "message": "This step requires an active paid plan for this domain.", "next_action": { "kind": "checkout", "title": "Continue to checkout" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Verify DNS > Re-check DNS now **Endpoint:** `POST /v1/applications/{application_id}/dns/verify` **Access:** Paid plan required **MCP tool:** `verify_dns` Checks the authoritative nameservers now and watches closely for the next minutes. ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/dns/verify" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Records. ```json { "all_required_ok": true, "checked_at": "2026-10-31T12:00:00Z", "dns_host": "string", "nameservers": [ "string" ], "records": [ { "current": [], "from_step": "domain", "glossary": "string", "host": "string", "host_relative": "string", "id": "ownership", "note": "string", "purpose": "string", "required": true, "rollout": { "direct_record": { "host": {}, "note": {}, "type": {}, "value": {} }, "email_usage": "none", "enforced": true, "next_stage": "missing", "path": "direct", "plan": [], "ready_to_advance": true, "reason": "Updated company logo", "recommended_record": { "host": {}, "note": {}, "type": {}, "value": {} }, "removed_tags": [], "stage": "missing" }, "rollout_stage": "missing", "status": "ok", "type": "string", "value": "string" } ], "resolver": "authoritative", "zone": "acme.com" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 402 (payment_required) `payment_required` — this step needs a paid plan. `next_action.kind = checkout`. ```json { "error": { "code": "payment_required", "message": "This step requires an active paid plan for this domain.", "next_action": { "kind": "checkout", "title": "Continue to checkout" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Eligibility > VMC / CMC / not-yet-eligible with evidence **Endpoint:** `GET /v1/applications/{application_id}/eligibility` **Access:** Free (guest token or account) **MCP tool:** `get_eligibility` ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/eligibility" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Assessment. ```json { "assessed_at": "2026-10-31T12:00:00Z", "estimated_eligible_on": "2026-10-31", "next_steps": [ { "description": "string", "kind": "string", "title": "string" } ], "override": "vmc_eligible", "recommended_product": "vmc", "routes": { "cmc": { "checkmark": true, "eligible": true, "requirements": [] }, "vmc": { "checkmark": true, "eligible": true, "requirements": [] } }, "status": "unknown" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Watch Eligibility > Email me when this domain qualifies **Endpoint:** `POST /v1/applications/{application_id}/eligibility/watch` **Access:** Free (guest token or account) **MCP tool:** `watch_eligibility` ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/eligibility/watch" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "email": "alex@acme.com" }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Request body | Field | Type | Description | |---|---|---| | `email` *(required)* | `string (email)` | Where to send the email when the domain qualifies. | ```json { "email": "alex@acme.com" } ``` ## Responses ### 201 OK Watching. ```json { "email": "alex@acme.com", "message": "Everything checks out.", "watching": true } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Email Security > Latest email-security report + DMARC rollout plan **Endpoint:** `GET /v1/applications/{application_id}/email-security` **Access:** Free (guest token or account) ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/email-security" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Report (null until the first scan finishes). ```json { "checked_at": "2026-10-31T12:00:00Z", "dmarc_reports": { "addresses": [], "receives_email": true, "source": "custom" }, "dmarc_rollout": { "direct_record": { "host": "string", "note": "string", "type": "string", "value": "string" }, "email_usage": "none", "enforced": true, "next_stage": "missing", "path": "direct", "plan": [], "ready_to_advance": true, "reason": "Updated company logo", "recommended_record": { "host": "string", "note": "string", "type": "string", "value": "string" }, "removed_tags": [], "stage": "missing" }, "email_usage": "none", "report": { "bimi": { "authority_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "findings": [], "logo_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "record": "string", "status": "pass" }, "checked_at": "2026-10-31T12:00:00Z", "dkim": { "findings": [], "selectors": [], "status": "pass" }, "dmarc": { "findings": [], "inherited_from": "string", "pct": 1, "policy": "none", "record": "string", "rollout_stage": "missing", "rua": [], "status": "pass", "subdomain_policy": "none" }, "domain": "acme.com", "findings": [], "mx": { "provider": { "id": "0192a5e0-1234-7000-8000-000000000001", "name": "Acme Inc." }, "records": [], "status": "pass" }, "resolver": "authoritative", "score": { "passed": 1, "status": "ready", "total": 1 }, "spf": { "all": "string", "findings": [], "lookups": 1, "record": "string", "status": "pass" } }, "suggested_email_usage": "none" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Recheck Email Security > Re-check email security now **Endpoint:** `POST /v1/applications/{application_id}/email-security/check` **Access:** Free (guest token or account) **MCP tool:** `recheck_email_security` Run after changing DNS records. Uses the domain's authoritative nameservers, so changes show within seconds. DKIM selectors found earlier are checked again. Sandbox domains simulate publishing the recommended DMARC record. ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/email-security/check" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Updated report. ```json { "checked_at": "2026-10-31T12:00:00Z", "dmarc_reports": { "addresses": [], "receives_email": true, "source": "custom" }, "dmarc_rollout": { "direct_record": { "host": "string", "note": "string", "type": "string", "value": "string" }, "email_usage": "none", "enforced": true, "next_stage": "missing", "path": "direct", "plan": [], "ready_to_advance": true, "reason": "Updated company logo", "recommended_record": { "host": "string", "note": "string", "type": "string", "value": "string" }, "removed_tags": [], "stage": "missing" }, "email_usage": "none", "report": { "bimi": { "authority_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "findings": [], "logo_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "record": "string", "status": "pass" }, "checked_at": "2026-10-31T12:00:00Z", "dkim": { "findings": [], "selectors": [], "status": "pass" }, "dmarc": { "findings": [], "inherited_from": "string", "pct": 1, "policy": "none", "record": "string", "rollout_stage": "missing", "rua": [], "status": "pass", "subdomain_policy": "none" }, "domain": "acme.com", "findings": [], "mx": { "provider": { "id": "0192a5e0-1234-7000-8000-000000000001", "name": "Acme Inc." }, "records": [], "status": "pass" }, "resolver": "authoritative", "score": { "passed": 1, "status": "ready", "total": 1 }, "spf": { "all": "string", "findings": [], "lookups": 1, "record": "string", "status": "pass" } }, "suggested_email_usage": "none" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Set Email Usage > How email is used on the domain **Endpoint:** `PUT /v1/applications/{application_id}/email-usage` **Access:** Free (guest token or account) **MCP tool:** `set_email_usage` `none` (new domain, no email yet) and `low` (only the mailbox provider sends email) get the blocking DMARC record straight away; `active` (several tools send email) gets the staged rollout. Waiting times are suggestions: every stage's record is in `dmarc_rollout.plan`. The email security step needs DMARC published with a report address (watch mode is enough); filtering or blocking is required by the DNS step. ## cURL example ```bash curl -X PUT "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/email-usage" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "email_usage": "none" }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Request body | Field | Type | Description | |---|---|---| | `email_usage` *(required)* | `string` | `none`: new domain, no email sent yet. `low`: email is sent only from the mailbox provider (e.g. Google Workspace). `active`: several tools send email (newsletters, billing, support…). | ```json { "email_usage": "none" } ``` ## Responses ### 200 OK Updated guidance. ```json { "checked_at": "2026-10-31T12:00:00Z", "dmarc_reports": { "addresses": [], "receives_email": true, "source": "custom" }, "dmarc_rollout": { "direct_record": { "host": "string", "note": "string", "type": "string", "value": "string" }, "email_usage": "none", "enforced": true, "next_stage": "missing", "path": "direct", "plan": [], "ready_to_advance": true, "reason": "Updated company logo", "recommended_record": { "host": "string", "note": "string", "type": "string", "value": "string" }, "removed_tags": [], "stage": "missing" }, "email_usage": "none", "report": { "bimi": { "authority_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "findings": [], "logo_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "record": "string", "status": "pass" }, "checked_at": "2026-10-31T12:00:00Z", "dkim": { "findings": [], "selectors": [], "status": "pass" }, "dmarc": { "findings": [], "inherited_from": "string", "pct": 1, "policy": "none", "record": "string", "rollout_stage": "missing", "rua": [], "status": "pass", "subdomain_policy": "none" }, "domain": "acme.com", "findings": [], "mx": { "provider": { "id": "0192a5e0-1234-7000-8000-000000000001", "name": "Acme Inc." }, "records": [], "status": "pass" }, "resolver": "authoritative", "score": { "passed": 1, "status": "ready", "total": 1 }, "spf": { "all": "string", "findings": [], "lookups": 1, "record": "string", "status": "pass" } }, "suggested_email_usage": "none" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Estimate > Itemized yearly price **Endpoint:** `GET /v1/applications/{application_id}/estimate` **Access:** Free (guest token or account) **MCP tool:** `get_estimate` Plan + pass-through issuer fee (estimated). Full refund if issuance is rejected. ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/estimate" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `product` (query) | `string` | Default: the chosen/recommended product. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Estimate. ```json { "currency": "usd", "eligible": true, "interval": "string", "issuer_fee_range_cents": { "max": 1, "min": 1 }, "line_items": [ { "amount_cents": 149900, "estimated": true, "kind": "plan", "label": "string" } ], "product": "vmc", "refund_policy": "string", "renewal": "string", "total_cents": 149900 } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # List Events > Timeline of the verification **Endpoint:** `GET /v1/applications/{application_id}/events` **Access:** Free (guest token or account) ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/events" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Events, newest first. ```json { "data": [ { "actor": { "id": "0192a5e0-1234-7000-8000-000000000001", "type": "guest" }, "created_at": "2026-10-31T12:00:00Z", "data": {}, "id": "0192a5e0-1234-7000-8000-000000000001", "kind": "string", "message": "Everything checks out." } ], "next_cursor": "string" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Go Live > BIMI publication, test email, display, monitoring, renewal **Endpoint:** `GET /v1/applications/{application_id}/go-live` **Access:** Free (guest token or account) ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/go-live" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Status. ```json { "bimi": { "assets_match": true, "certificate_reachable": true, "certificate_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "host": "string", "logo_reachable": true, "logo_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "record": "string", "status": "not_published" }, "inbox_display": { "expects_checkmark": true, "mailbox": "string", "observed_at": "2026-10-31T12:00:00Z", "status": "not_observed" }, "monitoring": { "enabled": true, "last_checked_at": "2026-10-31T12:00:00Z" }, "renewal": { "auto_renew": true, "not_after": "2026-10-31T12:00:00Z", "renewal_starts_on": "2026-10-31" }, "test_message": { "address": "string", "bimi_selector": "string", "created_at": "2026-10-31T12:00:00Z", "dkim": [], "dmarc": { "aligned_via": [], "result": "pass" }, "expires_at": "2026-10-31T12:00:00Z", "findings": [], "from": "string", "from_domain": "acme.com", "id": "0192a5e0-1234-7000-8000-000000000001", "instructions": "string", "received_at": "2026-10-31T12:00:00Z", "spf": { "aligned": true, "domain": "acme.com", "result": "pass" }, "status": "waiting" } } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Confirm Inbox Display > Record that the logo was seen in a mailbox **Endpoint:** `POST /v1/applications/{application_id}/inbox-display` **Access:** Paid plan required (scope `applications:write`) **MCP tool:** `confirm_inbox_display` ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/inbox-display" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "mailbox": "string", "observed": true }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Request body | Field | Type | Description | |---|---|---| | `mailbox` | `string` | gmail (default), yahoo, apple_mail, … | | `observed` | `boolean` | Default true. | ```json { "mailbox": "string", "observed": true } ``` ## Responses ### 200 OK Recorded. ```json { "bimi": { "assets_match": true, "certificate_reachable": true, "certificate_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "host": "string", "logo_reachable": true, "logo_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "record": "string", "status": "not_published" }, "inbox_display": { "expects_checkmark": true, "mailbox": "string", "observed_at": "2026-10-31T12:00:00Z", "status": "not_observed" }, "monitoring": { "enabled": true, "last_checked_at": "2026-10-31T12:00:00Z" }, "renewal": { "auto_renew": true, "not_after": "2026-10-31T12:00:00Z", "renewal_starts_on": "2026-10-31" }, "test_message": { "address": "string", "bimi_selector": "string", "created_at": "2026-10-31T12:00:00Z", "dkim": [], "dmarc": { "aligned_via": [], "result": "pass" }, "expires_at": "2026-10-31T12:00:00Z", "findings": [], "from": "string", "from_domain": "acme.com", "id": "0192a5e0-1234-7000-8000-000000000001", "instructions": "string", "received_at": "2026-10-31T12:00:00Z", "spf": { "aligned": true, "domain": "acme.com", "result": "pass" }, "status": "waiting" } } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 402 (payment_required) `payment_required` — this step needs a paid plan. `next_action.kind = checkout`. ```json { "error": { "code": "payment_required", "message": "This step requires an active paid plan for this domain.", "next_action": { "kind": "checkout", "title": "Continue to checkout" } } } ``` ### 403 (insufficient_scope) `forbidden` / `insufficient_scope` — the credential lacks permission. ```json { "error": { "code": "insufficient_scope", "message": "This API key does not have the required scope for this action." } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Logo > Logo status, analysis and changes **Endpoint:** `GET /v1/applications/{application_id}/logo` **Access:** Free (guest token or account) **MCP tool:** `get_logo_status` 404 until a logo was submitted. ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/logo" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Logo. ```json { "analysis": { "base_profile": "string", "compliant": true, "element_count": 1, "has_raster": true, "has_text": true, "has_title": true, "height": 1, "issues": [], "size_bytes": 1, "square": true, "title": "string", "version": "string", "view_box": [], "width": 1 }, "approved_at": "2026-10-31T12:00:00Z", "changes": [ { "code": "string", "lossy": true, "message": "Everything checks out.", "technical": "string" } ], "converted": { "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "size": 1, "url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" }, "created_at": "2026-10-31T12:00:00Z", "error": "string", "id": "0192a5e0-1234-7000-8000-000000000001", "original": { "filename": "string", "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "size": 1, "url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" }, "previews": { "converted": "string", "original": "string" }, "requirements": [ { "id": "0192a5e0-1234-7000-8000-000000000001", "label": "string", "met_by_converted": true, "met_by_original": true, "required": true, "source": "gmail" } ], "review": [ { "change": "string", "code": "string", "lossy": true, "noticed": "string", "outcome": "changed", "source": "gmail", "technical": "string", "why": "string" } ], "source": "upload", "source_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "status": "processing", "visual_diff": { "score": 1, "verdict": "identical" } } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Submit Logo > Submit the logo **Endpoint:** `PUT /v1/applications/{application_id}/logo` **Access:** Free (guest token or account) **MCP tool:** `submit_logo` SVG text, an https URL, or the logo discovered on the website. The original is preserved byte-for-byte; the SVG Tiny P/S derivative and a trademark search start in the background. ## cURL example ```bash curl -X PUT "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/logo" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "filename": "string", "svg": "string" }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | | `If-Match` (header) | `string` | The application `version` you last saw, e.g. `"7"`. Stale → 409 `version_conflict` with the current application. | ## Request body ```json { "filename": "string", "svg": "string" } ``` ## Responses ### 201 OK Received; processing. ```json { "analysis": { "base_profile": "string", "compliant": true, "element_count": 1, "has_raster": true, "has_text": true, "has_title": true, "height": 1, "issues": [], "size_bytes": 1, "square": true, "title": "string", "version": "string", "view_box": [], "width": 1 }, "approved_at": "2026-10-31T12:00:00Z", "changes": [ { "code": "string", "lossy": true, "message": "Everything checks out.", "technical": "string" } ], "converted": { "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "size": 1, "url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" }, "created_at": "2026-10-31T12:00:00Z", "error": "string", "id": "0192a5e0-1234-7000-8000-000000000001", "original": { "filename": "string", "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "size": 1, "url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" }, "previews": { "converted": "string", "original": "string" }, "requirements": [ { "id": "0192a5e0-1234-7000-8000-000000000001", "label": "string", "met_by_converted": true, "met_by_original": true, "required": true, "source": "gmail" } ], "review": [ { "change": "string", "code": "string", "lossy": true, "noticed": "string", "outcome": "changed", "source": "gmail", "technical": "string", "why": "string" } ], "source": "upload", "source_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "status": "processing", "visual_diff": { "score": 1, "verdict": "identical" } } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 409 (version_conflict) `conflict` / `version_conflict` (current state in `details.application`) / Idempotency-Key reuse. ```json { "error": { "code": "version_conflict", "message": "This application was updated elsewhere. Refresh and try again." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # List Logo Candidates > Logos found on the website **Endpoint:** `GET /v1/applications/{application_id}/logo-candidates` **Access:** Free (guest token or account) **MCP tool:** `get_logo_status` ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/logo-candidates" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Candidates. ```json { "data": [ { "height": 1, "kind": "svg", "source": "string", "url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "usable_for_bimi": true, "width": 1 } ], "scanned": true, "website": { "final_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "reachable": true, "site_name": "string" } } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Approve Logo > Approve the reviewed inbox-ready logo **Endpoint:** `POST /v1/applications/{application_id}/logo/approve` **Access:** Paid plan required (scope `applications:write`) **MCP tool:** `approve_logo` Approves exactly the derivative that was reviewed (`converted_sha256`); a different version → 409. ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/logo/approve" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "converted_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Request body | Field | Type | Description | |---|---|---| | `converted_sha256` *(required)* | `string` | The `converted.sha256` the customer reviewed. | ```json { "converted_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" } ``` ## Responses ### 200 OK Approved. ```json { "analysis": { "base_profile": "string", "compliant": true, "element_count": 1, "has_raster": true, "has_text": true, "has_title": true, "height": 1, "issues": [], "size_bytes": 1, "square": true, "title": "string", "version": "string", "view_box": [], "width": 1 }, "approved_at": "2026-10-31T12:00:00Z", "changes": [ { "code": "string", "lossy": true, "message": "Everything checks out.", "technical": "string" } ], "converted": { "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "size": 1, "url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" }, "created_at": "2026-10-31T12:00:00Z", "error": "string", "id": "0192a5e0-1234-7000-8000-000000000001", "original": { "filename": "string", "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "size": 1, "url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" }, "previews": { "converted": "string", "original": "string" }, "requirements": [ { "id": "0192a5e0-1234-7000-8000-000000000001", "label": "string", "met_by_converted": true, "met_by_original": true, "required": true, "source": "gmail" } ], "review": [ { "change": "string", "code": "string", "lossy": true, "noticed": "string", "outcome": "changed", "source": "gmail", "technical": "string", "why": "string" } ], "source": "upload", "source_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "status": "processing", "visual_diff": { "score": 1, "verdict": "identical" } } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 402 (payment_required) `payment_required` — this step needs a paid plan. `next_action.kind = checkout`. ```json { "error": { "code": "payment_required", "message": "This step requires an active paid plan for this domain.", "next_action": { "kind": "checkout", "title": "Continue to checkout" } } } ``` ### 403 (insufficient_scope) `forbidden` / `insufficient_scope` — the credential lacks permission. ```json { "error": { "code": "insufficient_scope", "message": "This API key does not have the required scope for this action." } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Logo Converted > Inbox-ready SVG Tiny P/S file **Endpoint:** `GET /v1/applications/{application_id}/logo/converted.svg` **Access:** Free (guest token or account) ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/logo/converted.svg" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK The compliant derivative. ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Logo Original > Original logo file **Endpoint:** `GET /v1/applications/{application_id}/logo/original.svg` **Access:** Free (guest token or account) ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/logo/original.svg" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK The original file (sandboxed CSP). ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Logo Preview > Rendered PNG preview **Endpoint:** `GET /v1/applications/{application_id}/logo/preview.png` **Access:** Free (guest token or account) ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/logo/preview.png" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `variant` (query) | `string` | Default `converted` (falls back to the original while converting). | | `size` (query) | `integer` | Square size in px (default 256, clamped 32–1024). | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK PNG. ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Next Steps > Current step and next actions **Endpoint:** `GET /v1/applications/{application_id}/next-steps` **Access:** Free (guest token or account) **MCP tool:** `get_application` ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/next-steps" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Next steps. ```json { "background": [ { "kind": "domain_scan", "message": "Everything checks out.", "next_run_at": "2026-10-31T12:00:00Z", "state": "string", "status": "pending", "updated_at": "2026-10-31T12:00:00Z" } ], "current_step": "domain", "next_actions": [ { "api": { "method": "string", "path": "string" }, "description": "string", "kind": "string", "mcp_tool": "string", "requires_auth": true, "requires_payment": true, "step": "domain", "title": "string", "url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" } ], "steps": [ { "completed_at": "2026-10-31T12:00:00Z", "paid": true, "phase": "check", "requirements": [], "slug": "domain", "status": "locked", "summary": "string", "title": "string" } ] } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Refresh Application > Re-run checks now **Endpoint:** `POST /v1/applications/{application_id}/refresh` **Access:** Free (guest token or account) ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/refresh" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Checks restarted. ```json { "background": [ { "kind": "domain_scan", "message": "Everything checks out.", "next_run_at": "2026-10-31T12:00:00Z", "state": "string", "status": "pending", "updated_at": "2026-10-31T12:00:00Z" } ], "brand_name": "string", "contact_email": "string", "created_at": "2026-10-31T12:00:00Z", "current_step": "domain", "domain": "acme.com", "domain_display": "string", "domain_status": "unknown", "email_usage": "none", "environment": "live", "id": "0192a5e0-1234-7000-8000-000000000001", "next_actions": [ { "api": { "method": "string", "path": "string" }, "description": "string", "kind": "string", "mcp_tool": "string", "requires_auth": true, "requires_payment": true, "step": "domain", "title": "string", "url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" } ], "organizational_domain": "string", "outcomes": { "bimi": "not_published", "business": "not_started", "certificate": "not_ordered", "dmarc_rollout": "missing", "eligibility": "unknown", "inbox_display": "not_observed", "logo": "missing", "ownership": "pending", "payment": "unpaid", "trademark": "not_started" }, "owner": { "type": "guest" }, "product": "vmc", "product_selected": "vmc", "recommended_product": "vmc", "steps": [ { "completed_at": "2026-10-31T12:00:00Z", "paid": true, "phase": "check", "requirements": [], "slug": "domain", "status": "locked", "summary": "string", "title": "string" } ], "updated_at": "2026-10-31T12:00:00Z", "version": 1, "web_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Create Resume Link > One-time link to continue in a browser **Endpoint:** `POST /v1/applications/{application_id}/resume-links` **Access:** Free (guest token or account) **MCP tool:** `create_resume_link` The token is in the URL fragment (never sent to servers). Optionally emailed. 24h, single use. ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/resume-links" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "email": "alex@acme.com" }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Request body | Field | Type | Description | |---|---|---| | `email` | `string (email)` | Optionally email the link. | ```json { "email": "alex@acme.com" } ``` ## Responses ### 201 OK Link. ```json { "emailed": true, "expires_at": "2026-10-31T12:00:00Z", "url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Complete Step > Complete a wizard step and move on **Endpoint:** `POST /v1/applications/{application_id}/steps/{step}/complete` **Access:** Free (guest token or account) **MCP tool:** `complete_step` Only the user (or their agent) moves the wizard forward; background work never does. Works for the current step once every required item in its `requirements` is met, and for an earlier step marked `attention` once it's fixed (that step's checks are re-run live). Paid steps need an account. 409 `step_not_ready` lists what's missing; 409 `step_locked` when an earlier step comes first. Completing an already completed step returns the application unchanged. ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/steps/domain/complete" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `step` (path, required) | `string` | Wizard step, e.g. `domain`, `email-security`, `dns`. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK The application after completing the step. ```json { "background": [ { "kind": "domain_scan", "message": "Everything checks out.", "next_run_at": "2026-10-31T12:00:00Z", "state": "string", "status": "pending", "updated_at": "2026-10-31T12:00:00Z" } ], "brand_name": "string", "contact_email": "string", "created_at": "2026-10-31T12:00:00Z", "current_step": "domain", "domain": "acme.com", "domain_display": "string", "domain_status": "unknown", "email_usage": "none", "environment": "live", "id": "0192a5e0-1234-7000-8000-000000000001", "next_actions": [ { "api": { "method": "string", "path": "string" }, "description": "string", "kind": "string", "mcp_tool": "string", "requires_auth": true, "requires_payment": true, "step": "domain", "title": "string", "url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" } ], "organizational_domain": "string", "outcomes": { "bimi": "not_published", "business": "not_started", "certificate": "not_ordered", "dmarc_rollout": "missing", "eligibility": "unknown", "inbox_display": "not_observed", "logo": "missing", "ownership": "pending", "payment": "unpaid", "trademark": "not_started" }, "owner": { "type": "guest" }, "product": "vmc", "product_selected": "vmc", "recommended_product": "vmc", "steps": [ { "completed_at": "2026-10-31T12:00:00Z", "paid": true, "phase": "check", "requirements": [], "slug": "domain", "status": "locked", "summary": "string", "title": "string" } ], "updated_at": "2026-10-31T12:00:00Z", "version": 1, "web_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 409 (version_conflict) Not ready (`step_not_ready`, `details.missing`) or not available yet (`step_locked`). ```json { "error": { "code": "version_conflict", "message": "This application was updated elsewhere. Refresh and try again." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Create Test Message > Get an address to send a real test email to **Endpoint:** `POST /v1/applications/{application_id}/test-messages` **Access:** Free (guest token or account) **MCP tool:** `create_test_message` Free. When the email arrives we check SPF, DKIM and DMARC on it, and a DKIM signature aligned with the domain is added to the email security report (so a key under an unusual selector is found). ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/test-messages" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 201 OK Address. ```json { "address": "string", "bimi_selector": "string", "created_at": "2026-10-31T12:00:00Z", "dkim": [ { "aligned": true, "domain": "acme.com", "result": "pass", "selector": "string" } ], "dmarc": { "aligned_via": [], "result": "pass" }, "expires_at": "2026-10-31T12:00:00Z", "findings": [ { "area": "mx", "detail": "string", "fix": { "host": "string", "note": "string", "type": "string", "value": "string" }, "id": "0192a5e0-1234-7000-8000-000000000001", "severity": "info", "technical": "string", "title": "string" } ], "from": "string", "from_domain": "acme.com", "id": "0192a5e0-1234-7000-8000-000000000001", "instructions": "string", "received_at": "2026-10-31T12:00:00Z", "spf": { "aligned": true, "domain": "acme.com", "result": "pass" }, "status": "waiting" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Test Message > Authentication results of the test email **Endpoint:** `GET /v1/applications/{application_id}/test-messages/{test_message_id}` **Access:** Free (guest token or account) **MCP tool:** `get_test_message_result` ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/test-messages/0192a5e0-1234-7000-8000-000000000005" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `test_message_id` (path, required) | `string (uuid)` | Test message id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Result. ```json { "address": "string", "bimi_selector": "string", "created_at": "2026-10-31T12:00:00Z", "dkim": [ { "aligned": true, "domain": "acme.com", "result": "pass", "selector": "string" } ], "dmarc": { "aligned_via": [], "result": "pass" }, "expires_at": "2026-10-31T12:00:00Z", "findings": [ { "area": "mx", "detail": "string", "fix": { "host": "string", "note": "string", "type": "string", "value": "string" }, "id": "0192a5e0-1234-7000-8000-000000000001", "severity": "info", "technical": "string", "title": "string" } ], "from": "string", "from_domain": "acme.com", "id": "0192a5e0-1234-7000-8000-000000000001", "instructions": "string", "received_at": "2026-10-31T12:00:00Z", "spf": { "aligned": true, "domain": "acme.com", "result": "pass" }, "status": "waiting" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Trademark > Trademark result **Endpoint:** `GET /v1/applications/{application_id}/trademark` **Access:** Free (guest token or account) **MCP tool:** `get_trademark_status` ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/trademark" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Trademark status. ```json { "candidates": [ { "filing_date": "2026-10-31", "image_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "mark_text": "string", "mark_type": "word", "method": "string", "office": "string", "owner": "string", "registration_date": "2026-10-31", "registration_number": "string", "serial_number": "string", "similarity": 1, "source_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "status": "registered", "status_text": "string" } ], "checked_at": "2026-10-31T12:00:00Z", "match": { "filing_date": "2026-10-31", "image_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "mark_text": "string", "mark_type": "word", "method": "string", "office": "string", "owner": "string", "registration_date": "2026-10-31", "registration_number": "string", "serial_number": "string", "similarity": 1, "source_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "status": "registered", "status_text": "string" }, "message": "Everything checks out.", "provider": "string", "status": "not_started", "trademark_service_email": "string" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Claim Trademark > Verify a specific USPTO number **Endpoint:** `POST /v1/applications/{application_id}/trademark/claim` **Access:** Free (guest token or account) **MCP tool:** `search_trademark` ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/trademark/claim" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "serial_or_registration_number": "string" }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Request body | Field | Type | Description | |---|---|---| | `serial_or_registration_number` *(required)* | `string` | US registration (7 digits) or serial (8 digits) number. | ```json { "serial_or_registration_number": "string" } ``` ## Responses ### 200 OK Check started. ```json { "candidates": [ { "filing_date": "2026-10-31", "image_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "mark_text": "string", "mark_type": "word", "method": "string", "office": "string", "owner": "string", "registration_date": "2026-10-31", "registration_number": "string", "serial_number": "string", "similarity": 1, "source_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "status": "registered", "status_text": "string" } ], "checked_at": "2026-10-31T12:00:00Z", "match": { "filing_date": "2026-10-31", "image_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "mark_text": "string", "mark_type": "word", "method": "string", "office": "string", "owner": "string", "registration_date": "2026-10-31", "registration_number": "string", "serial_number": "string", "similarity": 1, "source_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "status": "registered", "status_text": "string" }, "message": "Everything checks out.", "provider": "string", "status": "not_started", "trademark_service_email": "string" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Search Trademark > (Re)start the USPTO search **Endpoint:** `POST /v1/applications/{application_id}/trademark/search` **Access:** Free (guest token or account) **MCP tool:** `search_trademark` Budgeted for anonymous callers (429 with `details.reason = daily_budget_exhausted`). ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/applications/0192a5e0-1234-7000-8000-000000000001/trademark/search" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "mark_text": "string", "owner_name": "string" }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `application_id` (path, required) | `string (uuid)` | Application id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Request body | Field | Type | Description | |---|---|---| | `mark_text` | `string` | Words in the mark to search for (defaults to the brand name). | | `owner_name` | `string` | Legal owner name to search for (defaults to the company name). | ```json { "mark_text": "string", "owner_name": "string" } ``` ## Responses ### 200 OK Search started. ```json { "candidates": [ { "filing_date": "2026-10-31", "image_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "mark_text": "string", "mark_type": "word", "method": "string", "office": "string", "owner": "string", "registration_date": "2026-10-31", "registration_number": "string", "serial_number": "string", "similarity": 1, "source_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "status": "registered", "status_text": "string" } ], "checked_at": "2026-10-31T12:00:00Z", "match": { "filing_date": "2026-10-31", "image_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "mark_text": "string", "mark_type": "word", "method": "string", "office": "string", "owner": "string", "registration_date": "2026-10-31", "registration_number": "string", "serial_number": "string", "similarity": 1, "source_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "status": "registered", "status_text": "string" }, "message": "Everything checks out.", "provider": "string", "status": "not_started", "trademark_service_email": "string" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Create Billing Portal > Stripe billing portal link **Endpoint:** `POST /v1/billing/portal` **Access:** Account required (scope `billing:write`) ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/billing/portal" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Portal URL. ```json { "url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain" } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 403 (insufficient_scope) `forbidden` / `insufficient_scope` — the credential lacks permission. ```json { "error": { "code": "insufficient_scope", "message": "This API key does not have the required scope for this action." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Check Domain > Quick domain eligibility check **Endpoint:** `POST /v1/checks/domain` **Access:** Public (no credentials needed) **MCP tool:** `check_domain` Blocked shared providers (gmail.com…), registration, mailbox provider, DMARC/BIMI summary. Repeatable, no private data. ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/checks/domain" \ -H "Content-Type: application/json" \ -d '{ "domain": "acme.com" }' ``` ## Request body | Field | Type | Description | |---|---|---| | `domain` *(required)* | `string` | A domain, URL or email address. | ```json { "domain": "acme.com" } ``` ## Responses ### 200 OK Check result. ```json { "bimi": { "present": true }, "blocked_provider": { "provider": "string", "reason": "Updated company logo" }, "checked_at": "2026-10-31T12:00:00Z", "dmarc": { "bimi_ready": true, "policy": "none", "present": true }, "domain": "acme.com", "domain_display": "string", "has_mx": true, "input": "string", "is_subdomain": true, "mailbox_provider": { "id": "0192a5e0-1234-7000-8000-000000000001", "name": "Acme Inc." }, "organizational_domain": "string", "registered": true, "summary": "string", "verdict": "ok", "website": { "final_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "reachable": true } } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Check Email Auth > Full SPF/DKIM/DMARC/BIMI inspection **Endpoint:** `POST /v1/checks/email-auth` **Access:** Public (no credentials needed) **MCP tool:** `check_email_security` Queries the domain's authoritative nameservers (sees changes within seconds), DNS-over-HTTPS fallback. Findings are plain English with exact fixes. ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/checks/email-auth" \ -H "Content-Type: application/json" \ -d '{ "dkim_selectors": [ "string" ], "domain": "acme.com" }' ``` ## Request body | Field | Type | Description | |---|---|---| | `dkim_selectors` | `string[]` | Extra DKIM selectors to check (common ones are probed). | | `domain` *(required)* | `string` | A domain, URL or email address. | ```json { "dkim_selectors": [ "string" ], "domain": "acme.com" } ``` ## Responses ### 200 OK Inspection report. ```json { "bimi": { "authority_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "findings": [], "logo_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "record": "string", "status": "pass" }, "checked_at": "2026-10-31T12:00:00Z", "dkim": { "findings": [], "selectors": [], "status": "pass" }, "dmarc": { "findings": [], "inherited_from": "string", "pct": 1, "policy": "none", "record": "string", "rollout_stage": "missing", "rua": [], "status": "pass", "subdomain_policy": "none" }, "domain": "acme.com", "findings": [ { "area": "mx", "detail": "string", "fix": { "host": "string", "note": "string", "type": "string", "value": "string" }, "id": "0192a5e0-1234-7000-8000-000000000001", "severity": "info", "technical": "string", "title": "string" } ], "mx": { "provider": { "id": "0192a5e0-1234-7000-8000-000000000001", "name": "Acme Inc." }, "records": [], "status": "pass" }, "resolver": "authoritative", "score": { "passed": 1, "status": "ready", "total": 1 }, "spf": { "all": "string", "findings": [], "lookups": 1, "record": "string", "status": "pass" } } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Complete Mock Checkout > Simulate payment of a mock checkout **Endpoint:** `POST /v1/dev/mock-checkout/{order_id}/complete` **Access:** Account required Only for orders created in mock payments mode (no Stripe key for that environment); 404 otherwise. ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/dev/mock-checkout/0192a5e0-1234-7000-8000-000000000003/complete" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `order_id` (path, required) | `string (uuid)` | Order id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Marked paid. ```json { "application_id": "0192a5e0-1234-7000-8000-000000000001", "order_id": "0192a5e0-1234-7000-8000-000000000001", "redirect_url": "https://verifiedinbox.ai/verify/0192a5e0-1234-7000-8000-000000000001/domain", "status": "paid" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # List Glossary > All glossary terms **Endpoint:** `GET /v1/glossary` **Access:** Public (no credentials needed) ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/glossary" ``` ## Responses ### 200 OK Terms. ```json { "data": [ { "doc_href": "string", "id": "0192a5e0-1234-7000-8000-000000000001", "related": [], "short": "string", "simple": "string", "technical": "string", "term": "string" } ] } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Glossary Term > One glossary term **Endpoint:** `GET /v1/glossary/{term}` **Access:** Public (no credentials needed) **MCP tool:** `explain_term` ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/glossary/bimi" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `term` (path, required) | `string` | Glossary term id, e.g. `dkim`, `bimi`, `svg-tiny-ps`. | ## Responses ### 200 OK Term. ```json { "doc_href": "string", "id": "0192a5e0-1234-7000-8000-000000000001", "related": [ "string" ], "short": "string", "simple": "string", "technical": "string", "term": "string" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Create Guest Session > Create a guest credential **Endpoint:** `POST /v1/guest-sessions` **Access:** Public (no credentials needed) Also sets the `vi_guest` cookie. Usually unnecessary: `createApplication` creates one when needed. ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/guest-sessions" ``` ## Responses ### 201 OK Guest credential (shown once). ```json { "expires_at": "2026-10-31T12:00:00Z", "guest_token": "string", "note": "string" } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Revoke Guest Session > Revoke the current guest session **Endpoint:** `DELETE /v1/guest-sessions/current` **Access:** Free (guest token or account) ## cURL example ```bash curl -X DELETE "https://api.verifiedinbox.ai/v1/guest-sessions/current" \ -H "VI-Guest-Token: vi_guest_YOUR_TOKEN" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 204 OK Revoked. ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Me > Who am I (works anonymously too) **Endpoint:** `GET /v1/me` **Access:** Public (no credentials needed) **MCP tool:** `get_account` ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/me" ``` ## Responses ### 200 OK Caller. ```json { "authenticated": true, "credential": "anonymous", "current_organization_id": "0192a5e0-1234-7000-8000-000000000001", "environment": "live", "guest": { "active": true, "application_ids": [] }, "is_admin": true, "organizations": [ { "id": "0192a5e0-1234-7000-8000-000000000001", "name": "Acme Inc.", "role": "owner" } ], "scopes": [ "applications:read" ], "user": { "email": "alex@acme.com", "id": "0192a5e0-1234-7000-8000-000000000001", "name": "Acme Inc." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Set Environment > Switch live/test mode (web session) **Endpoint:** `POST /v1/me/environment` **Access:** Account required ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/me/environment" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "environment": "live" }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Request body | Field | Type | Description | |---|---|---| | `environment` *(required)* | `string` | — | ```json { "environment": "live" } ``` ## Responses ### 200 OK Updated. ```json { "authenticated": true, "credential": "anonymous", "current_organization_id": "0192a5e0-1234-7000-8000-000000000001", "environment": "live", "guest": { "active": true, "application_ids": [] }, "is_admin": true, "organizations": [ { "id": "0192a5e0-1234-7000-8000-000000000001", "name": "Acme Inc.", "role": "owner" } ], "scopes": [ "applications:read" ], "user": { "email": "alex@acme.com", "id": "0192a5e0-1234-7000-8000-000000000001", "name": "Acme Inc." } } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Set Organization > Switch organization (web session) **Endpoint:** `POST /v1/me/organization` **Access:** Account required ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/me/organization" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "organization_id": "0192a5e0-1234-7000-8000-000000000001" }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Request body | Field | Type | Description | |---|---|---| | `organization_id` *(required)* | `string (uuid)` | — | ```json { "organization_id": "0192a5e0-1234-7000-8000-000000000001" } ``` ## Responses ### 200 OK Updated. ```json { "authenticated": true, "credential": "anonymous", "current_organization_id": "0192a5e0-1234-7000-8000-000000000001", "environment": "live", "guest": { "active": true, "application_ids": [] }, "is_admin": true, "organizations": [ { "id": "0192a5e0-1234-7000-8000-000000000001", "name": "Acme Inc.", "role": "owner" } ], "scopes": [ "applications:read" ], "user": { "email": "alex@acme.com", "id": "0192a5e0-1234-7000-8000-000000000001", "name": "Acme Inc." } } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Meta > Pricing, steps and live/mocked integrations **Endpoint:** `GET /v1/meta` **Access:** Public (no credentials needed) **MCP tool:** `get_started` ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/meta" ``` ## Responses ### 200 OK Service metadata. ```json { "environment_default": "live", "links": { "docs": "string", "get_started": "string", "mcp": "string", "openapi": "string", "trademark_service_email": "string" }, "pricing": { "currency": "usd", "issuer": "string", "issuer_fee_estimate_cents": { "cmc": 1, "vmc": 1 }, "issuer_fee_range_cents": { "max": 1, "min": 1 }, "per": "string", "plans": [], "refund_policy": "string" }, "providers": {}, "steps": [ { "paid": true, "phase": "check", "slug": "domain", "title": "string" } ], "version": "string" } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # List Notifications > Notification log **Endpoint:** `GET /v1/notifications` **Access:** Account required ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/notifications" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Notifications. ```json { "data": [ { "application_id": "0192a5e0-1234-7000-8000-000000000001", "created_at": "2026-10-31T12:00:00Z", "domain": "acme.com", "id": "0192a5e0-1234-7000-8000-000000000001", "kind": "string", "recipient": "string", "sent_at": "2026-10-31T12:00:00Z", "status": "pending", "subject": "string" } ], "next_cursor": "string" } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # List Orders > Orders and payments **Endpoint:** `GET /v1/orders` **Access:** Account required **MCP tool:** `get_account`, `list_orders` ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/orders" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Orders. ```json { "data": [ { "application_id": "0192a5e0-1234-7000-8000-000000000001", "created_at": "2026-10-31T12:00:00Z", "currency": "usd", "domain": "acme.com", "environment": "live", "expires_at": "2026-10-31T12:00:00Z", "id": "0192a5e0-1234-7000-8000-000000000001", "issuer_fee_cents": 149900, "line_items": [], "organization_id": "0192a5e0-1234-7000-8000-000000000001", "paid_at": "2026-10-31T12:00:00Z", "payments_mode": "stripe", "plan_price_cents": 149900, "product": "vmc", "refund_reason": "string", "refunded_at": "2026-10-31T12:00:00Z", "status": "checkout_open", "total_cents": 149900 } ], "next_cursor": "string" } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Order > One order **Endpoint:** `GET /v1/orders/{order_id}` **Access:** Account required **MCP tool:** `list_orders` ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/orders/0192a5e0-1234-7000-8000-000000000003" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `order_id` (path, required) | `string (uuid)` | Order id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Order. ```json { "application_id": "0192a5e0-1234-7000-8000-000000000001", "created_at": "2026-10-31T12:00:00Z", "currency": "usd", "domain": "acme.com", "environment": "live", "expires_at": "2026-10-31T12:00:00Z", "id": "0192a5e0-1234-7000-8000-000000000001", "issuer_fee_cents": 149900, "line_items": [ { "amount_cents": 149900, "kind": "plan", "label": "string" } ], "organization_id": "0192a5e0-1234-7000-8000-000000000001", "paid_at": "2026-10-31T12:00:00Z", "payments_mode": "stripe", "plan_price_cents": 149900, "product": "vmc", "refund_reason": "string", "refunded_at": "2026-10-31T12:00:00Z", "status": "checkout_open", "total_cents": 149900 } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # List Organizations > My organizations **Endpoint:** `GET /v1/organizations` **Access:** Account required ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/organizations" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Organizations. ```json { "data": [ { "applications_count": 1, "created_at": "2026-10-31T12:00:00Z", "id": "0192a5e0-1234-7000-8000-000000000001", "members_count": 1, "name": "Acme Inc.", "role": "owner" } ], "next_cursor": "string" } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Create Organization > Create an organization **Endpoint:** `POST /v1/organizations` **Access:** Account required ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/organizations" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Acme Inc." }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Request body | Field | Type | Description | |---|---|---| | `name` *(required)* | `string` | — | ```json { "name": "Acme Inc." } ``` ## Responses ### 201 OK Created. ```json { "applications_count": 1, "created_at": "2026-10-31T12:00:00Z", "id": "0192a5e0-1234-7000-8000-000000000001", "members_count": 1, "name": "Acme Inc.", "role": "owner" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Get Organization > One organization **Endpoint:** `GET /v1/organizations/{organization_id}` **Access:** Account required ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/organizations/0192a5e0-1234-7000-8000-000000000002" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `organization_id` (path, required) | `string (uuid)` | Organization id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Organization. ```json { "applications_count": 1, "created_at": "2026-10-31T12:00:00Z", "id": "0192a5e0-1234-7000-8000-000000000001", "members_count": 1, "name": "Acme Inc.", "role": "owner" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Rename Organization > Rename an organization **Endpoint:** `PATCH /v1/organizations/{organization_id}` **Access:** Account required ## cURL example ```bash curl -X PATCH "https://api.verifiedinbox.ai/v1/organizations/0192a5e0-1234-7000-8000-000000000002" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Acme Inc." }' ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `organization_id` (path, required) | `string (uuid)` | Organization id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Request body | Field | Type | Description | |---|---|---| | `name` *(required)* | `string` | — | ```json { "name": "Acme Inc." } ``` ## Responses ### 200 OK Renamed. ```json { "applications_count": 1, "created_at": "2026-10-31T12:00:00Z", "id": "0192a5e0-1234-7000-8000-000000000001", "members_count": 1, "name": "Acme Inc.", "role": "owner" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # List Members > Organization members **Endpoint:** `GET /v1/organizations/{organization_id}/members` **Access:** Account required ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/organizations/0192a5e0-1234-7000-8000-000000000002/members" \ -H "Authorization: Bearer vi_live_YOUR_API_KEY" ``` ## Parameters | Parameter | Type | Description | |---|---|---| | `organization_id` (path, required) | `string (uuid)` | Organization id. | | `VI-Environment` (header) | `string` | `live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment. | ## Responses ### 200 OK Members. ```json { "data": [ { "created_at": "2026-10-31T12:00:00Z", "email": "alex@acme.com", "name": "Acme Inc.", "role": "owner", "user_id": "0192a5e0-1234-7000-8000-000000000001" } ], "next_cursor": "string" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 401 (unauthenticated) `unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`. ```json { "error": { "code": "unauthenticated", "message": "Sign in or provide an API key to continue.", "next_action": { "kind": "sign_in", "title": "Sign in or create an account", "url": "/login" } } } ``` ### 404 (not_found) `not_found` — doesn't exist or isn't accessible to you (never 403). ```json { "error": { "code": "not_found", "message": "We couldn't find that resource." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # List Blocked Providers > Shared mailbox domains that can't be used **Endpoint:** `GET /v1/providers/blocked` **Access:** Public (no credentials needed) ## cURL example ```bash curl "https://api.verifiedinbox.ai/v1/providers/blocked" ``` ## Responses ### 200 OK Blocked domains and why. ```json { "data": [ { "category": "free_mailbox", "domain": "acme.com", "provider": "string" } ], "reason": "Updated company logo" } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Resume > Exchange a one-time resume token **Endpoint:** `POST /v1/resume` **Access:** Public (no credentials needed) Unclaimed drafts get a fresh guest credential (+ cookie). Tokens expire after 24h and work once; otherwise 410 `resume_link_expired`. ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/resume" \ -H "Content-Type: application/json" \ -d '{ "token": "string" }' ``` ## Request body | Field | Type | Description | |---|---|---| | `token` *(required)* | `string` | One-time `rt_…` token from a resume link. | ```json { "token": "string" } ``` ## Responses ### 200 OK Where to continue. ```json { "application_id": "0192a5e0-1234-7000-8000-000000000001", "guest_token": "string", "requires_sign_in": true, "step": "domain" } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Create Support Request > Contact a human, feedback, feature request, partnership **Endpoint:** `POST /v1/support` **Access:** Public (no credentials needed) **MCP tool:** `contact_support`, `send_feedback` Works anonymously (include `email` so a human can reply). ## cURL example ```bash curl -X POST "https://api.verifiedinbox.ai/v1/support" \ -H "Content-Type: application/json" \ -d '{ "kind": "contact_human", "message": "Everything checks out." }' ``` ## Request body | Field | Type | Description | |---|---|---| | `application_id` | `string (uuid)` | Link the request to this application (only if you can access it). | | `company` | `string` | — | | `email` | `string (email)` | Required for anonymous requests that need a reply. | | `kind` *(required)* | `string` | — | | `message` *(required)* | `string` | The question or feedback, in the user's words. | | `name` | `string` | The sender's name. | ```json { "kind": "contact_human", "message": "Everything checks out." } ``` ## Responses ### 201 OK Received. ```json { "id": "0192a5e0-1234-7000-8000-000000000001", "kind": "contact_human", "message": "Everything checks out." } ``` ### 400 (invalid_request) `invalid_request` — the request is malformed. ```json { "error": { "code": "invalid_request", "message": "Please provide a valid domain name." } } ``` ### 422 (unprocessable) `unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason). ```json { "error": { "code": "unprocessable", "message": "Shared mailbox providers like gmail.com cannot be verified." } } ``` ### 429 (rate_limited) `rate_limited` — retry after `Retry-After` seconds (`details.retry_after_seconds`). `limit_reached` — a quota is used up; `details.limit` names it and `next_action` offers support. ```json { "error": { "code": "rate_limited", "message": "Too many requests. Please wait a moment and try again." } } ``` --- # Initial release > November 2026 web release notes. 🎉 --- # Initial release > November 2026 API release notes. 🎉 --- # Initial release > November 2026 MCP release notes. 🎉 --- # Terms of Service > The agreement between your company and VerifiedInbox, in plain English. *Last updated: October 19, 2026* These terms are an agreement between VerifiedInbox ("we", "us") and the company or person using our website, API or MCP server ("you"). By using VerifiedInbox, you agree to them. If you use VerifiedInbox for a company, you confirm you're allowed to accept these terms for it. ## 1. What we do VerifiedInbox helps companies show their logo, and where eligible Gmail's blue verified checkmark, next to the emails they send. We check domains and email settings, prepare logo files, look up trademarks, verify businesses, order mark certificates from a certificate issuer, provide DNS settings, host logos and certificates, monitor the setup and renew certificates. The free checks are available without an account. Paid services require an account and an active plan. ## 2. Accounts - Keep your login and API keys secure. You're responsible for activity under your account and keys. - Give us accurate information and keep it up to date. - You can belong to several organizations; organization owners and admins control who has access to its domains, keys and billing. ## 3. Payments, renewals and refunds - Plans are priced **per domain, per year**, and renew automatically each year until cancelled. - The **certificate issuer's fee** is charged by the issuer and collected by us at checkout as a separate line, without markup. Issuer pricing can change; we show the estimated total before you pay, and before each renewal. - Payments are processed by Stripe. - **If the certificate issuer rejects your application, we refund the plan and the issuer fee in full.** - **Cancellation:** you can cancel anytime. Your plan won't renew, and your hosted logo, certificate and monitoring stay active until the end of the period you paid for. Except as stated above, payments are non-refundable. ## 4. Your responsibilities - **Authority.** You confirm you are authorized to act for the company and the domain you submit. - **Accurate information.** Business details, applicant details and evidence you provide must be true and complete. - **Rights to your logo.** You confirm you own the logo or have the right to use it, including any trademark you rely on for a Verified Mark Certificate. - **Your domain and DNS.** You make DNS changes yourself (or through your IT). We give instructions but don't change your DNS in this release. - **Your email.** You remain responsible for the email you send and for complying with the laws that apply to it. ## 5. The certificate issuer Certificates are issued by an independent certificate issuer (currently DigiCert), under the issuer's own terms, policies and validation requirements. The issuer decides whether to issue, may contact you directly, and may revoke a certificate under its policies. We prepare and submit your application and manage it on your behalf, but we don't control the issuer's decisions. ## 6. What we can't promise Mailbox providers such as Gmail decide when and where to show logos and checkmarks. We make sure your setup is correct and keep it that way, but **we can't guarantee that a logo or checkmark will be displayed** in any particular inbox. Trademark searches and eligibility results are informational and are not legal advice. ## 7. Acceptable use Don't use VerifiedInbox to impersonate another company, submit a logo or trademark you don't have rights to, send spam or phishing, probe or overload our systems, get around rate limits, or access data that isn't yours. We may suspend accounts that do. ## 8. AI agents acting for you You may let AI assistants use VerifiedInbox for you, through the MCP server, the API or API keys. Actions an agent takes with your credentials or authorization are treated as your actions. By design, agents can't pay or change DNS for you, and sensitive actions (such as revoking a certificate) require explicit confirmation. Only give agents the access they need, and revoke keys you no longer use. ## 9. Test mode Test mode is for trying the service. Nothing is charged, certificates there are simulated and not trusted by any inbox, and test data may be reset. ## 10. Changes and termination We may update the service and these terms. If a change materially affects you, we'll tell you in advance by email or in the app. You can stop using VerifiedInbox anytime. We may suspend or end access for breach of these terms; if we end a paid plan without cause, we'll refund the unused part. ## 11. Liability The service is provided "as is". To the extent the law allows, we aren't liable for indirect or consequential losses (such as lost profits or lost email), and our total liability is limited to the amount you paid us in the 12 months before the claim. ## 12. Governing law These terms are governed by the laws of the State of Delaware, USA. Disputes will be handled by the state or federal courts in Delaware. ## 13. Contact Questions about these terms: [support@verifiedinbox.ai](mailto:support@verifiedinbox.ai). --- # Privacy Policy > What we collect, why we collect it, who helps us process it, and how long we keep it. *Last updated: November 1, 2026* VerifiedInbox is a business service. This policy explains what information we handle when your company uses our website, API or MCP server, and why. ## What we collect - **Account information** — your name, work email, organization, role and sign-in details. - **Domain information** — the domains you check and their public DNS records (SPF, DKIM, DMARC, BIMI, MX, nameservers), website information, and public web-archive history. - **Logos** — files you upload or that we find on your website, the converted versions, and their analysis. - **Business details** — legal company name, registration number, address, and the applicant's name, title, email and phone, plus trademark records we look up. - **Payment information** — handled by Stripe. We see the payment status and basic billing details, not your full card number. - **Messages** — support requests, feedback, and test emails you send to our test address (headers and authentication results). - **Ask AI conversations** (signed-in users): your questions, the assistant's answers, the actions you confirm in the chat, and the IP address each of your messages was sent from. - **Usage information** — logs of requests to our website, API and MCP server (IP address, browser or client, actions taken), used for security, rate limiting and improving the service. ## Why we use it - To provide the service: run checks, prepare your application, order and manage certificates, host your logo and certificate, monitor your setup and send renewal reminders. - To verify your business and your authority, as the certificate issuer requires. - To process payments and refunds. - To answer support requests and keep you informed about your domains. - To answer your questions in Ask AI and carry out the actions you confirm there. - To secure the service, prevent abuse and improve the product. We don't sell your information. ## IP addresses in Ask AI We record the IP address each Ask AI message is sent from. We use it only to prevent fraud and abuse of the assistant, for example to enforce usage limits and to investigate misuse. This is in our legitimate interest in keeping the service safe for every customer (Article 6(1)(f) GDPR), so we don't ask for separate consent. We don't use these IP addresses for advertising or profiling, and only VerifiedInbox staff can see them. ## Sub-processors We use these service providers to run VerifiedInbox: | Provider | Purpose | |---|---| | Railway | Hosting of our application, database and files | | WorkOS | Sign-in and organization management | | Stripe | Payments and billing | | DigiCert | Certificate validation and issuance | | Postmark | Sending email (notifications and reminders) and receiving test emails | | Browserbase | Automated browsing for public trademark and business-registry lookups | | Anthropic | AI analysis, such as comparing your logo with trademark records, and Ask AI answers | | OpenCorporates | Company registry lookups | Public information about your domain, logo and trademarks is also checked against public sources such as DNS, the US Patent and Trademark Office and the Internet Archive. ## Certificates are public Mark certificates are public by design: they include your company name, domain and logo, and the certificate issuer may publish them in public certificate logs. Your BIMI DNS record and hosted logo are publicly accessible so that inboxes can display them. ## Retention - Account, application, certificate and billing records: for as long as your account is active, and afterwards as needed for legal, tax and audit purposes. - Anonymous drafts (no account): deleted after the guest session expires, at most about 90 days. - Ask AI conversations, including the IP address of each message: kept with your account. - Logs: kept for a limited period for security and troubleshooting. You can ask us to delete your account and data; we'll do so unless we must keep certain records by law. ## Security We protect information with encryption in transit, access controls, tenant isolation between organizations, and scoped API keys. No system is perfectly secure; tell us right away if you suspect a problem. ## Your choices You can view and update your account information in the dashboard, revoke API keys at any time, and contact us to access, correct, export or delete your information. ## Contact Privacy questions: [support@verifiedinbox.ai](mailto:support@verifiedinbox.ai).