Your first verification

You have an account and a vai_ API key. This page sends your very first image through POST /v1/verify and then reads the response back, one field at a time, so you know exactly what each value means before you wire it into your app.

Before you start

You need a vai_ API key (see Authentication) and one photo — JPEG, PNG, or WebP, under 10 MB. We use the built-in scooter_parking policy here; swap in any policy ID you have access to. If you still need a key, the Quickstart gets you one in about five minutes.

1. Send the image

A verification request needs exactly two things: the image and a policy ID. The endpoint accepts multipart/form-data (best for camera uploads) or JSON with a base64-encoded image. We'll use multipart — it's the simplest from a terminal.

curl -X POST https://verify.switchlabs.dev/api/v1/verify \
-H "X-API-Key: vai_your_api_key" \
-F "image=@scooter.jpg" \
-F "policy=scooter_parking"

If the key is valid and the policy exists, you get a 200 OK with a JSON body. Anything else is an error — jump to reading errors below.

2. Read the response

Here's a representative response for a photo that failed the policy:

json
{
  "id": "ver_8x92m4k9",
  "created_at": "2026-05-12T14:30:00Z",
  "status": "success",
  "is_compliant": false,
  "confidence": 0.94,
  "policy": "scooter_parking",
  "category": "unsafe",
  "violation_reasons": ["blocking_sidewalk", "kickstand_up"],
  "feedback": "Please deploy the kickstand and move away from the walkway.",
  "metadata": {},
  "image_url": "https://...signed-url...",
  "evaluation_source": "cloud_vlm"
}

Now walk it field by field.

status

The first thing to check. success means the verification ran to completion and the rest of the fields are meaningful. error means processing failed — treat the result as unknown and surface a retry, not a pass or fail.

is_compliant

The top-level decision. This is the boolean almost all branching logic keys off:

if (result.is_compliant) {
// end the ride / accept the inspection
} else {
// ask the user to retake or fix the issue
}

is_compliant is simply the isCompliant flag of whatever category the model landed on — so it's already a severity-weighted decision, not a raw confidence threshold.

category

The outcome bucket. While is_compliant is a single boolean, the category tells you why — useful for routing. For the built-in scooter_parking policy:

| category | is_compliant | What it means | | ------------ | -------------- | --------------------------------------------------- | | compliant | true | Every criterion passed. | | improvable | false | Minor (warning-level) issues; consider a retake. | | unsafe | false | A critical rule failed — block the action. | | lacks_info | false | The photo doesn't show enough to decide. |

Custom policies define their own categories — see Concepts: Categories.

violation_reasons

A list of the stable criterion IDs that failed. In the example, blocking_sidewalk and kickstand_up failed. These IDs are stable across calls, so they're ideal for analytics ("how often does each failure happen?") and targeted branching:

js
if (result.violation_reasons.includes("blocking_sidewalk")) {
  // show a sidewalk-specific reminder
}

When is_compliant is true, violation_reasons is an empty array. See Concepts: Criteria for how each criterion is defined and rolled up.

feedback

A single user-facing sentence written by the model, derived from the failed criteria and the policy's tone. It's safe to display verbatim — the SDK scanners show it on the result screen by default. In the example: "Please deploy the kickstand and move away from the walkway."

confidence

The model's self-reported confidence, from 0 to 1. Don't gate your pass/fail on this number — the category decision already weighs severity across criteria. Confidence is most useful for monitoring: flag low-confidence calls for human review rather than auto-rejecting them.

metadata

Whatever you sent in the request, echoed back. Send a metadata object to thread your own identifiers through:

bash
curl -X POST https://verify.switchlabs.dev/api/v1/verify \
  -H "X-API-Key: vai_your_api_key" \
  -F "image=@scooter.jpg" \
  -F "policy=scooter_parking" \
  -F 'metadata={"user_id":"usr_123","trip_id":"trip_456"}'

It comes straight back on the response and is searchable in the dashboard. Send only the identifiers you need — avoid putting PII you don't have to store.

image_url and evaluation_source

image_url is a time-limited signed URL to the stored image, or null if image storage is disabled for your account. evaluation_source is cloud_vlm when the server ran the VLM, or on_device when the SDK ran inference locally and submitted a confirmation.

If something went wrong

Errors come back with a non-200 status and an error string instead of a verification object. The most common ones when getting started:

| Status | Meaning | | ------ | ---------------------------------------------------------------------- | | 400 | Missing image or policy, image over 10 MB, or an unknown policy ID. | | 401 | Missing or invalid API key. | | 403 | Key isn't valid for VerifyAI, or there's no active subscription. | | 429 | Rate limit or monthly usage cap reached — check Retry-After. |

Every response — success or error — carries an X-Request-Id header. Grab it from the response headers and include it when you contact support:

bash
curl -i -X POST https://verify.switchlabs.dev/api/v1/verify \
  -H "X-API-Key: vai_your_api_key" \
  -F "image=@scooter.jpg" -F "policy=scooter_parking" | grep -i x-request-id

What's next

  • Verify endpoint — the full request and response schema, including JSON-with-base64 mode and idempotency.
  • Concepts: Criteria — how each rule is defined and what drives violation_reasons.
  • Concepts: Categories — the outcome buckets behind category and is_compliant.
  • SDKs — drop in a scanner instead of building your own camera and retry loop.

Get in Touch

Questions about pricing, integrations, or custom deployments? We'd love to hear from you.