DigiSurvey ConnectAPI documentation Get an API key
OverviewQuickstartCreate a claimGet a reportWebhooksGet an API key →
REST API · version 2026-09-26

DigiSurvey Connect API

Connect your claim or policy administration system to your empanelled surveyors. Create claim assignments, follow their status, fetch survey reports with photos and documents, and get notified by signed webhooks. JSON over HTTPS; every request is scoped to your organisation.

Base URLhttps://vtlxdqgdrapeyhncbkrj.supabase.co/functions/v1/api

Quickstart

  1. Get your organisation activated. Request access, or sign in to the Stakeholder Portal and create your organisation. DigiSurvey verifies and activates it.
  2. Connect surveyors. Share your organisation's invite code with your empanelled surveyors (Portal → Surveyors).
  3. Create an API key. Portal → API keys → Create API key. Copy it — it is shown only once. Owners and admins can create keys.
  4. Call the API with Authorization: Bearer dsk_live_…:
curl https://vtlxdqgdrapeyhncbkrj.supabase.co/functions/v1/api/me \
  -H "Authorization: Bearer $DIGISURVEY_API_KEY"

Authentication & scopes

Every request (except GET /) must send an organisation API key as a bearer token. Keys look like dsk_live_ followed by 48 hex characters. DigiSurvey stores only a SHA-256 hash of your key — if you lose it, revoke it and create a new one.

Authorization: Bearer dsk_live_3f9c…e21a

Each key carries one or more scopes. A request without the needed scope fails with 403 insufficient_scope.

ScopeAllows
claims:readList and read claims, list connected surveyors
claims:writeCreate claims (single and bulk); assign, close, cancel, reopen
reports:readList and read submitted reports, get download links and HTML
reports:writeAcknowledge reports or raise queries
Keep keys on your server. Never put them in a mobile app or browser code. Revoke a key immediately in the portal if it leaks — it stops working at once.

Errors

Errors use standard HTTP status codes and a consistent JSON body. Every response carries an X-Request-Id header — include it when you contact support.

Error body
{
  "error": {
    "code": "invalid_request",
    "message": "survey_type must be spot, final or reinspection",
    "details": { … }        // sometimes, e.g. per-row errors
  }
}
StatuscodeMeaning
400invalid_jsonBody is not a JSON object
401unauthorized, invalid_api_keyMissing, malformed, unknown or revoked key
403insufficient_scope, organisation_inactive, forbiddenKey lacks the scope, organisation pending/suspended, or action not allowed
404not_found, route_not_foundObject doesn't exist in your organisation, or unknown path
409duplicate_external_ref, conflictClaim with that external_ref exists, or claim number already open
413payload_too_largeBody larger than 2 MB
422invalid_requestValidation failed — see message / details
429rate_limitedToo many requests — wait Retry-After seconds
500internal_errorOur side — safe to retry with backoff

Rate limits & pagination

Each API key may make 120 requests per minute. Responses include:

HeaderMeaning
X-RateLimit-LimitRequests allowed per minute (120)
X-RateLimit-RemainingRequests left in the current minute
X-RateLimit-ResetUnix time (seconds) when the window resets
Retry-AfterOn 429 only — seconds to wait

Use POST /claims/bulk instead of many single calls. List endpoints accept limit (1–200, default 50) and offset, and return has_more and next_offset:

List envelope
{ "object": "list", "data": [ … ], "has_more": true, "next_offset": 50 }

Idempotency

Send your own claim ID as external_ref (unique per organisation). Re-sending a claim with the same external_ref returns 409 duplicate_external_ref with the existing id in details — never a duplicate claim.

Alternatively send an Idempotency-Key header on POST /claims. It is stored as the claim's external_ref (if you didn't send one) and a retry with the same key returns the original claim with 200 and header Idempotent-Replay: true.

The claim object

A claim (assignment) is a survey request from your organisation to a connected surveyor.

FieldTypeDescription
iduuidDigiSurvey claim id
claim_numberstring ≤80Your claim number. claim_number or vehicle_number is required. Only one open claim per number.
vehicle_numberstring ≤20Registration number — normalised to upper case without spaces (RJ14CD5678)
policy_numberstring ≤80Policy number
insured_name, insured_phonestringInsured / contact person
insurer_namestring ≤160Insurance company (defaults to your organisation's name for insurers)
garage_name, garage_addressstringWhere the vehicle can be inspected
loss_datedateYYYY-MM-DD (also accepts DD-MM-YYYY / DD/MM/YYYY)
loss_locationstring ≤300Place of accident / loss
estimate_amountnumberRepair estimate in ₹ (commas and ₹ are stripped)
survey_typeenumspot | final (default) | reinspection
instructionsstring ≤2000Notes for the surveyor
external_refstring ≤120Your system's ID — unique per organisation (see idempotency)
surveyor_id / surveyor_emailuuid / stringWrite only. Assign to a connected surveyor (see GET /surveyors). Omit to create unassigned.
surveyorobject|nullRead only. { id, name, mobile }
statusenumnew → accepted → in_progress → report_submitted → closed; or rejected (surveyor declined) / cancelled
status_notestringLatest note (e.g. the surveyor's reason for declining)
survey_iduuid|nullSet when the surveyor accepts and the survey is created
created_viaenumui | csv | api
created_at, updated_at, accepted_at, report_submitted_at, closed_attimestampISO-8601, UTC

Create a claim

POST/claimsclaims:write

Creates one claim. If a surveyor is given, they get a push notification immediately. Unknown fields are rejected with 422 so typos don't go unnoticed. Returns 201 with the claim object.

curl -X POST $BASE/claims \
  -H "Authorization: Bearer $DIGISURVEY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: CLAIMSYS-88213" \
  -d '{
    "claim_number": "MC-2026-0412",
    "policy_number": "3001/12345678/00/000",
    "vehicle_number": "RJ14 CD 5678",
    "insured_name": "Ramesh Kumar",
    "insured_phone": "+91 98290 00000",
    "garage_name": "Shree Motors",
    "garage_address": "Sikar Road, Jaipur",
    "loss_date": "2026-09-20",
    "loss_location": "NH-52, Chomu",
    "estimate_amount": 52000,
    "survey_type": "spot",
    "instructions": "Insured available after 11 am",
    "surveyor_email": "surveyor@example.com"
  }'
201 Created
{
  "object": "claim",
  "id": "524f9bf0-b5e5-4074-92f0-a15cad984bdf",
  "claim_number": "MC-2026-0412",
  "vehicle_number": "RJ14CD5678",
  "insurer_name": "Your Insurance Co",
  "loss_date": "2026-09-20",
  "estimate_amount": 52000,
  "survey_type": "spot",
  "status": "new",
  "survey_id": null,
  "created_via": "api",
  "external_ref": "CLAIMSYS-88213",
  "surveyor": { "id": "20f1…", "name": "A. Sharma", "mobile": "98xxxxxx00" },
  "created_at": "2026-09-26T03:02:32.923Z", …
}

Create claims in bulk

POST/claims/bulkclaims:write

Send { "claims": [ … ] } with 1–500 claim objects (same fields as POST /claims). Each row is validated on its own: valid rows are created, invalid rows are reported with their index (0-based) — so fix and resend only the failures. Returns 201 if at least one claim was created, otherwise 422. Each surveyor gets one grouped notification.

curl -X POST $BASE/claims/bulk \
  -H "Authorization: Bearer $DIGISURVEY_API_KEY" -H "Content-Type: application/json" \
  -d '{ "claims": [
        { "claim_number": "MC-1001", "vehicle_number": "MH12AB1234", "surveyor_email": "a@example.com", "external_ref": "X-1001" },
        { "policy_number": "only-a-policy" },
        { "claim_number": "MC-1003", "loss_date": "31/02/2026" }
      ] }'
Rows with unknown field names fail the whole request with 422 and a list in details.errors — nothing is created, so a column mistake never creates half a batch. Duplicate external_ref rows come back with "code": "duplicate".

List claims

GET/claimsclaims:read

Newest first. Filters (all optional, combinable):

QueryExampleNotes
statusstatus=new,acceptedComma-separated statuses
updated_since2026-09-25T00:00:00ZPolling for changes — use the latest updated_at you saw
claim_number, vehicle_number, policy_number, external_refexternal_ref=CLAIMSYS-88213Exact match
limit, offsetlimit=100&offset=100See pagination
curl "$BASE/claims?status=report_submitted&updated_since=2026-09-25T00:00:00Z&limit=100" \
  -H "Authorization: Bearer $DIGISURVEY_API_KEY"

Retrieve a claim

GET/claims/{id}claims:read

Returns the claim object, or 404 if it doesn't exist in your organisation.

Assign, close, cancel or reopen

POST/claims/{id}/assignclaims:write

Body { "surveyor_id": "…" } or { "surveyor_email": "…" }, optional "note". Only for claims in new (unassigned) or rejected status; the surveyor must be connected to your organisation.

POST/claims/{id}/close  ·  /cancel  ·  /reopenclaims:write

Optional body { "note": "…" }. close marks the claim finished (usually after acknowledging the report); cancel withdraws it and notifies the surveyor; reopen brings back a closed/cancelled claim. Each returns the updated claim and fires assignment.updated.

cURL
curl -X POST $BASE/claims/524f9bf0-b5e5-4074-92f0-a15cad984bdf/assign \
  -H "Authorization: Bearer $DIGISURVEY_API_KEY" -H "Content-Type: application/json" \
  -d '{ "surveyor_email": "surveyor@example.com", "note": "Urgent — cashless" }'

List connected surveyors

GET/surveyorsclaims:read

Surveyors actively connected to your organisation — use their id or email when assigning.

200 OK
{ "object": "list", "has_more": false, "data": [
  { "object": "surveyor", "id": "20f1…", "name": "A. Sharma", "email": "a@example.com",
    "mobile": "98xxxxxx00", "license_number": "SLA-12345", "iiisla_number": "…", "connected_since": "2026-09-01T…" } ] }

The report object

A report submission is created when a surveyor sends one or more reports for a survey to your organisation (they can send many surveys at once). If the survey came from one of your claims, claim_id is set and the claim moves to report_submitted.

FieldTypeDescription
iduuidSubmission id
claim_iduuid|nullYour claim, when the survey was created from an assignment
survey_id, surveyor_iduuidSource survey and surveyor
report_typesstring[]e.g. spot, final, reinspection_v2, bill_final, summary
summaryobjectSnapshot at sending time: vehicle_number, claim_number, policy_number, insured_name, insurance_company, survey_type, report_number, assessed_amount, estimate_amount, make_model, date_of_loss, place_of_survey, job_number
filesobject[]Attached PDFs: { name, report_type, size, content_type } (+ download_url on the detail endpoint)
messagestringCovering note from the surveyor
statusenumsubmitted → viewed → acknowledged, or query_raised (back to submitted when the surveyor replies)
threadobject[]Acknowledgements, queries and replies: { side: "org"|"api"|"surveyor", text, at, kind }
created_at, viewed_at, acknowledged_attimestampISO-8601

List reports

GET/reportsreports:read

Newest first. Filters: status (comma-separated), since (ISO date-time, by created_at), claim_id, plus limit/offset. A typical integration polls GET /reports?status=submitted every few minutes — or uses webhooks.

Retrieve a report with download links

GET/reports/{id}reports:read

Returns the report object plus the surveyor, the linked claim, and time-limited download links (valid for 1 hour — fetch the report again for fresh links) for the attached PDFs, every photo and every document of the survey. Reading a submitted report marks it viewed. Add ?include=none to skip photos/documents, or ?include=html to also embed each report's HTML body (reports[].html, images rewritten to 1-hour download links) — combine as ?include=html,none.

200 OK
{
  "object": "report", "id": "0896304a-…", "status": "viewed",
  "claim": { "id": "524f9bf0-…", "claim_number": "MC-2026-0412", "external_ref": "CLAIMSYS-88213", "status": "report_submitted" },
  "surveyor": { "name": "A. Sharma", "license_number": "SLA-12345", … },
  "summary": { "vehicle_number": "RJ14CD5678", "assessed_amount": 41250, … },
  "documents_expire_at": "2026-09-26T04:03:07Z",
  "files": [ { "name": "Final_RJ14CD5678.pdf", "report_type": "final", "size": 812345,
               "download_url": "https://digitaliiisla.co.in/.netlify/functions/r2?action=ticket&t=9ffb…" } ],
  "reports": [ { "report_type": "final", "updated_at": "…", "changed_since_sent": false,
                 "html_url": "$BASE/reports/0896304a-…/html?type=final" } ],
  "photos": [ { "stage": "primary", "label": "Front bumper", "captured_at": "…", "latitude": 26.91, "longitude": 75.78, "download_url": "…" } ],
  "documents": [ { "doc_type": "rc", "uploaded_at": "…", "download_url": "…" } ]
}

download_url answers with a 302 redirect to the file — no API key needed, so you can hand it to a downloader. download_url is null if a file is not available. changed_since_sent: true means the surveyor edited the report after sending it.

Get the report as HTML

GET/reports/{id}/html?type=finalreports:read

Returns the saved report page (text/html) with letterhead, tables and text, ready to archive or print to PDF. Images are rewritten to download links valid for 24 hours. The attached PDF in files is the surveyor's official signed copy; prefer it when present. type must be one of the submission's report_types.

Acknowledge a report or raise a query

POST/reports/{id}/ackreports:write

Optional body { "message": "Received — thank you" }. Sets status to acknowledged and notifies the surveyor.

POST/reports/{id}/queryreports:write

Body { "message": "Please share RC back side" } (required). Sets query_raised; the surveyor is notified and their reply is added to thread.

cURL
curl -X POST $BASE/reports/0896304a-6ec9-4f3c-9ec4-fac4da66a950/ack \
  -H "Authorization: Bearer $DIGISURVEY_API_KEY" -H "Content-Type: application/json" \
  -d '{ "message": "Received, thanks" }'

Webhooks

Add an HTTPS endpoint in Portal → Webhooks, choose events and copy the signing secret (whsec_…). DigiSurvey sends a POST with a JSON event when something happens:

EventWhendata
report.submittedA surveyor sent a report to you{ report: { id, survey_summary, report_types, files, claim_id, created_at } }
assignment.updatedA claim was accepted, declined, started, linked, reported, assigned, closed, cancelled or reopened{ claim: <claim object>, action }
claim.createdClaims were created (portal, Excel or API){ count, claims: [{ id, claim_number, vehicle_number, surveyor_id, external_ref }] }
pingYou pressed Send test in the portal{ message }
Example delivery
POST /your/webhook HTTP/1.1
Content-Type: application/json
User-Agent: DigiSurvey-Webhooks/1.0
X-DigiSurvey-Event: report.submitted
X-DigiSurvey-Delivery: 5b0d3a1e-8f7c-4c2a-9a55-0c3f6f0b7a11
X-DigiSurvey-Timestamp: 1790391777
X-DigiSurvey-Signature: v1=6f1c0a…e93b

{
  "id": "5b0d3a1e-8f7c-4c2a-9a55-0c3f6f0b7a11",
  "object": "event",
  "type": "report.submitted",
  "api_version": "2026-09-26",
  "created_at": "2026-09-26T03:02:56Z",
  "data": { "report": { "id": "0896304a-…", "claim_id": "524f9bf0-…", "report_types": ["final"], … } }
}
  • Reply with any 2xx within 8 seconds. Do heavy work asynchronously — e.g. queue the event, then call GET /reports/{id}.
  • Delivery is best-effort: a failed attempt (network error or 5xx) is retried once. Use GET /reports?status=submitted or GET /claims?updated_since=… as a safety net.
  • Events can arrive more than once or out of order — de-duplicate on id.
  • After 25 consecutive failures the endpoint is switched off; fix it and re-enable it in the portal. The portal shows the latest deliveries with status codes.

Verifying signatures

Compute HMAC-SHA256(secret, timestamp + "." + raw_body) as hex and compare it with the value after v1= in X-DigiSurvey-Signature using a constant-time comparison. Reject events whose timestamp is more than 5 minutes old. Always use the raw request body — not re-serialised JSON.

import crypto from 'node:crypto';
import express from 'express';
const app = express();
const SECRET = process.env.DIGISURVEY_WEBHOOK_SECRET; // whsec_…

app.post('/webhooks/digisurvey', express.raw({ type: 'application/json' }), (req, res) => {
  const ts = req.get('X-DigiSurvey-Timestamp');
  const sig = (req.get('X-DigiSurvey-Signature') || '').replace(/^v1=/, '');
  const expected = crypto.createHmac('sha256', SECRET).update(`${ts}.${req.body}`).digest('hex');
  const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
  if (!fresh || sig.length !== expected.length ||
      !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
    return res.sendStatus(400);
  }
  const event = JSON.parse(req.body);
  queue.add(event);            // handle asynchronously, de-duplicate on event.id
  res.sendStatus(200);
});

End-to-end examples

Push new claims every hour, collect reports

import os, requests, pathlib
BASE = "https://vtlxdqgdrapeyhncbkrj.supabase.co/functions/v1/api"
s = requests.Session()
s.headers["Authorization"] = f"Bearer {os.environ['DIGISURVEY_API_KEY']}"

# 1) send today's claims from your system (max 500 per call)
claims = [{"claim_number": c.no, "vehicle_number": c.reg, "insured_name": c.insured,
           "survey_type": "spot", "surveyor_email": c.surveyor, "external_ref": c.id} for c in new_claims()]
for i in range(0, len(claims), 500):
    r = s.post(f"{BASE}/claims/bulk", json={"claims": claims[i:i+500]}, timeout=60)
    for e in r.json().get("errors", []):
        if e["code"] != "duplicate":
            log_error(claims[i + e["index"]], e["message"])

# 2) download new reports and acknowledge them
for rep in s.get(f"{BASE}/reports", params={"status": "submitted", "limit": 50}, timeout=30).json()["data"]:
    full = s.get(f"{BASE}/reports/{rep['id']}", timeout=30).json()
    folder = pathlib.Path("reports") / (full["summary"].get("claim_number") or rep["id"])
    folder.mkdir(parents=True, exist_ok=True)
    for f in full["files"]:
        if f["download_url"]:
            (folder / f["name"]).write_bytes(requests.get(f["download_url"], timeout=120).content)
    s.post(f"{BASE}/reports/{rep['id']}/ack", json={"message": "Auto-received by ClaimSys"}, timeout=30)

Support & changelog

Questions or a sandbox organisation for testing? Email help@digitaliiisla.co.in or WhatsApp +91 83026 98307. Include the X-Request-Id of the failing call.

VersionChanges
2026-09-26First release: claims (single, bulk, actions), surveyors, reports (detail, HTML, download links, ack/query), webhooks (report.submitted, assignment.updated, claim.created).

We add fields without notice (ignore unknown fields); anything that would break integrations gets a new version date and advance notice to API key owners.