Verifications API

Every call to POST /v1/verify writes a row scoped to your account. These two endpoints read that history back: list verifications with filters and cursor pagination, and retrieve a single verification by ID with a freshly signed image URL. Both are read-only and account-scoped — you only ever see your own verifications.

Authentication

Both endpoints authenticate the same way as every VerifyAI request.

| Header | Required | Value | | ----------- | -------- | ------------------------------------------------------ | | X-API-Key | Yes | Your vai_… key. See Authentication. |

List verifications

text
GET /api/v1/verifications

Returns your most recent verifications, newest first, with cursor-based pagination. Use the query parameters to filter by policy, status, compliance, or date range.

Query parameters

| Parameter | Type | Default | Description | | -------------- | ------- | ------- | ---------------------------------------------------------------------------- | | limit | number | 20 | Page size. Clamped to a maximum of 100. | | cursor | string | — | Pagination cursor. Pass the next_cursor from the previous page. | | policy | string | — | Filter to a single policy ID (e.g. scooter_parking). | | status | string | — | Filter by success or error. | | is_compliant | boolean | — | Filter by outcome — true or false. | | start_date | string | — | ISO 8601 lower bound (inclusive) on created_at. | | end_date | string | — | ISO 8601 upper bound (inclusive) on created_at. |

Example

curl "https://verify.switchlabs.dev/api/v1/verifications?limit=20&policy=scooter_parking&is_compliant=false" \
-H "X-API-Key: vai_your_api_key"

Response

json
{
  "data": [
    {
      "id": "ver_8x92m4k9",
      "created_at": "2026-05-12T14:30:00Z",
      "status": "success",
      "is_compliant": false,
      "confidence": 0.94,
      "policy": "scooter_parking",
      "violation_reasons": ["blocking_sidewalk", "kickstand_up"],
      "feedback": "Please deploy the kickstand and move away from the walkway.",
      "metadata": { "trip_id": "trip_456" },
      "image_url": "https://...signed-url..."
    }
  ],
  "has_more": true,
  "next_cursor": "2026-05-12T14:30:00Z"
}

Response fields

| Field | Type | Description | | ------------- | -------- | --------------------------------------------------------------------------------- | | data | object[] | The page of verification summaries, newest first. | | has_more | boolean | true when more results exist past this page. | | next_cursor | string? | Pass as cursor to fetch the next page. null on the last page. |

Each item in data carries id, created_at, status, is_compliant, confidence, policy, violation_reasons, feedback, metadata, and a freshly signed image_url (null when storage is disabled).

List items omit category

The list endpoint returns a compact summary and does not include the category field. If you need the resolved category for a specific verification, fetch it from the retrieve endpoint below, or read it from the original POST /v1/verify response.

Paginating

next_cursor is the created_at of the last item on the page. Pass it back as cursor and keep going until has_more is false:

let cursor = null;
const all = [];
do {
const params = new URLSearchParams({ limit: "100" });
if (cursor) params.set("cursor", cursor);
const res = await fetch(
  "https://verify.switchlabs.dev/api/v1/verifications?" + params,
  { headers: { "X-API-Key": process.env.VERIFY_AI_KEY } },
);
const page = await res.json();
all.push(...page.data);
cursor = page.next_cursor;
} while (cursor);

Retrieve a verification

text
GET /api/v1/verifications/:id

Returns a single verification by its ver_ ID. The image URL is signed fresh on every call — use this endpoint when an image_url from an earlier response has expired.

Example

curl https://verify.switchlabs.dev/api/v1/verifications/ver_8x92m4k9 \
-H "X-API-Key: vai_your_api_key"

Response

json
{
  "id": "ver_8x92m4k9",
  "created_at": "2026-05-12T14:30:00Z",
  "status": "success",
  "is_compliant": false,
  "confidence": 0.94,
  "policy": "scooter_parking",
  "violation_reasons": ["blocking_sidewalk", "kickstand_up"],
  "feedback": "Please deploy the kickstand and move away from the walkway.",
  "metadata": { "trip_id": "trip_456" },
  "image_url": "https://...signed-url...",
  "processing_time_ms": 1184,
  "error_message": null
}

Response fields

| Field | Type | Description | | -------------------- | -------- | ---------------------------------------------------------------------------- | | id | string | The verification ID, prefix ver_. | | created_at | ISO 8601 | When the verification was processed. | | status | string | success or error. | | is_compliant | boolean | Top-level pass/fail decision. | | confidence | number | 0–1 confidence score from the underlying model. | | policy | string | The policy ID that ran. | | violation_reasons | string[] | Machine-readable IDs of the failed criteria. | | feedback | string | Human-readable explanation, safe to surface to end users. | | metadata | object | The metadata you sent with the original request. | | image_url | string? | Freshly signed URL to the stored image. null if storage is disabled. | | processing_time_ms | number? | Server-side processing time for the original verification. | | error_message | string? | Populated when status is error; otherwise null. |

Errors

| Status | Reason | | ------ | ------------------------------------------------------------------- | | 401 | Missing or invalid API key. | | 403 | API key not valid for VerifyAI, or not linked to a customer. | | 404 | Verification not found, or it belongs to a different account (retrieve). | | 429 | Rate limit — check the Retry-After header. | | 500 | Failed to fetch verifications. | | 503 | Authentication service unavailable. Retry. |

What's next

Get in Touch

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