Browse docs

Async Deep Scans

Use async scans, polling, and webhooks for deep image and PDF review.

Goal

Run deeper scans without blocking a user request.

Async scans are for image and PDF workflows where depth matters more than immediate completion.

Requirements

  • async=true
  • mode=comprehensive
  • content_type=image or content_type=pdf
  • Optional webhook_url

Architecture

  1. Submit the image or PDF with async=true.
  2. Store the returned scan_id.
  3. Show a pending review state to the user or workflow.
  4. Receive the webhook or poll GET /v1/scan/{scan_id}.
  5. Route the final result.

Submit An Async Scan

curl -X POST https://gateway.trymighty.ai/v1/scan \
  -H "Authorization: Bearer $MIGHTY_API_KEY" \
  -F "file=@./claim-packet.pdf" \
  -F "content_type=pdf" \
  -F "scan_phase=input" \
  -F "mode=comprehensive" \
  -F "focus=all" \
  -F "async=true" \
  -F "webhook_url=https://example.com/api/mighty/webhook"

Poll For Completion

curl https://gateway.trymighty.ai/v1/scan/c178225b-1ee2-4c60-bab3-41f1ad32d532 \
  -H "Authorization: Bearer $MIGHTY_API_KEY"

preliminary: true on the initial response means the heuristic verdict is in but the deep scan is still running — do not treat preliminary as final. Each item in threats is an object with category, confidence, an optional evidence excerpt, and a human-readable reason.

Webhook Handler Shape

export async function POST(request: Request) {
  const scan = await request.json();

  await saveMightyScanResult({
    scanId: scan.scan_id,
    scanGroupId: scan.scan_group_id,
    action: scan.action,
    riskScore: scan.risk_score,
    status: scan.scan_status,
    threats: scan.threats ?? [],
  });

  await routeFinalScanResult(scan);

  return Response.json({ ok: true });
}

Validate webhook authenticity according to your deployment policy. At minimum, use HTTPS, strict routing, request logs, replay protection, and a secret value in your own webhook path or headers when available.

Routing Logic

export function routeAsyncStatus(scan: { scan_status?: string; action?: string }) {
  if (scan.scan_status === "pending") return "keep_pending";
  if (scan.scan_status === "failed") return "manual_review";
  if (scan.action === "ALLOW") return "continue";
  if (scan.action === "WARN") return "manual_review";
  return "stop";
}

Common Mistakes

  • Setting async=true with mode=secure. Async requires mode=comprehensive.
  • Using async for text. Async deep scan is for image and PDF.
  • Letting the workflow continue before final routing.
  • Treating a preliminary response as the final answer.

Production Checklist

  • Store pending state with scan_id.
  • Retry polling with backoff.
  • Make webhook handling idempotent.
  • Re-route if the final result differs from the preliminary route.
  • Add monitoring for stuck pending scans.
  • Use manual review as the safe fallback for failed deep scans.
Next step

Ready to scan real traffic?

Create an API key, keep it on your server, then wire Mighty into the workflow that handles untrusted material.

AI-Agent Prompt

AI-ready prompt
Add async deep scans

Paste this into Cursor, Codex, Claude Code, or Windsurf.

Add Mighty async deep scans for high-risk PDF or image workflows.

Requirements:
- Submit file scans with async=true and mode=comprehensive.
- Only use async for content_type=image or content_type=pdf.
- Store scan_id, scan_group_id, preliminary, scan_status, and action.
- Add a pending state in the workflow.
- Implement GET /v1/scan/{scan_id} polling with backoff.
- Implement webhook receiving if the app has a public HTTPS endpoint.
- Make webhook updates idempotent.
- Route complete ALLOW, WARN, BLOCK.
- Route failed or stuck pending scans to manual review.

Acceptance criteria:
- Tests cover pending, complete, failed, webhook duplicate, and polling retry.
- The workflow does not treat preliminary as final.
- Logs include scan_id and request_id.