Developers

Public API v2.2 — build VIP into your ERP, CRM, and dashboards

Full CRUD over inspections, templates, users, and reports. Pull files and images programmatically, manage global Custom Sets, and receive signed webhooks the moment an inspection is created or completed — no more data blind spots between VIP and your systems.

Quickstart

  1. Open Settings → API Keys and generate a key. Choose live or test and the scopes the integration needs.
  2. Copy the key immediately — it is hashed at rest and never shown again.
  3. Send it as a bearer token on every request.
curl https://virtualinspection.ai/api/public/v2/inspections?limit=25 \
  -H "Authorization: Bearer vip_live_xxxxxxxxxxxxxxxx"

Base URL: https://virtualinspection.ai/api/public/v2. All requests and responses are JSON over HTTPS. CORS is enabled, but keys must never be shipped in browser code.

Endpoint reference

Every endpoint is organization-scoped: a key only ever sees its own organization's data.

Sessions & integrations

The adapter layer CRM and contact-centre platforms use: test a key, start a live session with a one-tap SMS join link, and subscribe to completed-report events. Works with Zapier, HubSpot, Zoho, Dynamics 365, Salesforce, Zendesk, Genesys, NICE, and Procore.

  • GET/meread

    Test a key — returns the organization and scopes

  • POST/sessionswrite

    Start a live session and text the join link (send an Idempotency-Key header to make retries safe)

  • GET/hooksread

    List webhook subscriptions

  • POST/hookswrite

    Subscribe to an event — returns the signing secret once

  • DELETE/hooks/{id}write

    Unsubscribe

    • id (path) — Resource ID.
  • POST/hooks/{id}/testwrite

    Send a signed sample event to verify your receiver

    • id (path) — Resource ID.
  • GET/sessions/{id}read

    Retrieve a session and its join link

    • id (path) — Resource ID.
  • PATCH/sessions/{id}write

    Reschedule or update a session's contact and job details

    • id (path) — Resource ID.
  • DELETE/sessions/{id}write

    Cancel a session

    • id (path) — Resource ID.
  • GET/hooks/{id}/deliveriesread

    Delivery history for a subscription

    • id (path) — Resource ID.
  • POST/hooks/{id}/deliverieswrite

    Replay a past delivery (body: `{ delivery_id }`)

    • id (path) — Resource ID.
  • POST/hooks/{id}/rotate-secretwrite

    Rotate the signing secret — the previous one stays valid for 24 hours

    • id (path) — Resource ID.

Inspections

Create, retrieve, and update inspections. Use this to sync jobs from an ERP or CRM and to pull results back out.

  • GET/inspectionsread

    List inspections

    • limit (query) — Page size, 1–200. Defaults to 50.
    • cursor (query) — Opaque cursor from the previous page.
    • status (query) — draft, scheduled, in_progress, completed, cancelled.
    • updated_after (query) — ISO 8601 — only records changed since.
    • inspector_id (query) — Filter by assigned inspector.
  • POST/inspectionswrite

    Create an inspection

  • GET/inspections/{id}read

    Retrieve an inspection

    • id (path) — Resource ID.
  • PATCH/inspections/{id}write

    Update an inspection

    • id (path) — Resource ID.

Responses

Checklist answers for an inspection. v2.2 adds reference_id and isFlagged, plus targeting by question ID.

  • GET/inspections/{id}/responsesread

    List responses (checklist items)

    • id (path) — Resource ID.
    • limit (query) — Page size, 1–200. Defaults to 50.
    • cursor (query) — Opaque cursor from the previous page.
    • question_ids (query) — Comma-separated question IDs to retrieve.
    • flagged (query) — true returns only flagged responses.
    • rating (query) — Filter by rating.
    • updated_after (query) — Only responses changed since.

Files & images

Pull captured photos, clips, and their verification metadata into your own systems.

  • GET/inspections/{id}/mediaread

    List inspection media with capture metadata

    • id (path) — Resource ID.
    • limit (query) — Page size, 1–200. Defaults to 50.
    • cursor (query) — Opaque cursor from the previous page.
  • GET/media/{id}/downloadread

    Download a file — 302 to a signed URL, or add ?format=json for the URL itself

    • id (path) — Resource ID.
    • format (query) — Return the signed URL as JSON instead of redirecting.

Templates

List, retrieve, and clone report template structures.

  • GET/templatesread

    List report templates

    • limit (query) — Page size, 1–200. Defaults to 50.
    • cursor (query) — Opaque cursor from the previous page.
  • GET/templates/{id}read

    Retrieve a template

    • id (path) — Resource ID.
  • POST/templates/{id}/clonewrite

    Clone a template

    • id (path) — Resource ID.

Reports

List completed reports, trigger generation, and download finished PDFs.

  • GET/reportsread

    List completed reports (`limit`, `since`, `job_reference`)

  • POST/reportswrite

    Generate a report PDF for an inspection

  • GET/reports/{id}read

    Report status

    • id (path) — Resource ID.
  • GET/reports/{id}/downloadread

    Download a report PDF

    • id (path) — Resource ID.

Users

Read organization members so your systems can map inspectors to internal identities.

  • GET/usersread

    List organization members

Custom Sets

Global lists — statuses, causes, locations, part codes — referenced from fields in templates. Manage them programmatically to keep VIP consistent with your source systems.

  • GET/custom-setsread

    List Custom Sets

  • POST/custom-setswrite

    Create a Custom Set

  • GET/custom-sets/{id}read

    Retrieve a Custom Set with items

    • id (path) — Resource ID.
  • PATCH/custom-sets/{id}write

    Update a Custom Set

    • id (path) — Resource ID.
  • DELETE/custom-sets/{id}admin

    Delete a Custom Set

    • id (path) — Resource ID.
  • GET/custom-sets/{id}/itemsread

    List items

    • id (path) — Resource ID.
  • POST/custom-sets/{id}/itemswrite

    Append items

    • id (path) — Resource ID.
  • PUT/custom-sets/{id}/itemswrite

    Replace all items

    • id (path) — Resource ID.

Evidence Integrity

Photo compliance, retake turnaround, and outstanding evidence requests across the workspace. Use it to feed QA dashboards or block ERP job closure while retakes are open.

  • GET/evidenceread

    Evidence integrity metrics

    • days (query) — Window size, 1-365. Defaults to 30.
    • inspection_id (query) — Scope the metrics to a single inspection.

Smarter response filtering in v2.2

v2.2 makes retrieval far more targeted, so integrations pull only what they need:

  • New fields: reference_id and isFlagged are returned on responses, so records line up with your own work-order numbers and exception queues.
  • Better targeting: fetch responses for specific question_ids, or set flagged=true to retrieve only flagged items.
  • New filters: narrow lists by status, updated_after, and template_id — ideal for incremental syncs on a schedule.

Webhooks

Subscribe endpoints in Settings → Webhooks. Choose the events you want and, optionally, exactly which fields the payload should include.

  • inspection.createdAn inspection was created, from the app or the API.
  • inspection.completedAn inspection was marked complete and its results are final.
  • report.completedA report PDF finished generating and is ready to download.
  • media.flaggedVerification flagged a photo or clip for review.
  • evidence.failedPhotos on a finished inspection fall short of the organization's evidence policy.
  • evidence.retake_requestedAn inspector asked for one or more photos to be captured again.
  • evidence.retake_fulfilledA replacement photo arrived for a retake request.

Sample payload

{
  "id": "evt_01J9…",
  "type": "inspection.completed",
  "created_at": "2026-08-05T17:42:11.204Z",
  "data": {
    "inspection": {
      "id": "b1f4…",
      "status": "completed",
      "reference_id": "WO-88213",
      "address": "1420 W Grand Ave, Chicago IL",
      "completed_at": "2026-08-05T17:42:09.000Z"
    }
  }
}

Verifying the signature

Every delivery carries x-vip-signature: t=<unix>,v1=<hex>. Compute HMAC-SHA256 over `${t}.${rawBody}` with your endpoint secret and compare using a timing-safe check.

import { createHmac, timingSafeEqual } from "crypto";

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(
    header.split(",").map((kv) => kv.split("=", 2)),
  );
  const age = Math.abs(Date.now() / 1000 - Number(parts.t));
  if (age > 300) return false; // reject replays older than 5 minutes

  const expected = createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest("hex");

  const a = Buffer.from(parts.v1), b = Buffer.from(expected);
  return a.length === b.length && timingSafeEqual(a, b);
}

Retries: non-2xx responses and timeouts are retried with exponential backoff. Persistent failures disable the endpoint and surface a failure count in Settings, where you can re-enable it once your receiver is healthy. Deliveries can repeat, so make handlers idempotent on the event id.

Zapier & CRM integration

Connect HubSpot, Zoho, Dynamics 365, Zendesk, Genesys, Procore and hundreds of other systems without custom code. Create a key with the read and write scopes, paste it into the Zapier connection screen, and the app authenticates against GET /v2/me.

  • Start Inspection SessionAction — POST /v2/sessions creates the session, mints the one-tap join link, and texts it to the customer.
  • Report CompletedTrigger — a REST hook registered through POST /v2/hooks, delivering the frozen report.completed payload above.

Start a session from any CRM record

curl -X POST https://virtualinspection.ai/api/public/v2/sessions \
  -H "Authorization: Bearer vip_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: WO-4821" \
  -d '{
    "customer_name": "Jane Doe",
    "customer_phone": "+15551234567",
    "address": "12 Elm St, Austin TX",
    "job_reference": "WO-4821",
    "source": "zapier",
    "send_sms": true
  }'

Send an Idempotency-Key header so a retried automation returns the original session instead of creating a duplicate. SMS is best-effort — if it can't send, sms.sent is false with a reason and join_url is still returned so your CRM can deliver it.

Subscribe to finished reports

curl -X POST https://virtualinspection.ai/api/public/v2/hooks \
  -H "Authorization: Bearer vip_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "target_url": "https://hooks.zapier.com/hooks/catch/123/abc/",
    "event_type": "report.completed"
  }'
# -> { "data": { "id": "…", "secret": "whsec_…" } }

The signing secret is returned once at subscribe time — store it and verify every delivery as shown above. Remove a subscription with DELETE /v2/hooks/{id}.

Rate limits & pagination

Limits are per key — 120 requests/minute by default, configurable up to 6,000 — so one integration can never starve another. Over the limit returns 429 with Retry-After.

List endpoints are cursor-paginated: pass limit (1–200) and follow next_cursor until it is absent.

Versioning

The version is pinned in the path (/v2). Additive changes — new fields, new endpoints — ship without a version bump, so parse responses tolerantly. Breaking changes get a new path and an announced deprecation window.

Errors

  • 400 invalid_request
    Malformed body or query parameter. The response names the offending field.
  • 401 unauthorized
    Missing, malformed, revoked, or expired API key.
  • 403 insufficient_scope
    The key is valid but lacks the scope this endpoint requires.
  • 404 not_found
    No such resource in your organization.
  • 429 rate_limited
    Per-key rate limit exceeded. Retry after the Retry-After header.
  • 500 server_error
    Unexpected failure on our side. Safe to retry with backoff.

Developer FAQ

How do I authenticate against the Public API?

Every request carries an organization API key as a bearer token: Authorization: Bearer vip_live_… Keys are created in Settings → API Keys, are scoped to read, write, or admin, and can be issued separately per integration or environment and rotated independently.

What are the API rate limits?

Each key has its own per-minute limit, 120 requests per minute by default and configurable up to 6,000. Exceeding it returns HTTP 429 with a Retry-After header. Because the limit is per key, one noisy integration cannot starve the others.

How does pagination work?

List endpoints are cursor-paginated. Pass limit (1–200, default 50) and follow the next_cursor value from each response as the cursor parameter on the following request. An absent next_cursor means you have reached the end.

How do I verify a webhook signature?

Each delivery includes x-vip-signature: t=<unix-timestamp>,v1=<hex>. Compute HMAC-SHA256 over the string `${t}.${rawBody}` using the endpoint's signing secret and compare it to v1 with a timing-safe comparison. Reject deliveries older than five minutes.

What happens when a webhook delivery fails?

Failed deliveries are retried automatically with exponential backoff across several attempts. Endpoints that keep failing are disabled and shown with a failure count in Settings → Webhooks, where you can re-enable them after fixing the receiver.

Can AI search engines read Virtual Inspection Pro content?

Yes. /api/public/ai/content returns a JSON catalog of every FAQ, industry, and feature page, and /api/public/ai/content/{type}/{slug} returns the full text of a single item. Both are unauthenticated, CORS-enabled, and listed in llms.txt.

Ready to integrate?

API access and webhooks are included with Enterprise. Generate a key in seconds, or talk to us about a guided integration with your ERP, CRM, or analytics stack.

Get started

Ready to run your first virtual inspection?

See verified remote capture in action, or dive straight into pricing built for teams that inspect at scale.

Privacy