API Reference · Applications

Complete Step

Complete a wizard step and move on

POST/v1/applications/{application_id}/steps/{step}/complete
Free (guest token or account)MCP: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

cURL request
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

ParameterTypeDescription
application_idpathrequired
string (uuid)Application id.
steppathrequired
stringWizard step, e.g. `domain`, `email-security`, `dns`.
VI-Environmentheader
string`live` (default) or `test`. Used for anonymous callers and OAuth access tokens; API keys and web sessions carry their own environment.

Response

200 OKThe application after completing the step.
application/json
FieldTypeDescription
backgroundrequired
BackgroundWork[]

Active long-running work plus anything finished in the last 10 minutes.

Nested fields (6)
  • kind (string)
  • message (string): Plain-English description.
  • next_run_at (string (date-time))
  • state (string)
  • status (string)
  • updated_at (string (date-time))
brand_namerequired
string

Display-name guess (website title or domain label).

contact_emailrequired
string | null—
created_atrequired
string (date-time)—
current_steprequired
string

Where to work now: the first step needing attention, else the first step the user hasn't completed.

Allowed: domain, email-security, logo, trademark, eligibility, checkout, business, logo-approval, dns, certificate, go-live

domainrequired
string

Normalized ASCII (punycode) domain.

domain_displayrequired
string

Unicode form of the domain.

domain_statusrequired
string

Whether the domain is registered and can have its own logo (`unknown` while checking).

Allowed: unknown, ok, not_registered, blocked_provider

email_usagerequired
string | null

How email is used on the domain, as told by the customer (null until answered).

Allowed: none, low, active

environmentrequired
string—

Allowed: live, test

idrequired
string (uuid)—
next_actionsrequired
NextAction[]

What to do next (for people and agents).

Nested fields (9)
  • api (ApiRef)
  • description (string)
  • kind (string)
  • mcp_tool (string)
  • requires_auth (boolean)
  • requires_payment (boolean)
  • step (string)
  • title (string)
  • url (string): Web URL when the action happens in the browser (e.g. sign in).
organizational_domainrequired
string

Registrable domain (e.g. `acme.com` for `news.acme.com`).

outcomesrequired
Outcomes

Separate outcomes — never collapsed into one "verified" flag.

Nested fields (10)
  • bimi (string)
  • business (string)
  • certificate (string)
  • dmarc_rollout (string)
  • eligibility (string)
  • inbox_display (string)
  • logo (string)
  • ownership (string)
  • payment (string)
  • trademark (string)
ownerrequired
ApplicationOwner—
productrequired
string | null

Chosen product, else the recommended one.

Allowed: vmc, cmc

product_selectedrequired
string | null

Product explicitly chosen by the customer.

Allowed: vmc, cmc

recommended_productrequired
string | null

From eligibility; null until assessed.

Allowed: vmc, cmc

stepsrequired
Step[]

The 11 wizard steps, in order.

Nested fields (8)
  • completed_at (string (date-time)): When the user completed the step (null if not yet).
  • paid (boolean)
  • phase (string)
  • requirements (StepRequirement[]): The minimum this step needs (required) plus recommended extras.
  • slug (string)
  • status (string): `complete`: the user finished it. `attention`: finished earlier, but something it relied on is now missing or broken. `ready`: the current step has everything it needs; complete it with `completeStep`. `action_required` / `in_progress` / `blocked`: the current step isn't ready. `locked`: comes after the current step.
  • summary (string): One plain-English sentence.
  • title (string)
updated_atrequired
string (date-time)—
versionrequired
integer

Optimistic-concurrency version; send as `If-Match`.

web_urlrequired
string

Deep link to the current step in the web app.

Example 200 response (Application)
{
  "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"
}

Other responses & possible errors

Expand a status code below to see the error code, description, and example error payload.

400 (invalid_request)`invalid_request` — the request is malformed.

`invalid_request` — the request is malformed.

Example 400 error response
{
  "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`.

`unauthenticated` — sign in / send a valid credential. `next_action.kind = sign_in`.

Example 401 error response
{
  "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).

`not_found` — doesn't exist or isn't accessible to you (never 403).

Example 404 error response
{
  "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`).

Not ready (`step_not_ready`, `details.missing`) or not available yet (`step_locked`).

Example 409 error response
{
  "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).

`unprocessable` / `blocked_provider` — understood but can't be done (plain-English reason).

Example 422 error response
{
  "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.

`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.

Example 429 error response
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Please wait a moment and try again."
  }
}
Was it helpful?