Document Insights

Ocrolus and Ocrolus Instant Integrations - Troubleshooting Guide

This document covers the Ocrolus and Ocrolus Instant integrations within Document Insights. Both integrations share the same Apex codebase and differ only in the book creation flag and processing speed. Unless stated otherwise, all guidance applies to both sources.


Integration Architecture Summary

Ocrolus follows a synchronous polling model, not a webhook model. There is no callback URL — Salesforce uploads documents and later retrieves results via batch processing.

Stage

Component

Description

  1. Feature Check

PDFInsightRequestController

Validates Feature_Entitlement__c.PDF_Insights__c

  1. Book Creation

WS_Ocrolus.createBook

Creates an Ocrolus "book" (application container)

  1. Document Upload

QueueablePDFConvertWS_Ocrolus

Routes each file to the correct upload endpoint based on document type

  1. Analytics Retrieval

InvocableRetrievePDFAnalyticsBatch_OcrolusReprocessingJob

Batch polls Ocrolus for results

  1. Post-Processing

WS_Ocrolus.getPaystubData, getFraudSignals, getFormData

Fetches detailed sub-records per document type

  1. Trigger Aggregation

PdfInsightTriggerHandler

Aggregates statuses and averages onto PDF_Insight__c

Ocrolus vs Ocrolus Instant

The only architectural difference is the bInstant boolean passed to WS_Ocrolus.createBook():

Field

Ocrolus

Ocrolus Instant

Source__c on PDF_Insight__c

Ocrolus

Ocrolus Instant

bInstant flag

false

true

Processing speed

Standard (minutes to hours)

Near real-time

Book endpoint behaviour

Standard book creation

Instant-mode book creation

All other code paths — upload, retrieval, parsing — are identical.


Prerequisites and Configuration Checklist

Custom Settings / Named Credentials

Setting

Expected Value

Auth_Token_Endpoint__c

Ocrolus OAuth2 token URL

Create_Book_Endpoint__c

Ocrolus book creation URL

Upload_Mixed_Document_Endpoint__c

Endpoint for bank/paystub documents

Upload_Plaid_JSON_Endpoint__c

Endpoint for Plaid JSON payloads

Upload_PDF_Endpoint__c

Endpoint for identity/tax/income PDFs

client_id

Ocrolus OAuth2 client ID

client_secret

Ocrolus OAuth2 client secret

audience

Ocrolus OAuth2 audience value

Feature_Entitlement__c.PDF_Insights__c

Must be true

Remote Site Settings

All Ocrolus endpoint domains must be registered under Setup → Remote Site Settings. The auth endpoint domain is often different from the API endpoint domain — register both.

Ocrolus_Response_Field_Mapping__mdt

This custom metadata type drives field mapping for getFormData() (identity and tax documents). If records are missing from this metadata, form fields will not be written to Identity_Verification__c or Income_Insight__c.

Navigate to Setup → Custom Metadata Types → Ocrolus Response Field Mapping and verify active records exist for the document types in use.


Document Type Routing Reference

QueueablePDFConvert routes each document to a different upload method based on sDocumentType and sFormType. Misidentifying a document type at submission is the most common cause of data ending up in the wrong object.

Document Type

sFormType

Upload Method

Child Record Created

Bank statement

uploadMixedDocuments

Statement_Summary__c

PayStub

Paystub

uploadMixedDocuments

Income_Insight__c

SSA-1099

SSA-1099

uploadMixedDocuments

Income_Insight__c

Plaid JSON

— (bPlaid=true)

uploadMixedDocuments → Plaid endpoint

PDF_Insight_Item__c

Identity (DL/Passport)

uploadPDFtoBook

Identity_Verification__c

Tax (W2, 1040, etc.)

uploadPDFtoBook

Income_Insight__c

Other income

uploadPDFtoBook

Income_Insight__c


Issue: Request Button Not Visible

Root Cause 1 — Feature flag disabled PDFInsightRequestController.isFeatureEnabled() checks Feature_Entitlement__c.PDF_Insights__c. Navigate to Setup → Custom Settings → Feature Entitlement and ensure the flag is enabled for the org or the relevant profile.

Root Cause 2 — Record is a PDF_Insight__c showRequestButton() returns false when the current record is itself a PDF_Insight__c. This is by design.

Root Cause 3 — ShowRequestSection metadata disabled Check the ShowRequestSection field in the relevant custom metadata record. This is evaluated after the feature flag check.


Issue: Submission Creates No PDF_Insight__c (Fails Silently)

Root Cause 1 — Existing record reuse Unlike MoneyThumb (which has a time-based reuse window), Ocrolus always reuses an existing PDF_Insight__c for the same parent record if one exists in any status. checkExistingPDFInsightRequest() will return the existing record ID rather than creating a new one.

Check: Query for existing PDF_Insight__c records on the parent:

SELECT Id, Status__c, Source__c, CreatedDate
FROM PDF_Insight__c
WHERE <ParentLookupField__c> = '<parentRecordId>'
  AND Source__c IN ('Ocrolus', 'Ocrolus Instant')
ORDER BY CreatedDate DESC

If a record exists and should be replaced, it must be manually deleted before resubmitting.

Root Cause 2 — No files selected or files exceed size limit getListContentVersion() filters files by size. Files exceeding the configured size limit are excluded from batches. Verify ContentVersion.ContentSize for the files being submitted.

Root Cause 3 — FLS or sharing prevents record creation If ConfigurationService has FLS enforcement enabled, the running user must have Create access on PDF_Insight__c and all relevant fields. Check field-level security for the Ocrolus-specific fields (Book_UUID__c, Application_Id__c, Source__c).


Issue: Authentication Failure (401 from Ocrolus)

Symptom: PDF_Insight__c status is Failed, debug logs show a 401 HTTP response.

Ocrolus uses OAuth2 client credentials. WS_Ocrolus.getAuthToken() POSTs to Auth_Token_Endpoint__c with:

grant_type=client_credentials
audience=<audience value>
client_id=<client_id>
client_secret=<client_secret>

Diagnostic Steps:

  1. Confirm all four values are correctly populated in Custom Settings.

  2. The audience field is frequently missed or set incorrectly — it is an Ocrolus-specific parameter that is not standard in all OAuth2 flows.

  3. Test in Execute Anonymous:

WS_Ocrolus ws = new WS_Ocrolus();
String token = ws.getAuthToken();
System.debug('Token: ' + token);

A null result means the auth call failed. Check the debug log for the raw HTTP response body, which will contain the Ocrolus error message.

  1. Tokens are fetched fresh per Queueable execution — there is no token caching. If credentials are valid but tokens are being rejected, check if Ocrolus has IP allowlisting enabled on their side and whether Salesforce's outbound IP ranges are whitelisted.


Issue: Book Creation Fails

Symptom: Queueable fails immediately after auth succeeds. No PDF_Insight__c record is updated with an Application_Id__c.

WS_Ocrolus.createBook() POSTs JSON to Create_Book_Endpoint__c and parses the response via Ocrolus_CreateBookParser.

Common causes:

Cause

Indicator

Endpoint URL misconfigured

404 or connection refused in logs

Malformed JSON body

400 response from Ocrolus

Account not provisioned for Instant

403 when bInstant = true

Parser failure on unexpected response schema

Null Application_Id__c after call

Resolution for Ocrolus Instant 403: Ocrolus Instant is a separately provisioned feature. If the org is not entitled for Instant processing, Ocrolus will return a 403. Verify with Ocrolus that the account has Instant access enabled. If not, use standard Ocrolus as the source.


Issue: Document Upload Fails

Symptom: Book is created (Application_Id exists on PDF_Insight__c) but no child records (Statement_Summary__c, Identity_Verification__c, etc.) are created. Status remains Processing or moves to Failed.

Sub-issue: Wrong upload endpoint used

The routing in QueueablePDFConvert depends on sDocumentType being set correctly at submission. If a bank statement is submitted with sDocumentType = 'Identity', it will be sent to uploadPDFtoBook instead of uploadMixedDocuments, and an Identity_Verification__c record will be created with blank fields.

Verification: Check the sDocumentType value stored on PDF_Insight_Item__c children or in the Application_Name__c field on the parent PDF_Insight__c.

Sub-issue: Plaid JSON not flagged correctly

Plaid JSON payloads require bPlaid = true in DocumentInsightRequestWrapper. If this flag is absent, the JSON file is sent to the standard mixed document endpoint rather than Upload_Plaid_JSON_Endpoint__c, causing an Ocrolus parsing failure.

Sub-issue: File format rejection

uploadMixedDocuments sends files as multipart form data. uploadPDFtoBook includes a form_type parameter derived from sFormType. If form_type is empty or does not match Ocrolus's accepted values for the endpoint, Ocrolus returns a 400.

Check: Enable Apex debug logging for the Queueable user and look for the raw HTTP response body on 400 errors — Ocrolus typically returns a descriptive error message identifying the invalid parameter.


Issue: PDF_Insight__c Stuck in "Processing" (No Analytics Retrieved)

Unlike MoneyThumb, there is no webhook. Analytics retrieval is initiated by InvocableRetrievePDFAnalytics, which is called after all documents are uploaded. It enqueues Batch_OcrolusReprocessingJob with batchSize = 1.

Step 1 — Check Apex Jobs

Navigate to Setup → Apex Jobs and filter for Batch_OcrolusReprocessingJob and InvocableRetrievePDFAnalytics.

Status

Meaning

Never appeared

InvocableRetrievePDFAnalytics was never triggered — upload may have failed silently

Failed

Batch execution threw an exception — check the error message

Completed but no data

Ocrolus returned empty or in-progress results; batch may need re-running

Step 2 — Ocrolus Processing Lag

Ocrolus processes documents asynchronously on their end. If the batch runs before Ocrolus finishes processing, it receives an in-progress response. The batch does not automatically retry.

Resolution — Manual reprocessing:

Database.executeBatch(new Batch_OcrolusReprocessingJob(), 1);

This re-queries all PDF_Insight__c records with Source IN ('Ocrolus', 'Ocrolus Instant') and Status = Processing, then fetches results for each.

For a specific record:

InvocableRetrievePDFAnalytics.callPDFInsightService(new List<Id>{'<PDF_Insight__c Id>'});

Step 3 — Check Ocrolus Portal

Log in to the Ocrolus portal and check the book status using the Book_UUID__c or Application_Id__c from the PDF_Insight__c record. If Ocrolus shows the book as failed or still processing, the issue is upstream and a re-upload may be necessary.


Issue: Paystub Data Missing or Incomplete

Symptom: Income_Insight__c records exist but earnings/deductions data (Income_Insight_Item__c records) is absent.

WS_Ocrolus.getPaystubData() is a separate GET call that fetches detailed paystub breakdown. It runs after the initial upload response is parsed.

Diagnostic Steps:

  1. Confirm Income_Insight__c records exist on the PDF_Insight__c — if not, the upload itself failed (see upload issues above).

  2. Check Apex debug logs for getPaystubData HTTP call. Look for:

    • 404 — the document doc_pk or doc_uuid stored in DocumentInsightRequestWrapper is invalid or was not persisted correctly

    • 200 with empty arrays — Ocrolus could not extract earnings/deductions from the document (poor scan quality, unsupported paystub format)

  3. Query Income_Insight_Item__c:

SELECT Id, Name, Type__c, Amount__c, Income_Insight__c
FROM Income_Insight_Item__c
WHERE Income_Insight__c IN (
  SELECT Id FROM Income_Insight__c WHERE PDF_Insight__c = '<recordId>'
)

If this returns records, the data was retrieved but may not be displaying correctly in the UI.


Issue: Fraud Signals Missing

Symptom: Thumbprints__c records are absent after a Completed Ocrolus processing run.

WS_Ocrolus.getFraudSignals() is triggered by PdfInsightTriggerHandler.handleAfterUpdate when PDF_Insight__c transitions to Completed. It is called via InvocableRetrieveFraudAnalytics.callOcrolusFraudService().

Diagnostic Steps:

  1. Confirm the PDF_Insight__c status is actually Completed (not Processing or Failed).

  2. Check Setup → Apex Jobs for InvocableRetrieveFraudAnalytics — if it never ran, the trigger did not fire or the invocable method was not called.

  3. Verify the Apex trigger PdfInsightTrigger is active on PDF_Insight__c: Setup → Object Manager → PDF Insight → Triggers.

  4. Check handleAfterUpdate logic — getFraudSignals() is only called on status transition to Completed. If the record was manually set to Completed without going through the proper update path, the trigger guard condition may not have been met.

  5. Check debug logs for getFraudSignals HTTP call — a 404 indicates the book_pk or book_uuid is invalid.

Manual trigger:

InvocableRetrieveFraudAnalytics.callOcrolusFraudService(new List<Id>{'<PDF_Insight__c Id>'});


Issue: Identity or Tax Form Fields Not Populated

Symptom: Identity_Verification__c or Income_Insight__c records are created but most fields are blank after getFormData() runs.

WS_Ocrolus.getFormData() uses Ocrolus_Response_Field_Mapping__mdt custom metadata to map Ocrolus API response keys to Salesforce field API names. If metadata records are missing or field API names are incorrect, the mapping silently skips unmapped fields.

Diagnostic Steps:

  1. Navigate to Setup → Custom Metadata Types → Ocrolus Response Field Mapping and check that records exist for the relevant document type.

  2. For each metadata record, verify:

    • Ocrolus_Field_Key__c matches the exact key name returned by Ocrolus's API (case-sensitive)

    • SF_Field_API_Name__c is a valid field API name on the target object

    • Active__c is checked

  3. Enable debug logging and run getFormData() — look for any deserialization errors or key-not-found messages.

  4. Cross-reference the raw Ocrolus response (if raw response saving is enabled via ConfigurationService.rawResponseFile()) against the metadata mapping to identify gaps.


Issue: Status Not Updating Correctly on PDF_Insight__c

Symptom: Individual document uploads succeed but the parent PDF_Insight__c status does not reflect the aggregate state.

PdfInsightTriggerHandler.updateStatus() fires on before update and aggregates child verification statuses for Ocrolus records. It also computes paystub and W2 averages.

Common failure modes:

Symptom

Cause

Status stays Processing despite all children being Completed

Trigger not active, or SOQL in updateStatus() returning 0 rows due to sharing/FLS

Averages not computed

Income_Insight__c records exist but the aggregate SOQL is filtered by a field that is null

Status shows Completed prematurely

A previous manual update bypassed the trigger's status computation logic

Resolution: Verify the trigger is active. If the trigger is active but the status is wrong, manually update the PDF_Insight__c record to trigger the before update handler and force re-aggregation.


Issue: Duplicate Statement_Summary__c or Income_Insight__c Records

Symptom: Multiple child records for the same document appear after reprocessing.

Each call to uploadMixedDocuments or uploadPDFtoBook creates new child records without checking for existing ones. Re-running Batch_OcrolusReprocessingJob or manually calling InvocableRetrievePDFAnalytics on an already-processed record will create duplicate children.

Prevention: Before manually triggering reprocessing, check whether child records already exist:

SELECT Id, Name, CreatedDate
FROM Statement_Summary__c
WHERE PDF_Insight__c = '<recordId>'
ORDER BY CreatedDate DESC

If records exist and are complete, do not reprocess. If records are corrupt or incomplete, delete the existing child records before triggering reprocessing.


Issue: Analytics Tab Displays Incorrectly in viewPDFAnalytics

Symptom: The wrong tabs appear, or expected tabs are absent, for a Completed Ocrolus record.

PDFInsightRequestController.getChildData() determines which tabs to show by querying for child record existence:

Tab

Condition

Bank Analytics

Statement_Summary__c or Historical_Balance__c records exist

Document Analytics

Identity_Verification__c records exist

Income Analytics

Income_Insight__c records exist

If a tab is missing, the corresponding child records do not exist for that PDF_Insight__c. This is a data problem, not a UI problem.

Additionally, getChildData() checks the PDF_Insight__c.Status__c. If the status is not Completed, none of the data tabs are shown regardless of whether child records exist.


Debugging Workflow (Quick Reference)

PDF_Insight__c Status = Processing (no progress)
│
├─ Check Apex Jobs for Batch_OcrolusReprocessingJob
│   ├─ Never ran → InvocableRetrievePDFAnalytics not triggered
│   │              Check: QueueablePDFConvert completed successfully?
│   │              Check: All files uploaded before retrieval triggered?
│   ├─ Failed → Check batch error; re-run manually
│   └─ Completed but no data → Ocrolus still processing upstream
│                              Wait and re-run: Database.executeBatch(new Batch_OcrolusReprocessingJob(), 1)

PDF_Insight__c Status = Failed
│
├─ No child records at all → Book creation or first upload failed
│   Check: Auth token valid?
│   Check: Create_Book_Endpoint__c configured?
│   Check: QueueablePDFConvert Apex Job error message
│
└─ Some child records exist → Partial upload; one file type failed
    Check: Which document types have records vs which are missing
    Check: Ocrolus_Response_Field_Mapping__mdt for missing field types

Child records exist but fields are blank
│
├─ Statement_Summary__c blank → uploadMixedDocuments returned empty data
│   Check: PDF quality; re-upload cleaner file
│
├─ Identity_Verification__c blank → Ocrolus_Response_Field_Mapping__mdt missing/misconfigured
│   Check: Metadata records for document type
│
└─ Income_Insight_Item__c missing → getPaystubData() failed or returned empty
    Check: doc_pk/doc_uuid stored correctly; check Ocrolus portal


Relevant Apex Classes Reference

Class

Responsibility

PDFInsightRequestController

LWC controller — feature check, submission, data retrieval

WS_Ocrolus

All Ocrolus HTTP callouts (auth, book, upload, retrieval)

QueueablePDFConvert

Async document upload — routes by document type

InvocableRetrievePDFAnalytics

Triggers batch analytics retrieval post-upload

Batch_OcrolusReprocessingJob

Batch class polling Ocrolus for processing results

InvocableRetrieveFraudAnalytics

Triggers fraud signal retrieval on status = Completed

Ocrolus_CreateBookParser

Parses book creation response (extracts pk, book_uuid)

PdfInsightTriggerHandler

Status aggregation, averages, fraud trigger, cascade delete


Relevant Objects Reference

Object

Role

PDF_Insight__c

Central record — one per submission; holds Application_Id__c (book pk) and Book_UUID__c

Statement_Summary__c

Monthly bank statement data from uploadMixedDocuments

Income_Insight__c

Paystub, W2, 1099 summary record

Income_Insight_Item__c

Individual earnings/deductions line items from getPaystubData

Identity_Verification__c

Driver's licence, passport, SSN data from uploadPDFtoBook

Thumbprints__c

Fraud signals from getFraudSignals

PDF_Insight_Item__c

Created for Plaid/asset document uploads