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.
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:
{
"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:
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:
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:
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-idWhat'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
categoryandis_compliant. - SDKs — drop in a scanner instead of building your own camera and retry loop.