API Reference

IdScanly Identity Document Scanning API - v1

The IdScanly API accepts one or more identity document images in a session, processes them asynchronously, and exposes partial and final recognition results through polling.

Typical integration flow

  1. 1Create a session
  2. 2Upload the first image
  3. 3Optionally upload additional images to the same session
  4. 4Poll the session until isProcessing is false
  5. 5Read merged fields from result.fields

Base URL

text
https://api.idscanly.com/v1

All examples below use:

bash
BASE_URL="https://api.idscanly.com/v1"
API_KEY="idscanly_live_replace_me"
Use HTTPS in every non-local environment. Identity documents and recognition results contain sensitive personal data.

Authentication

Send the API key in the x-api-key header on every request.

http
x-api-key: idscanly_live_replace_me
bash
curl -H "x-api-key: $API_KEY" "$BASE_URL/stats/today"

Missing, invalid, disabled, or expired keys return:

http
HTTP/1.1 401 Unauthorized
Cache-Control: no-store
json
{
  "error": "Invalid or expired API key",
  "code": "UNAUTHORIZED",
  "requestId": "req-123"
}
Do not embed an API key in browser or mobile application code. Call IdScanly from your own backend so the key remains secret.

API keys are provisioned by IdScanly. Contact support and provide the partner or application name, technical contact, and intended environment.

Image Requirements

PropertyDescription
Multipart Content-Typemultipart/form-data
Multipart field nameimage
Base64 Content-Typeapplication/json
Max file size15 MiB
Max decoded base64 image size15 MiB
Max images per session10 (default)
Images per requestOne image per upload request
Supported formatsJPEG, PNG, WebP, HEIC, HEIF

The server validates actual image content, not only the MIME header. HEIC/HEIF uploads are decoded server-side before preprocessing and OCR.

Recommended capture

  • Show the entire document - avoid cut-off corners
  • Use sufficient light without glare
  • Keep text reasonably sharp
  • Use the highest practical resolution
  • Upload front and back as separate images in the same session
Uploading multiple views of the same document is strongly recommended. Images in one session are merged - a clear front can provide names while the back provides MRZ or barcode data.

1. Create a Session

POST/v1/sessions

No request body is required. Returns a session ID and upload URL.

bash
curl -X POST \
  -H "x-api-key: $API_KEY" \
  "$BASE_URL/sessions"

Response 201 Created

json
{
  "success": true,
  "sessionId": "9c5c6e9c-73b6-4976-a84f-527a98552713",
  "isProcessing": false,
  "status": "waiting_for_images",
  "uploadUrl": "/v1/sessions/9c5c6e9c-73b6-4976-a84f-527a98552713/images",
  "resultUrl": "/v1/sessions/9c5c6e9c-73b6-4976-a84f-527a98552713"
}

2. Upload an Image

POST/v1/sessions/{sessionId}/images
bash
SESSION_ID="9c5c6e9c-73b6-4976-a84f-527a98552713"

curl -X POST \
  -H "x-api-key: $API_KEY" \
  -F "image=@front.jpg" \
  "$BASE_URL/sessions/$SESSION_ID/images"

Optional document crop

Enable automatic ID detection and cropping with the crop=true query parameter. Accepted values: true, false, 1, 0. Disabled by default.

bash
curl -X POST \
  -H "x-api-key: $API_KEY" \
  -F "image=@front.jpg" \
  "$BASE_URL/sessions/$SESSION_ID/images?crop=true"

Use crop when the ID occupies only part of a larger photograph. Leave disabled when the image is already tightly framed.

Response 202 Accepted

json
{
  "success": true,
  "imageId": "5f4fc156-c08d-4cd0-b9f6-5112eb13178e",
  "sessionId": "9c5c6e9c-73b6-4976-a84f-527a98552713",
  "status": "queued",
  "cropEnabled": false,
  "resultUrl": "/v1/sessions/9c5c6e9c-73b6-4976-a84f-527a98552713"
}

202 means accepted for processing. It does not mean recognition has finished.

Upload a base64 image

Use the dedicated base64 endpoint when the client cannot send multipart form data.

POST/v1/sessions/{sessionId}/images/base64
bash
IMAGE_BASE64="$(base64 -w 0 front.jpg)"

curl -X POST \
  -H "x-api-key: $API_KEY" \
  -H "content-type: application/json" \
  -d "{\"imageBase64\":\"$IMAGE_BASE64\",\"filename\":\"front.jpg\",\"mimeType\":\"image/jpeg\"}" \
  "$BASE_URL/sessions/$SESSION_ID/images/base64"

JSON body

json
{
  "imageBase64": "/9j/4AAQSkZJRgABAQ...",
  "filename": "front.jpg",
  "mimeType": "image/jpeg"
}

imageBase64 may also be a data URL. When a data URL is used, mimeType is optional. The response is the same 202 Accepted payload returned by multipart uploads.

Base64 expands the request body by roughly 33%, so reverse proxies should allow about 21 MiB of JSON body size for the default 15 MiB decoded image limit.

Add more images

Upload each additional image (e.g. back of the document) in a separate request using the same session ID. Do not create a separate session for each side of the same document.

bash
curl -X POST \
  -H "x-api-key: $API_KEY" \
  -F "image=@back.jpg" \
  "$BASE_URL/sessions/$SESSION_ID/images"

Upload without explicit session creation

A compatibility endpoint that creates a session automatically:

bash
# Creates a new session automatically
curl -X POST \
  -H "x-api-key: $API_KEY" \
  -F "image=@front.jpg" \
  "$BASE_URL/session-images"

# Append to an existing session
curl -X POST \
  -H "x-api-key: $API_KEY" \
  -F "image=@back.jpg" \
  "$BASE_URL/session-images?sessionId=$SESSION_ID"

For new integrations, the explicit POST /sessions → POST /sessions/{sessionId}/images workflow is preferred.

The same compatibility behavior is available for base64 uploads:

bash
curl -X POST \
  -H "x-api-key: $API_KEY" \
  -H "content-type: application/json" \
  -d "{\"imageBase64\":\"$IMAGE_BASE64\",\"filename\":\"front.jpg\",\"mimeType\":\"image/jpeg\"}" \
  "$BASE_URL/session-images/base64"

3. Poll Results

GET/v1/sessions/{sessionId}?view=summary
bash
curl \
  -H "x-api-key: $API_KEY" \
  "$BASE_URL/sessions/$SESSION_ID?view=summary"
Poll every 1.5 to 3 seconds. Stop polling when isProcessing is false. Partial fields appear while processing is still active.

Example response (still processing)

json
{
  "success": true,
  "sessionId": "9c5c6e9c-73b6-4976-a84f-527a98552713",
  "status": "processing",
  "isProcessing": true,
  "summary": {
    "status": "partial_or_complete",
    "isComplete": false,
    "imageCount": 1,
    "successfulImageCount": 1,
    "failedImageCount": 0,
    "fields": {
      "documentType": "identity_card",
      "documentCountry": "HUN",
      "documentNumber": "123456AB",
      "fullName": "EXAMPLE PERSON"
    },
    "fieldConfidences": {
      "documentNumber": 0.96,
      "fullName": 0.93
    },
    "fieldSources": {
      "documentNumber": {
        "value": "123456AB",
        "source": "spatial_label",
        "imageId": "5f4fc156-c08d-4cd0-b9f6-5112eb13178e",
        "confidence": 0.96
      }
    }
  },
  "result": {},
  "images": [
    {
      "imageId": "5f4fc156-c08d-4cd0-b9f6-5112eb13178e",
      "status": "running",
      "sourceName": "front.jpg",
      "fieldCount": 8,
      "avgConfidence": 0.93,
      "mrzFound": false,
      "error": null
    }
  ],
  "createdAt": "2026-06-10T08:00:00.000Z",
  "updatedAt": "2026-06-10T08:00:05.000Z",
  "completedAt": null
}

Full diagnostic view

Omitting ?view=summary returns the full response including per-image OCR blocks, candidates, recognition layer details, preprocessing timings and logs. Use only for controlled diagnostics.

GET/v1/sessions/{sessionId}

Detailed logs are also available from:

GET/v1/sessions/{sessionId}/debug-log

Session Status Values

StatusMeaning
waiting_for_imagesSession exists but no image has been uploaded
processingAt least one image is queued or running
finishedNo image is processing; at least one image completed
errorAll images in the session failed
The authoritative completion condition is isProcessing === false. A finished session may contain individual failed images - check result.failedImageCount and each image status.

Image status values

StatusMeaning
queuedAccepted and waiting to start
runningImage processing is active
doneProcessing completed successfully
errorProcessing failed for this image

Merged Result

The merged result is available under summary.fields (summary view) or result.fields (full view).

PropertyDescription
fieldsSelected normalized field values
fieldConfidencesFinal confidence per selected field (0–1)
fieldSourcesSource layer, image and confidence for each field
documentsPer-image document classifications
mrzValid parsed MRZ results
imagesPer-image result summary
isCompleteWhether every image reached a terminal state

Available field names

text
documentType       documentCountry    documentNumber
documentCode       fullName           surname
givenNames         dateOfBirth        placeOfBirth
nationality        sex                issueDate
dateOfExpiry       issuingAuthority   motherMaidenName
address            categories         can
Not every document contains every field. Unknown documents can still return partial fields. Treat all fields as optional and use fieldConfidences and fieldSources when making automated decisions.

Error Responses

Unknown session 404 Not Found

json
{ "error": "Session not found" }

A session created by one API key is not visible to another API key.

Invalid upload 400 Bad Request

json
{
  "error": "Uploaded file is not a valid supported image",
  "code": "INVALID_UPLOAD",
  "requestId": "req-124"
}

File too large 413 Payload Too Large

json
{
  "error": "request file too large",
  "code": "PAYLOAD_TOO_LARGE",
  "requestId": "req-125"
}

Session image limit 400 Bad Request

json
{
  "error": "Session image limit reached (10)",
  "code": "INVALID_UPLOAD",
  "requestId": "req-126"
}

JavaScript Example

javascript
const baseUrl = 'https://api.idscanly.com/v1';
const apiKey = process.env.IDSCANLY_API_KEY;
const headers = { 'x-api-key': apiKey };

// 1. Create session
const created = await fetch(`${baseUrl}/sessions`, {
  method: 'POST',
  headers,
}).then(readJson);

// 2. Upload image
const form = new FormData();
form.append('image', new Blob([imageBuffer], { type: 'image/jpeg' }), 'front.jpg');

await fetch(`${baseUrl}/sessions/${created.sessionId}/images?crop=false`, {
  method: 'POST',
  headers,
  body: form,
}).then(readJson);

// 3. Poll until done
let session;
do {
  await new Promise((resolve) => setTimeout(resolve, 2000));
  session = await fetch(
    `${baseUrl}/sessions/${created.sessionId}?view=summary`,
    { headers }
  ).then(readJson);
} while (session.isProcessing);

console.log(session.result.fields);

async function readJson(response) {
  const payload = await response.json();
  if (!response.ok) throw new Error(payload.error || `HTTP ${response.status}`);
  return payload;
}

For HEIC images, use the original filename and MIME type:

javascript
form.append(
  'image',
  new Blob([heicBuffer], { type: 'image/heic' }),
  'front.heic'
);

Privacy Notes

Identity document images and extracted fields are sensitive personal data. API responses include Cache-Control: no-store, but clients must also:

  • Avoid browser caching of responses
  • Encrypt data in transit and at rest
  • Avoid placing fields in URLs
  • Avoid logging full response bodies
  • Delete local temporary files promptly
  • Restrict access to diagnostic responses
  • Comply with applicable privacy and retention requirements

Ready to integrate?

Request an API key and start extracting structured data from identity documents.