This document covers the Lazarus custom OCR integration within Document Insights. Lazarus handles two distinct document processing paths: identity document extraction (driver's licences, passports, SSNs) and custom form extraction (any document type mapped via a template). Both paths are covered here.
Integration Architecture Summary
Lazarus is a synchronous OCR service. Unlike MoneyThumb (webhook) or Ocrolus (batch polling), Lazarus returns extracted data in the same HTTP response as the upload. There is no callback URL and no separate retrieval step.
Path 1 — Identity Document Extraction
|
Stage |
Component |
Description |
|---|---|---|
|
|
Validates |
|
|
Groups files by source, creates |
|
|
Routes to DL/Passport or SSN endpoint |
|
|
Parses response via |
Path 2 — Custom Form Extraction
|
Stage |
Component |
Description |
|---|---|---|
|
|
Validates |
|
|
Enqueues |
|
|
Multipart POST to Lazarus generic form endpoint |
|
|
Converts Lazarus key-value response into structured pairs |
|
|
Maps key-value pairs to Salesforce fields and objects |
Key Differences from Other Integrations
|
Characteristic |
Lazarus |
MoneyThumb |
Ocrolus |
Gemini |
|---|---|---|---|---|
|
Auth method |
|
Basic auth → Bearer token |
OAuth2 client credentials |
API key header |
|
Response timing |
Synchronous (same request) |
Async webhook |
Async batch poll |
Synchronous |
|
Request format |
Multipart form data |
Multipart form data |
JSON / Multipart |
JSON (inline base64) |
|
Feature flag |
|
|
|
|
|
Confidence Matrix |
Yes |
No |
No |
No |
Prerequisites and Configuration Checklist
Custom Settings
|
Setting |
Expected Value |
|---|---|
|
|
Lazarus identity document extraction URL |
|
|
Lazarus generic (custom) form extraction URL |
|
|
Organisation ID issued by Lazarus |
|
|
Authentication key issued by Lazarus |
|
|
Must be |
|
|
Must be |
Remote Site Settings
Both the identity endpoint domain and the generic form endpoint domain must be registered under Setup → Remote Site Settings. These may be on different subdomains — register both.
Document_Insight_Template__c Records (Custom Forms Only)
Custom form extraction requires a matching template. Verify:
|
Field |
Requirement |
|---|---|
|
|
Must be checked |
|
|
Must match the source string used in the submission (not |
|
|
Must exactly match the name selected in the UI |
|
|
Must contain valid JSON array of |
|
|
Must be a valid Salesforce object API name |
Document Type Routing Reference
QueueablePDFConvert routes identity documents based on sDocumentType. Custom form documents bypass this class entirely and go through QueueableCallCustomFormService.
|
Document Type |
Method Called |
Child Record Created |
|---|---|---|
|
DL (Driver's Licence) |
|
|
|
Passport |
|
|
|
SSN |
|
|
|
Custom Form |
|
Primary object record (via |
Issue: Request Button Not Visible (Identity Path)
Root Cause 1 — Feature flag disabled The identity document path checks Feature_Entitlement__c.PDF_Insights__c. Navigate to Setup → Custom Settings → Feature Entitlement and ensure PDF_Insights__c is enabled.
Root Cause 2 — Record is a PDF_Insight__c PDFInsightRequestController.showRequestButton() always returns false when the current record is itself a PDF_Insight__c.
Root Cause 3 — ShowRequestSection metadata disabled Check the ShowRequestSection field on the relevant custom metadata record.
Issue: Submission Form Not Accessible (Custom Form Path)
The custom form submission UI checks Feature_Entitlement__c.Custom_Forms__c, not PDF_Insights__c. An org with identity document processing enabled may still have custom forms disabled. Enable Custom_Forms__c in Custom Settings → Feature Entitlement.
Issue: Authentication Failure
Symptom: PDF_Insight__c or Identity_Verification__c is not created. Debug logs show a 401 or 403 HTTP response from Lazarus.
Lazarus uses a header-based authentication scheme — no token exchange, no OAuth flow. Every request includes:
orgId: <value from Custom Setting>
authKey: <value from Custom Setting>
Diagnostic Steps:
-
Verify both
orgIdandauthKeyare populated in Custom Settings. A blank value sends an empty header, which Lazarus rejects. -
Both values are case-sensitive. Confirm there are no leading or trailing spaces — these are invisible in the UI but break authentication.
-
Test in Execute Anonymous:
WS_DocumentInsight ws = new WS_DocumentInsight();
// Trigger a minimal call and inspect the HTTP response
Check the debug log for the raw response body — Lazarus typically returns a descriptive error when credentials are invalid.
-
If credentials were recently rotated by the Lazarus administrator, update both Custom Setting values. Unlike OAuth2, there is no fallback or retry on auth failure — the job will fail immediately.
Issue: Endpoint Misconfiguration (Connection Error)
Symptom: System.CalloutException in Apex logs rather than an HTTP status code. No network response received.
Root Cause 1 — Remote Site Settings missing The Lazarus endpoint domain is not registered. Add it at Setup → Remote Site Settings.
Root Cause 2 — Wrong endpoint URL Form_Identification_Endpoint__c and Generic_Form_Endpoint__c are separate endpoints. Using the identity endpoint for a custom form call (or vice versa) results in either a connection error or an unexpected response structure that fails parsing.
Verification: Check which endpoint is being called against which document type. callFormExtractionService and callFormSSNService use Form_Identification_Endpoint__c. callCustomFormService uses Generic_Form_Endpoint__c.
Root Cause 3 — Missing Version header on generic form endpoint customFormAPI() sends a Version: 2 header on the generic form endpoint. If the endpoint has been updated to a newer version but the header value has not been updated in code or Custom Settings, Lazarus may return a 400 or serve an older API version with a different response schema.
Issue: Wrong Document Type Routed (Identity Path)
Symptom: A driver's licence is processed but an Identity_Verification__c is created with only SSN/name fields (or vice versa).
QueueablePDFConvert routes based on sDocumentType:
-
sDocumentType = 'DL'or'Passport'→callFormExtractionService→ full identity extraction -
sDocumentType = 'SSN'→callFormSSNService→ SSN and name only
If sDocumentType is set incorrectly at submission, the wrong extraction method is called and only a subset of fields are populated.
Resolution: Verify the document type selection in the submission UI. The sDocumentType value must match exactly — check for case sensitivity and spacing. If the submission UI does not allow explicit type selection and auto-detects the type, verify the auto-detection logic.
Issue: Identity_Verification__c Created but Fields Are Blank
Symptom: An Identity_Verification__c record exists but most or all fields are empty after processing.
callFormExtractionService parses the Lazarus response via DocumentExtractionParser. The parser maps response keys to Salesforce field API names. Blank fields indicate either that Lazarus could not extract those values from the document, or that the parser mapping is missing entries for the returned keys.
Diagnostic Steps:
-
Enable Apex debug logging for the Queueable user and reproduce the submission.
-
In the debug log, find the raw HTTP response from
Form_Identification_Endpoint__c. Check:-
Did Lazarus return a
200with data, or a success wrapper with empty fields? -
Are the field keys in the response present in
DocumentExtractionParser?
-
-
Check the document quality — Lazarus OCR accuracy degrades significantly on:
-
Low-resolution images (below 300 DPI)
-
Glare, shadow, or partial obstructions on the ID
-
Expired IDs with faded printing
-
Non-standard ID formats from unsupported regions
-
-
If the raw response contains data but fields are blank in Salesforce, the parser mapping is missing those keys. This requires a code change to
DocumentExtractionParserto add the new field mappings.
Issue: Custom Form Extraction Fails to Find Template
Symptom: PDF_Insight__c is created with Status = Failed. Debug logs show a template lookup returning no results inside callCustomFormService.
WS_DocumentInsight.callCustomFormService queries Document_Insight_Template__c using values from GenericFormWrapper:
-
sTemplateNamemust matchTemplate_Name__cexactly -
sTemplatePrimaryObjectmust matchPrimary_Object__cexactly -
Active__cmust be true
Common causes:
|
Cause |
Resolution |
|---|---|
|
Template name has trailing space or different casing |
Edit the template record and verify exact spelling |
|
Template was deactivated |
Set |
|
Template source does not match routing |
If the template's |
|
Wrong primary object selected at submission |
The object selected in the UI populates |
Issue: Custom Form Extraction Returns a Response but PDF_Insight__c Shows "Failed"
Symptom: The Lazarus callout succeeds (HTTP 200) but PDF_Insight__c is set to Status = Failed.
On failure inside callCustomFormService, a PDF_Insight__c is created with Status = Failed and Missing_Fields__c populated. The failure may occur at the parsing stage, not the callout stage.
Root Cause 1 — GenericFormInsightParser.parse() failed GenericFormInsightParser.parse() processes the Lazarus key-value response. If the response structure does not match the expected format (e.g., Lazarus changed its response schema), parsing throws an exception.
Resolution: Enable debug logging and check the raw Lazarus response. Confirm it matches the format GenericFormInsightParser expects — a key-value pair structure where each entry has a recognisable key and value field.
Root Cause 2 — ProcessCustomFormParser exception After parsing, ProcessCustomFormParser.parseCustomFormJSON() applies the template field mapping. Common exceptions:
-
sFieldAPINameinData_Field_Mapping__creferences a field that no longer exists on the target object -
sObjectNamein a configuration entry is not a valid or accessible object API name -
Running user lacks Create/Edit permissions on the target object
Check Missing_Fields__c on the PDF_Insight__c record for clues about which fields failed.
Issue: Picklist Fields Not Populated (Custom Forms)
Symptom: Text and date fields are written correctly but picklist fields remain blank after custom form extraction.
Lazarus uses a specific syntax for picklist values in its response. Unlike Gemini (which returns direct values), Lazarus returns picklist selections in one of two formats:
-
:selected:— indicates the field was checked/selected -
true— boolean-style selection
ProcessCustomFormParser handles both formats for Lazarus picklist fields. If a picklist value is not populating:
-
Check the raw Lazarus response to see what value is being returned for that key.
-
If Lazarus is returning a plain string (e.g.,
"Married") rather than:selected:ortrue, the parser will not coerce it correctly for a picklist field — this requires reviewing the parser logic or adjusting the template'ssDataTypetoTextfor that field. -
Verify the
sPicklistValueinData_Field_Mapping__cmatches the API value of the picklist entry exactly (not the label).
Issue: QueueablePDFConvert Chains Not Completing
Symptom: Some files in a multi-file submission are processed but others are not. Processing stops partway through.
QueueablePDFConvert processes one file per Queueable execution and chains itself for remaining files. Each file results in a separate Queueable job. If chaining stops:
Root Cause 1 — Queueable job limit reached Salesforce limits the number of chained Queueable jobs. In a sandbox, this limit is lower than production. If many files are submitted simultaneously across multiple submissions, the org-wide Queueable limit may be hit, and later jobs are rejected.
Resolution: Check Setup → Apex Jobs for QueueablePDFConvert jobs with status Held or Failed. Failed jobs with System.AsyncException: Maximum stack depth has been reached confirm the limit issue. Reduce batch submission size or stagger submissions.
Root Cause 2 — Earlier job threw unhandled exception If a Queueable job fails without catching the exception, the chain stops at that point. The remaining files are never processed.
Resolution: In Apex Jobs, find the failed job and read the error message. Fix the underlying issue (invalid file, auth error, endpoint down) and resubmit only the remaining files.
Root Cause 3 — File removed from ContentDocument between submission and processing If a file is deleted after submission but before its Queueable job runs, WS_DocumentInsight cannot load the ContentVersion.VersionData. This causes a null reference exception that breaks the chain.
Issue: PDF_Insight__c Stuck in "Processing" (Lazarus / Identity Path)
Unlike MoneyThumb and Ocrolus, Lazarus should never leave a record in Processing indefinitely because the response is synchronous. If a record is stuck in Processing:
Root Cause 1 — QueueablePDFConvert never ran The job was enqueued but never executed — possible in orgs with high async job volume. Check Setup → Apex Jobs for the QueueablePDFConvert job. If status is Queued or Holding, the job is waiting for an execution slot.
Root Cause 2 — Job ran but threw a governor limit exception CPU time, heap size, or SOQL limits exceeded inside the Queueable. The job is marked Failed in Apex Jobs. The PDF_Insight__c status was set to Processing before the callout and was never updated to Completed or Failed because the exception interrupted execution before the status update DML.
Resolution: Check Apex Jobs for the failure. Fix the limit issue (usually caused by overly large files or excessive SOQL inside the job), then manually update PDF_Insight__c.Status__c to Failed and resubmit.
Root Cause 3 — Governor limit hit mid-chain For a multi-file submission, early files complete but a later file hits a limit. The completed files' PDF_Insight__c status is Completed while the parent record may still show Processing if it aggregates child statuses.
Issue: Confidence Matrix Not Generating or Incomplete
Symptom: The Confidence Matrix tab shows no data, or a spinner that does not resolve.
The Confidence Matrix is built from Success*.json ContentVersion files attached to completed Lazarus PDF_Insight__c records.
Step 1 — Confirm Source Records Exist
CustomDocumentInsightController.asyncGetConfidenceMatrix(recordId) queries for Lazarus PDF_Insight__c records with Status = Completed on the parent record. If no such records exist, the matrix cannot be built.
SELECT Id, Status__c, Source__c
FROM PDF_Insight__c
WHERE <ParentLookupField__c> = '<parentRecordId>'
AND Source__c = 'Lazarus'
AND Status__c = 'Completed'
Step 2 — Confirm Success JSON Files Exist
For each completed Lazarus record, check for a ContentVersion named Success*.json:
SELECT Id, Title, ContentDocumentId
FROM ContentVersion
WHERE Title LIKE 'Success%'
AND FileExtension = 'json'
AND FirstPublishLocationId = '<PDF_Insight__c Id>'
If no Success*.json files exist, the raw Lazarus response was not saved. Check whether ConfigurationService.rawResponseFile() is enabled — this controls whether raw responses are persisted.
Step 3 — Confirm @future Method Ran
asyncGetConfidenceMatrix is an @future method. It cannot be chained from another async context. If it was called from a Queueable or another @future method, Salesforce silently drops the call with no error.
Check whether createConfidenceMatrixTable was invoked from a synchronous context (LWC controller call) vs an async context. It should only be called from a user-initiated LWC action.
Step 4 — Confirm Platform Event Was Published
On completion, asyncGetConfidenceMatrix publishes a Custom_Documents_Event__e platform event to signal the UI to refresh. If the event was published but the UI did not update, check that the LWC subscriber for Custom_Documents_Event__e is correctly registered and the running user has access to the platform event.
Step 5 — Template Data Field Mapping
The confidence matrix compares Lazarus extraction values against threshold values defined in the template's Data_Field_Mapping__c. If the template mapping has been modified since the last successful extraction, the matrix columns may not align with the stored extraction data.
Issue: Raw Response File Not Created
Symptom: No ContentVersion file is attached to the PDF_Insight__c record after processing. This blocks confidence matrix generation and makes debugging extraction failures difficult.
ProcessCustomFormParser saves the raw response via ConfigurationService.rawResponseFile(). This method checks a configuration flag before creating the file.
Resolution: Check ConfigurationService settings and ensure raw response file saving is enabled. In production environments, this may be disabled to reduce storage consumption — consider enabling it temporarily during debugging, then disabling again.
Debugging Workflow (Quick Reference)
Identity_Verification__c not created
│
├─ Check: Apex Jobs for QueueablePDFConvert
│ ├─ Failed → Check error message
│ │ ├─ Auth error (401/403) → Verify orgId/authKey in Custom Settings
│ │ ├─ Connection error → Check Remote Site Settings
│ │ └─ Null response → Endpoint URL wrong or document unreadable
│ └─ Never appeared → Submission failed before enqueue
│ Check: PDF_Insight__c created? Auth on callPDFInsightService?
Identity_Verification__c created but fields blank
│
├─ Check: Document quality (resolution, clarity, supported format)
├─ Check: sDocumentType correct (DL/Passport vs SSN routes differently)
└─ Check: DocumentExtractionParser has mapping for all response keys
PDF_Insight__c Status = Failed (Custom Form path)
│
├─ Check: Missing_Fields__c — which keys had no mapping?
├─ Check: Template exists and Active = true?
├─ Check: Template_Name__c and Primary_Object__c match exactly?
└─ Check: Running user has Create/Edit on target object?
Confidence Matrix blank
│
├─ Check: Completed Lazarus PDF_Insight__c records exist on parent?
├─ Check: Success*.json ContentVersion attached to those records?
├─ Check: asyncGetConfidenceMatrix called from sync context?
└─ Check: Custom_Documents_Event__e published and LWC subscribed?
Relevant Apex Classes Reference
|
Class |
Responsibility |
|---|---|
|
|
LWC controller for identity document submission and feature check |
|
|
LWC controller for custom form submission, confidence matrix, template management |
|
|
All Lazarus HTTP callouts — identity extraction, SSN extraction, custom form extraction |
|
|
Async per-file routing for identity documents; chains per file |
|
|
Async per-document routing for custom forms; chains per item |
|
|
Parses Lazarus identity document response |
|
|
Parses Lazarus custom form key-value response |
|
|
Generic field mapping engine — creates/updates SF records from parsed key-value pairs |
|
|
Raw response file toggle, FLS enforcement, test mode settings |
|
|
Builds multipart form-data request bodies for Lazarus upload calls |
Relevant Objects and Metadata Reference
|
Object / Metadata |
Role |
|---|---|
|
|
Central audit record — one per submission; tracks status, missing fields, duplicate keys |
|
|
Stores extracted identity document fields (name, DOB, address, ID number, expiry) |
|
|
Template config for custom forms — prompt, field mapping JSON, related object config |
|
|
Feature flag for identity document path |
|
|
Feature flag for custom form path |
|
|
Carries all context through queueable chain (template name, object, document ID, source) |
|
|
Platform event published on confidence matrix completion to trigger UI refresh |