Handling review and avoid from InboxPolicy in a Signup Workflow

By , founder of InboxPolicy · Updated July 20, 2026

In a signup workflow, use InboxPolicy's action as a send-routing signal, not proof that an account or mailbox is safe. Send on send; apply your own risk gate on send_with_caution; hold review; queue retry_later; block avoid. A review result means unresolved evidence, not permission to send.

The five actions and what to do at signup

The API's exact five-action enum is send, send_with_caution, review, retry_later, and avoid. The signup actions in the right column below are implementation recommendations — the API only returns the action; the API does not manage signup state, account activation, or automatic blocking.

API actionMeaning (grounded in repo)Recommended signup action
send Final policy branch: "Verified mailbox with acceptable risk." Reached only after earlier invalid, temporary, disposable, unknown, catch-all, strict-role, and low-confidence checks do not apply. Implementation recommendation: send the signup verification email and continue the normal pending-verification flow. Do not treat this alone as proof of identity or grant high-trust access.
send_with_caution Either an aggressive-policy catch-all ("Catch-all mailbox detected.") or confidence below 70 ("Mailbox verified with moderate confidence."). Implementation recommendation: send only if the product's own risk budget allows it; flag or rate-limit the signup and keep normal email confirmation requirements.
review status === "unknown": "Mailbox could not be verified confidently"; default-policy catch-all: "Catch-all mailbox detected. Review before sending"; strict-policy role account: "Role-based address detected under strict policy". Implementation recommendation: hold the signup email; place the address in a review queue for a human or a separately defined secondary check. Keep signup pending; do not silently send.
retry_later status === "temporary_unavailable": "Temporary verification issue. Retry later." Implementation recommendation: keep signup pending and queue a retry. Use retry_at when present; the repo does not define a client backoff schedule, so do not invent one.
avoid status === "invalid" or "syntax_error": "Mailbox is invalid or malformed"; or disposable === true: "Disposable email detected". Local syntax failures are free, return an avoid result, and make no provider call. Implementation recommendation: do not send the signup verification email. Ask the user to correct the address or choose an alternate verification path; do not equate this action with a universal account-ban rule.

Precedence that matters

The policy is ordered. Invalid/syntax and temporary-unavailable are checked first, then disposable, unknown, catch-all, strict role-account, and finally confidence. Therefore, "confidence below 70 means caution" is not a universal override: an invalid or disposable result becomes avoid first, and an unknown result becomes review first.

Worked example: routing review and avoid

The JSON keys below are the real /v1/decide response shape from the route. The values are deterministic repo-grounded fixtures — a valid catch-all with confidence 60 follows the catch-all/default policy branch, while a provider result: "disposable" normalizes to an avoid action. These are not live API captures.

Case A — catch-all, default strictness:

{
  "request_id": "vrf_...",
  "email": "[email protected]",
  "action": "review",
  "reason": "Catch-all mailbox detected. Review before sending.",
  "confidence": 60,
  "status": "valid",
  "retry_at": null,
  "signals": {
    "catch_all": true,
    "disposable": false,
    "free": false,
    "role_account": false
  },
  "from_cache": false,
  "cost": {
    "credits": 1,
    "billable": true
  }
}

Case B — disposable provider result:

{
  "request_id": "vrf_...",
  "email": "[email protected]",
  "action": "avoid",
  "reason": "Disposable email detected.",
  "confidence": 0,
  "status": "unknown",
  "retry_at": null,
  "signals": {
    "catch_all": false,
    "disposable": true,
    "free": false,
    "role_account": false
  },
  "from_cache": false,
  "cost": {
    "credits": 1,
    "billable": true
  }
}

Routing logic (implementation recommendation):

response = POST /v1/decide({ email, strictness: "default" })

if response.action == "review":
    keep_signup_pending()
    enqueue_manual_or_secondary_review(response.email, response.reason, response.signals)
elif response.action == "avoid":
    do_not_send_verification_email()
    ask_for_correction_or_alternate_verification()

If the application needs deliverable, reachable, MX context, or the evidence object, call /v1/verify (or the MCP verify_email tool) rather than assuming /v1/decide contains those fields.

What POST /v1/decide actually returns (and what it doesn't)

The route builds a 200 response with exactly these application fields: request_id, email, action, reason, confidence, status, retry_at, signals.catch_all, signals.disposable, signals.free, signals.role_account, from_cache, and cost. The OpenAPI schema defines confidence as an integer from 0 through 100.

Important distinction: /v1/decide does not copy the evidence, deliverable, reachable, or mx_hosts fields into its response body; it exposes the four normalized signal booleans above. If you need the full verification shape, /v1/verify returns { request_id, ...result } including status, deliverable, reachable, catch_all, disposable, free, role_account, confidence, esg_provider, mx_hosts, recommended_action, recommended_retry_at, note, evidence, provider metadata, and cost.

The provider normalizer reads nested raw.smtp for SMTP deliverable and catch_all, reads reachable from the provider response, and normalizes MX hosts. Its returned evidence object is explicitly { provider, from_cache }, with fallback-chain fields added by the waterfall when applicable. The API response does not include a raw SMTP transcript or server reply code.

Confidence is not calibrated

Confidence is a bounded integer field clamped to 0–100 by normalization; the API does not define confidence as a calibrated probability. Do not rewrite confidence: 60 as "60% safe," "60% deliverable," or any probability. A cache hit within 72 hours still returns the cached decision; it is not a fresh confidence calibration. For the broader pre-send playbook that routes by these same five actions, see how to reduce email bounce rate.

Catch-all is honestly unresolved

SMTP RCPT TO acceptance on a catch-all domain means the domain did not reject at the edge, not that the named mailbox exists. The default policy maps catch-all to review so the address is held rather than silently treated as safe. Do not claim review means safe, and do not claim catch-all always bounces — the existing site guide explicitly rejects both blanket rules. For the SMTP limitation and evidence-based routing, see are catch-all emails safe to send to; for the deeper catch-all verification mechanics, see catch-all email verification.

Honest limits / non-claims

Related guides

For the broader pre-send playbook that routes by the five actions (send catch-all to review, suppress hard failures, requeue greylisted results), see how to reduce email bounce rate. For why catch-all SMTP acceptance is not mailbox confirmation, see are catch-all emails safe to send to. For the deeper catch-all verification mechanics and the distinction between domain acceptance and mailbox confirmation, see catch-all email verification. For the canonical REST and MCP request/response contract (/v1/decide, /v1/verify, cache, idempotency, MCP tools), see the docs.

Frequently asked questions

How do I handle review and avoid responses from InboxPolicy in a signup workflow?

Use InboxPolicy's action as a send-routing signal, not proof that an account or mailbox is safe. Send on send, apply your own risk gate on send_with_caution, hold review, queue retry_later, and block avoid. A review result means unresolved evidence, not permission to send. The signup actions are implementation recommendations; the API only returns the action.

What does it mean when InboxPolicy returns review?

review means the engine could not verify the mailbox confidently. It is returned when status is unknown ("Mailbox could not be verified confidently"), when a default-policy catch-all is detected ("Catch-all mailbox detected. Review before sending"), or for a role-based address under strict policy. In a signup workflow the implementation recommendation is to hold the signup email, place the address in a review queue for a human or secondary check, and keep signup pending rather than silently sending.

What does avoid mean in InboxPolicy email verification?

avoid means the address is invalid, malformed, or disposable. It is returned when status is invalid or syntax_error ("Mailbox is invalid or malformed"), or when disposable is true ("Disposable email detected"). Local syntax failures are free, return an avoid result, and make no provider call. The implementation recommendation for signup is to not send the verification email, and to ask the user to correct the address or choose an alternate verification path; do not equate this with a universal account-ban rule.

Should I send a signup verification email when InboxPolicy returns review?

No. review is unresolved evidence, not permission to send. The implementation recommendation is to hold the signup email and place the address in a review queue. A default-policy catch-all maps to review specifically because SMTP RCPT TO acceptance on a catch-all domain does not confirm the specific mailbox exists.

How do I route InboxPolicy review, retry_later, and avoid verdicts?

Branch on the action field: hold review, queue retry_later using retry_at when present (the repo does not define a client backoff schedule, so do not invent one), and block avoid by asking for correction or an alternate verification path. send proceeds the normal pending-verification flow; send_with_caution applies your own risk gate. Confidence is a bounded integer field, not a calibrated probability.

What fields does POST /v1/decide return for routing a signup?

The route returns request_id, email, action, reason, confidence, status, retry_at, signals.catch_all, signals.disposable, signals.free, signals.role_account, from_cache, and cost. It does not copy the evidence, deliverable, reachable, or mx_hosts fields into its response body. If you need those, call /v1/verify or the MCP verify_email tool instead of assuming /v1/decide contains them.

Is InboxPolicy's confidence score a calibrated probability or deliverability percentage?

No. The repo defines confidence as a bounded integer field clamped to 0 through 100 by normalization, with no calibration methodology provided. Do not rewrite confidence: 60 as "60% safe," "60% deliverable," or any probability without separate calibration evidence.

Is a catch-all mailbox safe to send to during signup?

Not automatically. A catch-all result is honestly unresolved: RCPT TO acceptance means the domain did not reject at the edge, not that the named mailbox exists. The default policy maps catch-all to review so the address is held rather than silently treated as safe. Do not claim review means safe, and do not claim catch-all always bounces.

Get started, pay per call, no signup →