Verify
POST https://verify.switchlabs.dev/api/v1/verifySubmits a single photo for verification against a policy. Returns a structured decision including compliance result, confidence, violation reasons, and a human-readable feedback string.
The endpoint accepts either multipart/form-data (best for live camera
uploads) or application/json with a base64-encoded image.
Request
Headers
| Header | Required | Value |
| ------------------ | -------- | ------------------------------------------------------ |
| X-API-Key | Yes | Your vai_… key. See Authentication. |
| Content-Type | Yes | multipart/form-data or application/json. |
| Idempotency-Key | No | Opaque string. Identical retries return the cached response. |
Body parameters
| Parameter | Type | Required | Description |
| -------------------- | ----------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| image | file or base64 string | Yes | The image to verify. JPEG, PNG, or WebP. Max size 10 MB. |
| policy | string | Yes | The policy ID. Built-in IDs include scooter_parking, bike_parking, forest_green_bay, forest_designated_bay, and pol_forest1. |
| metadata | object | No | Free-form key/value data persisted with the verification (e.g. user_id, vehicle_id, GPS). |
| provider | "openai" \\| "anthropic" \\| "gemini"| No | Override which VLM provider runs the verification. Most callers should omit this. |
| include_image_data | boolean | No | When true, includes the base64 image in the response (useful for audit-trail capture). Defaults to false. |
Multipart example
curl -X POST https://verify.switchlabs.dev/api/v1/verify \
-H "X-API-Key: vai_your_api_key" \
-F "image=@photo.jpg" \
-F "policy=scooter_parking" \
-F 'metadata={"device_id":"dev_123","gps":{"lat":37.77,"lng":-122.42}}'JSON with base64 example
curl -X POST https://verify.switchlabs.dev/api/v1/verify \
-H "X-API-Key: vai_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"image": "data:image/jpeg;base64,/9j/4AAQ...",
"policy": "scooter_parking",
"metadata": { "device_id": "dev_123" }
}'The image field accepts both a raw base64 string and a data URL — the
prefix (data:image/...;base64,) is stripped automatically.
Response
A 200 OK response is a Verification object.
{
"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": {
"device_id": "dev_123",
"gps": { "lat": 37.77, "lng": -122.42 }
},
"image_url": "https://...signed-url...",
"evaluation_source": "cloud_vlm"
}Response fields
| Field | Type | Description |
| ------------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| id | string | Unique verification ID, prefix ver_. |
| created_at | ISO 8601 | When the verification was processed. |
| status | string | success if the verification completed, error if processing failed. |
| is_compliant | boolean | Top-level pass/fail decision. |
| confidence | number | 0–1 confidence score from the underlying VLM. |
| policy | string | The policy ID that ran. |
| category | string | Outcome bucket — e.g. compliant, improvable, unsafe, lacks_info, or any custom category on the policy. |
| violation_reasons | string[] | Machine-readable violation IDs (one per failed criterion). |
| feedback | string | Human-readable explanation, safe to surface to end users. |
| metadata | object | The metadata you sent in the request, echoed back. |
| image_url | string? | Time-limited signed URL to the stored image. null if storage is disabled for this customer. |
| evaluation_source | string? | cloud_vlm (server-side VLM) or on_device (on-device ML, when the SDK runs inference locally). |
| image_data | string? | Base64 image — only present when include_image_data was true. |
Headers on every response
| Header | Description |
| ---------------- | -------------------------------------------------------------------------- |
| X-Request-Id | Unique request ID. Include this when contacting support. |
| Server-Timing | Per-phase timings (auth, parse, vision, write). Useful for performance work. |
| Retry-After | On 429 responses, seconds to wait before retrying. |
Errors
| Status | Reason |
| ------ | --------------------------------------------------- |
| 400 | Missing fields, invalid JSON, image too large, unknown policy ID. |
| 401 | Missing or invalid API key. |
| 403 | API key not valid for VerifyAI, or no active subscription. |
| 409 | Conflicting Idempotency-Key request still in flight. |
| 422 | Idempotency-Key reused with a different body. |
| 429 | Rate limit or monthly usage cap. |
| 500 | Verification processing failed. |
| 503 | Authentication service unavailable. Retry. |
Idempotency
Send the same Idempotency-Key header on a retry, and the API returns
the original cached response — useful when the network drops mid-flight
or a mobile app reties on app resume.
curl -X POST https://verify.switchlabs.dev/api/v1/verify \
-H "X-API-Key: vai_your_api_key" \
-H "Idempotency-Key: 9c7f1c44-1d0e-4ea1-9f55-2c2a39d7fcb1" \
-F "image=@photo.jpg" -F "policy=scooter_parking"Keys are scoped per route. If you reuse a key with a different request
body the API returns 422 Unprocessable Entity.