Docs
2D-DOC / VDS

Document Comparison

Verify that 2D-DOC barcode data matches the source document content

The compare endpoint combines barcode verification with document text extraction to verify that the barcode data matches what's printed on the document. This detects fraudulent modifications to the visible document content.

Request

POST /2ddoc/compare
Authorization: Bearer oat_your_publishable_key
Content-Type: multipart/form-data

file: <PDF or image file>
forceOcr: true
FieldTypeRequiredDescription
filefileYesPDF or image (pdf, png, jpg, jpeg, gif, webp, bmp, tiff), max 20MB
forceOcrstringNo"true" to force OCR even when the PDF has a text layer
asyncstringNo"true" to queue the comparison and answer 202 at once, like the Prefer: respond-async header. See asynchronous processing

Comparison requires a detected 2D-DOC barcode. If no barcode is found, the response is { "found": false, "error": "..." } (same as scan). If the file's only code is not a 2D-DOC, the response is { "found": true, "notTwoDDoc": true, "verification": { ... }, "documents": [...] } with the failed verdict described in scan, and no comparison.

Response

The barcode result lives under verification (the full verify response), and the text comparison under comparison. There is no top-level success field.

These top-level fields hold the first 2D-DOC of the file. documents lists every 2D-DOC in page order, the first one included, each with its page, verification and comparison. When a PDF holds several codes, each one is compared with the text of its own page only. Each 2D-DOC counts as one verification.

{
  "found": true,
  "page": 1,
  "verification": {
    "status": "valid",
    "verification": {
      "SIGNATURE": [
        { "status": "✅", "check": "Signature is cryptographically valid" }
      ]
    },
    "document": {
      "header": { "formatVersion": "v1", "version": "04", "caId": "FR04", "certificateId": "0001", "documentTypeCode": "01", "documentEmissionDate": "2026-01-15T00:00:00.000Z", "signatureDate": "2026-01-15T00:00:00.000Z", "signatureDateFormatted": "15/01/2026" },
      "documentType": "Facture",
      "fields": { "invoice": [ { "id": "10", "value": "MME/SPECIMEN/NATACHA", "rawValue": "MME/SPECIMEN/NATACHA", "truncated": false, "label": "Nom du bénéficiaire", "category": "invoice" } ] },
      "data": { "beneficiary_name": "MME/SPECIMEN/NATACHA" }
    },
    "certificate": { "providerTradeName": "Certigna", "trusted": true }
  },
  "comparison": {
    "success": true,
    "overallConfidence": 0.95,
    "fieldsMatched": 5,
    "totalFields": 6,
    "fields": [
      {
        "fieldId": "10",
        "label": "Nom du bénéficiaire",
        "expectedValue": "MME/SPECIMEN/NATACHA",
        "found": true,
        "confidence": 1.0,
        "matchType": "exact",
        "matchedText": "MME SPECIMEN NATACHA"
      },
      {
        "fieldId": "22",
        "label": "Ligne 4 adresse point de service",
        "expectedValue": "145 AV DES SPECIMENS",
        "found": true,
        "confidence": 0.9,
        "matchType": "normalized",
        "matchedText": "145 Avenue des Spécimens"
      }
    ],
    "extraction": {
      "method": "pdf-text",
      "textLength": 4523,
      "text": "FACTURE\nMME SPECIMEN NATACHA\n145 Avenue des Spécimens\n..."
    }
  },
  "documents": [
    { "page": 1, "verification": { "…": "…" }, "comparison": { "…": "…" } }
  ]
}

Comparison fields

FieldTypeDescription
successbooleanWhether text extraction and comparison ran successfully
overallConfidencenumberOverall confidence score (0-1)
fieldsMatchednumberFields found with confidence at or above 0.7
totalFieldsnumberTotal number of fields compared
fieldsarrayPer-field comparison results
extraction.methodstringpdf-text+ocr for a PDF with a text layer (the text layer and the OCR of the code's page, merged), ocr for a scan or a photo, pdf-text when the page could not be read by OCR
extraction.textLengthnumberLength of the extracted text
extraction.textstringRaw extracted text, useful for debugging OCR issues

Confidence scores

Each field gets a confidence score based on how it was matched:

Match typeConfidenceDescription
exact1.0Exact string match (including date/numeric format variations)
normalized0.9-0.95Match after normalization (accents, abbreviations)
partial0.5-0.85Partial word match (60%+ words found)
not-found0.0Field not found in document text
mismatch0.0The document prints another value right after the field's label; matchedText holds that value

Overall confidence

The overallConfidence score reflects the quality of fields that were found on the document. Fields not found are treated as neutral; issuers often encode data in the barcode (country codes, internal references) that doesn't appear on the printed document, and this should not lower the trust score.

The scoring policy:

  • All found fields match perfectly gives 100%: missing fields don't penalize the score
  • Weak matches are very damaging: a field partially found but with low confidence (a potential mismatch) is amplified as a penalty. A wrong value is far more suspicious than a missing one
  • A mismatch makes the score 0: when the page prints another value than the code right after a field's label (Nombre de parts : 2,00 against a signed 1), the document was altered after it was issued, whatever else matches
  • Majority missing triggers a coverage penalty: if more than 70% of fields are absent and there are many fields, a penalty applies to catch "wrong document entirely" scenarios

Text extraction

The API uses two extraction methods:

  1. PDF text layer and OCR of the page (pdf-text+ocr): the text layer is read, and the page holding the code is always read by OCR too. A forger can draw a value over the page or leave the genuine values in the text layer under a retouched page: the two readings are merged, and a value contradicted in either is a mismatch
  2. OCR (ocr): for scanned documents or images, with multiple Tesseract passes

Use the forceOcr form field to skip the PDF text layer and run OCR anyway.

Matching strategies

French documents have format variations that the comparison handles:

StrategyExample
Date formats31072025 matches 31/07/2025
Numeric spacing3009811657195 matches 30 09 811 657 195
OCR corrections3O O9 corrected to 30 09 (O/0 confusion)
Accent normalizationJérôme matches Jerome
Whole words only1 is not found in 19, 2025 or 1,5; a value under 3 characters counts only right after its label
Address abbreviationsAV matches Avenue, BD matches Boulevard

Use cases

  • KYC verification: verify that uploaded identity documents haven't been tampered with
  • Tax document validation: confirm tax notice data before processing
  • Fraud detection: flag documents where visible content doesn't match the barcode data