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 |
|---|---|---|
|
|
Validates |
|
|
Creates an Ocrolus "book" (application container) |
|
|
Routes each file to the correct upload endpoint based on document type |
|
|
Batch polls Ocrolus for results |
|
|
Fetches detailed sub-records per document type |
|
|
Aggregates statuses and averages onto |
Ocrolus vs Ocrolus Instant
The only architectural difference is the bInstant boolean passed to WS_Ocrolus.createBook():
|
Field |
Ocrolus |
Ocrolus Instant |
|---|---|---|
|
|
|
|
|
|
|
|
|
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 |
|---|---|
|
|
Ocrolus OAuth2 token URL |
|
|
Ocrolus book creation URL |
|
|
Endpoint for bank/paystub documents |
|
|
Endpoint for Plaid JSON payloads |
|
|
Endpoint for identity/tax/income PDFs |
|
|
Ocrolus OAuth2 client ID |
|
|
Ocrolus OAuth2 client secret |
|
|
Ocrolus OAuth2 audience value |
|
|
Must be |
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 |
|
Upload Method |
Child Record Created |
|---|---|---|---|
|
Bank statement |
— |
|
|
|
PayStub |
|
|
|
|
SSA-1099 |
|
|
|
|
Plaid JSON |
— (bPlaid=true) |
|
|
|
Identity (DL/Passport) |
— |
|
|
|
Tax (W2, 1040, etc.) |
— |
|
|
|
Other income |
— |
|
|
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:
-
Confirm all four values are correctly populated in Custom Settings.
-
The
audiencefield is frequently missed or set incorrectly — it is an Ocrolus-specific parameter that is not standard in all OAuth2 flows. -
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.
-
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 |
|
|
Malformed JSON body |
|
|
Account not provisioned for Instant |
|
|
Parser failure on unexpected response schema |
Null |
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 |
|
|
|
Batch execution threw an exception — check the error message |
|
|
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:
-
Confirm
Income_Insight__crecords exist on thePDF_Insight__c— if not, the upload itself failed (see upload issues above). -
Check Apex debug logs for
getPaystubDataHTTP call. Look for:-
404— the documentdoc_pkordoc_uuidstored inDocumentInsightRequestWrapperis invalid or was not persisted correctly -
200with empty arrays — Ocrolus could not extract earnings/deductions from the document (poor scan quality, unsupported paystub format)
-
-
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:
-
Confirm the
PDF_Insight__cstatus is actuallyCompleted(notProcessingorFailed). -
Check Setup → Apex Jobs for
InvocableRetrieveFraudAnalytics— if it never ran, the trigger did not fire or the invocable method was not called. -
Verify the Apex trigger
PdfInsightTriggeris active onPDF_Insight__c: Setup → Object Manager → PDF Insight → Triggers. -
Check
handleAfterUpdatelogic —getFraudSignals()is only called on status transition toCompleted. If the record was manually set toCompletedwithout going through the proper update path, the trigger guard condition may not have been met. -
Check debug logs for
getFraudSignalsHTTP call — a404indicates thebook_pkorbook_uuidis 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:
-
Navigate to Setup → Custom Metadata Types → Ocrolus Response Field Mapping and check that records exist for the relevant document type.
-
For each metadata record, verify:
-
Ocrolus_Field_Key__cmatches the exact key name returned by Ocrolus's API (case-sensitive) -
SF_Field_API_Name__cis a valid field API name on the target object -
Active__cis checked
-
-
Enable debug logging and run
getFormData()— look for any deserialization errors or key-not-found messages. -
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 |
Trigger not active, or SOQL in |
|
Averages not computed |
|
|
Status shows |
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 |
|
|
Document Analytics |
|
|
Income Analytics |
|
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 |
|---|---|
|
|
LWC controller — feature check, submission, data retrieval |
|
|
All Ocrolus HTTP callouts (auth, book, upload, retrieval) |
|
|
Async document upload — routes by document type |
|
|
Triggers batch analytics retrieval post-upload |
|
|
Batch class polling Ocrolus for processing results |
|
|
Triggers fraud signal retrieval on status = Completed |
|
|
Parses book creation response (extracts |
|
|
Status aggregation, averages, fraud trigger, cascade delete |
Relevant Objects Reference
|
Object |
Role |
|---|---|
|
|
Central record — one per submission; holds |
|
|
Monthly bank statement data from |
|
|
Paystub, W2, 1099 summary record |
|
|
Individual earnings/deductions line items from |
|
|
Driver's licence, passport, SSN data from |
|
|
Fraud signals from |
|
|
Created for Plaid/asset document uploads |