Verify

text
POST https://verify.switchlabs.dev/api/v1/verify

Submits a single photo for verification against a policy. Authenticate with a managed vai_ key, then receive 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

HeaderRequiredValue
X-API-KeyYesYour vai_… key. See Authentication.
Content-TypeYesmultipart/form-data or application/json.
Idempotency-KeyNoOpaque string. Identical retries return the cached response.

Body parameters

ParameterTypeRequiredDescription
imagefile or base64 stringYesThe image to verify. JPEG, PNG, or WebP. Max size 10 MB.
policystringYesThe policy ID. Built-in IDs include scooter_parking, bike_parking, forest_green_bay, forest_designated_bay, and pol_forest1.
metadataobjectNoFree-form key/value data persisted with the verification (e.g. user_id, vehicle_id, GPS).
include_image_databooleanNoWhen 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

bash
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.

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": {
    "device_id": "dev_123",
    "gps": { "lat": 37.77, "lng": -122.42 }
  },
  "image_url": "https://...signed-url...",
  "evaluation_source": "cloud_vlm"
}

Response fields

FieldTypeDescription
idstringUnique verification ID, prefix ver_.
created_atISO 8601When the verification was processed.
statusstringsuccess if the verification completed, error if processing failed.
is_compliantbooleanTop-level pass/fail decision.
confidencenumber0–1 confidence score from the image analyzer.
policystringThe policy ID that ran.
categorystringOutcome bucket — e.g. compliant, improvable, unsafe, lacks_info, or any custom category on the policy.
violation_reasonsstring[]Machine-readable violation IDs (one per failed criterion).
feedbackstringHuman-readable explanation, safe to surface to end users.
metadataobjectThe metadata you sent in the request, echoed back.
image_urlstring?Time-limited signed URL to the stored image. null if storage is disabled for this customer.
evaluation_sourcestring?cloud (server-side analysis) or on_device (on-device ML, when the SDK runs inference locally).
image_datastring?Base64 image — only present when include_image_data was true.

Headers on every response

HeaderDescription
X-Request-IdUnique request ID. Include this when contacting support.
Server-TimingPer-phase timings (auth, parse, vision, write). Useful for performance work.
Retry-AfterOn 429 responses, seconds to wait before retrying.

Errors

StatusReason
400Missing fields, invalid JSON, image too large, unknown policy ID.
401Missing or invalid API key.
403API key not valid for VerifyAI, or no active subscription.
409Conflicting Idempotency-Key request still in flight.
422Idempotency-Key reused with a different body.
429Rate limit or monthly usage cap.
500Verification processing failed.
503Authentication 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.

bash
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.

Get in Touch

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