Verify

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

Submits 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

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

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

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.