Verify AI API
Submit photos for AI-powered compliance verification. Get structured results with confidence scores, violation details, and user feedback.
https://www.switchlabs.dev/api/verify-ai/v1Authentication
All API requests require an API key passed via the X-API-Key header.
curl https://www.switchlabs.dev/api/verify-ai/v1/verifications \
-H "X-API-Key: vai_your_api_key_here"Get your API key from the sign-up page or your dashboard settings.
Submit Verification
Submit a photo for AI analysis. Accepts multipart form-data or JSON with base64 image.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| image | file | base64 | Yes | The image to verify (JPEG, PNG, WebP, max 10MB) |
| policy | string | Yes | Policy ID (e.g., scooter_parking, damage_detection, delivery_pod) |
| metadata | object | No | Arbitrary metadata to attach (device_id, user_id, gps, etc.) |
| provider | string | No | AI provider override: "openai", "anthropic", or "gemini" |
Example: Multipart Form-Data
curl -X POST https://www.switchlabs.dev/api/verify-ai/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}}'Example: JSON with Base64
curl -X POST https://www.switchlabs.dev/api/verify-ai/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"}
}'Response
{
"id": "ver_8x92m4k9",
"created_at": "2026-01-10T14:30:00Z",
"status": "success",
"is_compliant": false,
"confidence": 0.98,
"policy": "scooter_parking",
"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..."
}List Verifications
Retrieve a paginated list of past verifications. Results are scoped to your API key.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | No | Results per page (default 20, max 100) |
| cursor | string | No | Pagination cursor (created_at of last item) |
| policy | string | No | Filter by policy ID |
| status | string | No | Filter by status (success, error) |
| is_compliant | boolean | No | Filter by compliance result |
| start_date | ISO date | No | Filter: created after this date |
| end_date | ISO date | No | Filter: created before this date |
Response
{
"data": [
{
"id": "ver_8x92m4k9",
"created_at": "2026-01-10T14:30:00Z",
"status": "success",
"is_compliant": false,
"confidence": 0.98,
"policy": "scooter_parking",
"violation_reasons": ["blocking_sidewalk"],
"feedback": "...",
"metadata": {},
"image_url": "https://...signed-url..."
}
],
"has_more": true,
"next_cursor": "2026-01-10T14:30:00Z"
}Get Verification
Retrieve full details of a specific verification, including a fresh signed image URL.
Example
curl https://www.switchlabs.dev/api/verify-ai/v1/verifications/ver_8x92m4k9 \
-H "X-API-Key: vai_your_api_key"Webhooks
Configure webhooks to receive queued notifications when verifications complete. Deliveries are processed every 5 minutes from your dashboard settings.
Payload Format
{
"event": "verification.completed",
"data": {
"id": "ver_8x92m4k9",
"created_at": "2026-01-10T14:30:00Z",
"status": "success",
"is_compliant": false,
"confidence": 0.98,
"policy": "scooter_parking",
"violation_reasons": ["blocking_sidewalk"],
"feedback": "Please move away from the walkway.",
"metadata": {},
"image_url": "https://...signed-url..."
}
}Signature Verification
Each delivery includes an X-VerifyAI-Signature header with format t=timestamp,v1=signature.
// Verify webhook signature (Node.js)
const crypto = require('crypto');
function verifySignature(payload, header, secret) {
const [tPart, sigPart] = header.split(',');
const timestamp = tPart.split('=')[1];
const signature = sigPart.split('=')[1];
const expected = crypto
.createHmac('sha256', secret)
.update(timestamp + '.' + payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}Retry Behavior
Failed deliveries are retried up to 3 times: after 1 minute, 5 minutes, and 30 minutes. A delivery is considered successful if your endpoint returns a 2xx status code within 5 seconds.
Policies
Available verification policies:
scooter_parkingScooter Parking ComplianceVerifies electric scooter/bike parking meets city regulations. Checks: vehicle visible, upright, kickstand deployed, not blocking sidewalk or entrance, in designated area.
no_vehicle_visiblevehicle_fallenkickstand_upblocking_sidewalkblocking_entrancenot_designated_areain_roadwayimage_uncleardamage_detectionVehicle Damage DetectionDetects and documents damage to vehicles or equipment. Identifies type and severity of damage.
scratchesdentscracksbroken_partsflat_tiremissing_componentsstructural_damageelectrical_damagedelivery_podDelivery Proof of DeliveryVerifies package delivery placement and condition. Checks: package visible, at door, protected, upright, accessible.
no_package_visiblenot_at_doorexposed_to_weatherpackage_damagedblocking_pathimage_unclearCode Examples
cURL
curl -X POST https://www.switchlabs.dev/api/verify-ai/v1/verify \
-H "X-API-Key: vai_your_api_key" \
-F "image=@photo.jpg" \
-F "policy=scooter_parking"JavaScript (fetch)
const formData = new FormData();
formData.append('image', fileInput.files[0]);
formData.append('policy', 'scooter_parking');
formData.append('metadata', JSON.stringify({
device_id: 'dev_123',
user_id: 'usr_456',
}));
const response = await fetch(
'https://www.switchlabs.dev/api/verify-ai/v1/verify',
{
method: 'POST',
headers: { 'X-API-Key': 'vai_your_api_key' },
body: formData,
}
);
const result = await response.json();
console.log(result.is_compliant); // true or false
console.log(result.feedback); // User-friendly messagePython (requests)
import requests
import json
response = requests.post(
'https://www.switchlabs.dev/api/verify-ai/v1/verify',
headers={'X-API-Key': 'vai_your_api_key'},
files={'image': open('photo.jpg', 'rb')},
data={
'policy': 'scooter_parking',
'metadata': json.dumps({
'device_id': 'dev_123',
'user_id': 'usr_456',
}),
},
)
result = response.json()
print(f"Compliant: {result['is_compliant']}")
print(f"Feedback: {result['feedback']}")Errors & Rate Limits
HTTP Status Codes
| Code | Meaning |
|---|---|
| 200 | Success |
| 400 | Bad request — missing required fields or invalid image format |
| 401 | Unauthorized — invalid or missing API key |
| 403 | Forbidden — no active subscription or wrong product |
| 404 | Verification not found |
| 429 | Rate limit exceeded |
| 500 | Server error — AI processing failure |
Rate Limits
| Limit | Value |
|---|---|
| Requests per minute | 60 |
| Requests per hour | 1,000 |
| Max image size | 10 MB |
When rate limited, the response includes a Retry-After header with the number of seconds to wait.