Skip to main content

Overview

This guide shows you how to verify a government-issued identity document (passport, driver’s license, national ID) using only the Beltic API. This is the fastest path to document verification — no SDK integration, no session management, and no client-side setup required. You will:
  1. Create a document/idv with file declarations and get presigned upload URLs
  2. Upload the document images to the presigned URLs
After the files are uploaded, the document is automatically processed — a verification is created, authenticity checks are run, and data is extracted from the document.

Prerequisites

  • A Beltic API key with documents:write and verifications:write permissions
  • A document image (JPEG or PNG) of a government-issued ID

Step 1: Create the Document

Create a document/idv resource with the files you intend to upload declared in the request body. Since we’re running a standalone verification, we don’t need to link it to a session or account.
curl -X POST https://api.beltic.com/v1/identity/documents/idvs \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "type": "document/idv",
      "attributes": {
        "files": [
          {
            "filename": "id_card.jpeg"
          }
        ]
      }
    }
  }'
The response contains the document ID and presigned upload URLs for each declared file:
{
  "data": {
    "type": "document/idv",
    "id": "doc_01HQ...",
    "attributes": {
      "status": "pending",
      "document_type": null,
      "files": [
        {
          "id": "file_01...",
          "filename": "id_card.jpeg",
          "status": "pending_upload"
        }
      ],
      "created_at": "2025-01-15T10:30:00Z"
    }
  },
  "meta": {
    "files": [
      {
        "id": "file_01...",
        "presigned_upload_url": "https://files.beltic.com/doc_01HQ.../id_card.jpeg?X-Amz-Signature=...",
        "expires_in": 3600
      }
    ]
  }
}
For two-sided documents (like a driver’s license or national ID card), declare both files in the request. This improves extraction accuracy and enables additional security checks.
{
  "data": {
    "type": "document/idv",
    "attributes": {
      "files": [
        { "filename": "id_card_front.jpeg" },
        { "filename": "id_card_back.jpeg" }
      ]
    }
  }
}

Step 2: Upload the Document Images

Upload each file to its corresponding presigned URL using a PUT request:
curl -X PUT "{presigned_upload_url}" \
  -H "Content-Type: image/jpeg" \
  --data-binary @id_card.jpeg
The presigned URL expires after the time specified in expires_in (typically 1 hour). Upload your files before it expires.
Once all files are uploaded, the document is automatically processed — Beltic runs the verification, performs authenticity checks, and extracts identity data from the document. No additional API call is needed.

Retrieving the Results

After uploading, poll the document to check when processing is complete:
curl -X GET https://api.beltic.com/v1/identity/documents/idvs/{document_id} \
  -H "X-Api-Key: YOUR_API_KEY"
When the status changes from pending to processed, the document response includes all extracted data and the linked verification with check results:
{
  "data": {
    "type": "document/idv",
    "id": "doc_01HQ...",
    "attributes": {
      "status": "processed",
      "document_type": "passport",
      "name": {
        "first": "Jane",
        "middle": null,
        "last": "Doe"
      },
      "birth_date": "1990-05-15",
      "sex": "female",
      "document_number": "AB1234567",
      "issue_date": "2020-03-01",
      "expiry_date": "2030-03-01",
      "issuing_authority": "Department of State",
      "nationality": "US",
      "issuing_country": "US",
      "address": null,
      "front_side": {
        "id": "file_01...",
        "filename": "front_cropped.jpg"
      },
      "portrait": {
        "id": "file_02...",
        "filename": "portrait.jpg"
      },
      "back_side": null,
      "signature": null,
      "created_at": "2025-01-15T10:30:00Z",
      "submitted_at": "2025-01-15T10:30:05Z",
      "processed_at": "2025-01-15T10:30:08Z"
    }
  }
}
You can also retrieve the verification directly to inspect individual check results:
curl -X GET "https://api.beltic.com/v1/identity/verifications?filter[document_id]=doc_01HQ..." \
  -H "X-Api-Key: YOUR_API_KEY"

Verification Response

{
  "data": [
    {
      "type": "verification/idv",
      "id": "ver_01HQ...",
      "attributes": {
        "status": "passed",
        "capture_method": "api",
        "document_type": "passport",
        "name": {
          "first": "Jane",
          "middle": null,
          "last": "Doe"
        },
        "birth_date": "1990-05-15",
        "document_number": "AB1234567",
        "expiry_date": "2030-03-01",
        "issuing_country": "US",
        "checks": [
          {
            "name": "status_optical",
            "status": "passed",
            "requirement": "required",
            "reasons": [],
            "metadata": {}
          },
          {
            "name": "optical_expiry",
            "status": "passed",
            "requirement": "required",
            "reasons": [],
            "metadata": {}
          },
          {
            "name": "optical_mrz",
            "status": "passed",
            "requirement": "required",
            "reasons": [],
            "metadata": {}
          },
          {
            "name": "optical_doc_type",
            "status": "passed",
            "requirement": "required",
            "reasons": [],
            "metadata": {}
          },
          {
            "name": "image_quality_glare",
            "status": "not_applicable",
            "requirement": "not_required",
            "reasons": [],
            "metadata": {}
          }
        ],
        "created_at": "2025-01-15T10:30:05Z",
        "completed_at": "2025-01-15T10:30:08Z"
      }
    }
  ]
}

Interpreting Results

Overall Status

StatusWhat it meansWhat to do
passedThe document passed all required checksAccept the verification
failedOne or more required checks failedReject or investigate further
requires_retryImage quality issues prevented verificationAsk the user to retake the photo

Key Checks to Monitor

For most use cases, pay attention to these checks:
  • status_optical — Was the document successfully read?
  • optical_expiry — Is the document expired?
  • optical_mrz — Does the MRZ (machine-readable zone) validate?
  • optical_security — Do security features check out?
  • image_quality_* — Was the image quality sufficient?

Handling Failures

const verification = response.data[0];
const { status, checks } = verification.attributes;

if (status === 'passed') {
  console.log('Document verified successfully');
  console.log('Name:', verification.attributes.name);
  console.log('DOB:', verification.attributes.birth_date);
  console.log('Doc #:', verification.attributes.document_number);
} else if (status === 'requires_retry') {
  const retryChecks = checks.filter(c => c.requirement === 'requires_retry');
  console.log('Please retake the photo. Issues:', retryChecks.map(c => c.name));
} else if (status === 'failed') {
  const failedChecks = checks.filter(c => c.status === 'failed');
  console.log('Verification failed:', failedChecks.map(c => ({
    check: c.name,
    reasons: c.reasons
  })));
}

Complete Example

Here’s a complete Node.js example that runs a document verification end-to-end:
import fs from 'fs';

const API_KEY = 'YOUR_API_KEY';
const BASE_URL = 'https://api.beltic.com/v1/identity';

async function verifyDocument(imagePath) {
  const headers = {
    'X-Api-Key': API_KEY,
    'Content-Type': 'application/json'
  };

  const filename = imagePath.split('/').pop();

  // Step 1: Create the document with file declarations
  const docResponse = await fetch(`${BASE_URL}/documents/idvs`, {
    method: 'POST',
    headers,
    body: JSON.stringify({
      data: {
        type: 'document/idv',
        attributes: {
          files: [{ filename }]
        }
      }
    })
  });
  const doc = await docResponse.json();
  const documentId = doc.data.id;
  const uploadUrl = doc.meta.files[0].presigned_upload_url;
  console.log(`Created document: ${documentId}`);

  // Step 2: Upload the file
  await fetch(uploadUrl, {
    method: 'PUT',
    headers: { 'Content-Type': 'image/jpeg' },
    body: fs.readFileSync(imagePath)
  });
  console.log('File uploaded — processing automatically...');

  // Step 3: Poll until processed
  let document;
  while (true) {
    const statusResponse = await fetch(`${BASE_URL}/documents/idvs/${documentId}`, {
      headers: { 'X-Api-Key': API_KEY }
    });
    document = await statusResponse.json();
    const status = document.data.attributes.status;

    if (status === 'processed') {
      console.log('Document processed!');
      break;
    } else if (status === 'failed') {
      throw new Error('Document processing failed');
    }

    console.log(`Status: ${status} — waiting...`);
    await new Promise(resolve => setTimeout(resolve, 3000));
  }

  // Print extracted data
  const attrs = document.data.attributes;
  console.log(`Name: ${attrs.name?.first} ${attrs.name?.last}`);
  console.log(`DOB: ${attrs.birth_date}`);
  console.log(`Document #: ${attrs.document_number}`);
  console.log(`Expiry: ${attrs.expiry_date}`);

  return document;
}

// Usage
verifyDocument('./id_card.jpeg');

Tips for Best Results

  • Use high-resolution images (at least 300 DPI)
  • Ensure even lighting with no glare or shadows
  • Capture the full document with all edges visible
  • Place the document on a dark, contrasting background
For documents with information on both sides (e.g., driver’s licenses), declare and upload both images. This enables:
  • More complete data extraction
  • Additional security checks
  • Better overall verification accuracy
The IDV verification engine supports a wide range of government-issued documents:
  • Passports
  • Driver’s licenses
  • National identity cards
  • Residence permits
  • Travel documents
  • And many more

Next Steps