The Ocrolus Widget is an embeddable document upload and bank connection interface that allows your users to securely submit financial documents directly from your application.
- Overview
- Features
- Widget Setup in Dashboard
- How It Works
- Integration Guide
- Running the Quickstart
- Events
- Webhooks
- API Reference
- Support
This repository provides working examples in multiple languages (Node.js, PHP) to demonstrate widget integration. Use these examples to understand how to embed the Ocrolus Widget in your application.
Key Benefits:
- Secure - Documents are uploaded directly to Ocrolus via encrypted iframe
- Customizable - Match your brand with custom colors and messaging
- Easy Integration - Simple script tag integration
- Real-time Events - Track upload progress and user actions
The Ocrolus Widget provides a secure, embeddable interface for collecting financial documents from your users.
| Feature | Description |
|---|---|
| Secure Upload | Documents are uploaded directly to Ocrolus via an encrypted iframe |
| Bank Connection | Plaid integration for automatic bank statement retrieval |
| Customizable UI | Match your brand with custom colors and messaging |
| Real-time Events | Track upload progress and user actions via postMessage events |
| Webhook Notifications | Receive notifications when documents are processed |
- Drag-and-drop file upload interface
- Supports PDF, JPG, JPEG, and PNG files (up to 200MB)
- Real-time upload status tracking
- Automatic document processing, classification, and data extraction
- Secure bank account linking via Plaid
- Automatic bank statement retrieval via Asset Reports
- Wide financial institution coverage
- User credentials entered directly with the bank (Ocrolus never sees them)
For complete details, see the official Ocrolus Widget documentation.
- Log in to the Ocrolus Dashboard
- Navigate to Account & Settings → Embedded Widget
- Click ADD WIDGET
- Configure your widget settings:
- Enter a Widget Name for your reference
- Add your website domain(s) to Allowed URLs
- Enable Show upload documents and/or Show connect to bank
- Customize colors and text as needed
- Use the Preview section to test your widget:
- Select file-uploader from the dropdown to test uploads
- Click SAVE in the top right corner
- Copy your generated credentials:
- Widget UUID
- Client ID
- Client Secret (save securely - not accessible later)
- JavaScript Snippet for frontend integration
Security Note: Store your Client ID and Client Secret securely on your server. Never expose them in frontend code.
| Setting | Description |
|---|---|
| Widget Name | A name for your reference (not shown to users) |
| Allowed URLs | Domains where the widget can be embedded (e.g., https://yoursite.com). The widget iframe will refuse to render unless your URL is in this list. |
| Branding Color | Primary color for buttons and accents (hex code) |
| Text Color | Text color (hex code) |
| Show upload documents | Enable the "Upload Documents" section |
| Upload Header | Header text (e.g., "Upload Your Bank Statements") |
| Upload Description | Description text (e.g., "Please upload your last 3 months of statements") |
| Show connect to bank | Enable the "Connect to Bank" section (requires Plaid configuration) |
| Bank Header | Header text for bank section |
| Bank Description | Description text for bank section |
The Ocrolus widget seamlessly supports Plaid integration, allowing you to securely store and manage Plaid API keys directly from the Dashboard.
- Navigate to Dashboard → Account & Settings → Embedded Widget
- Click CONNECT PLAID ACCOUNT (or UPDATE ACCOUNT DETAILS if already connected)
- Log in to your Plaid Dashboard and retrieve:
- Plaid Client ID
- Plaid Client Secret (Production)
- Enter these credentials in the Configure Plaid Account dialog
- Click Done to save
Note: Your Plaid credentials are securely stored by Ocrolus. You can update them at any time from the Embedded Widget settings.
- Go to Dashboard → Account & Settings → Embedded Widget
- Edit an existing widget or create a new one
- Turn ON the Show connect to bank toggle
- Customize the bank connection header and description text
- Click SAVE
Important: Ensure at least one of Show upload documents or Show connect to bank is enabled. Don't disable one unless the other is active.
Plaid Features:
- Users can securely link their bank accounts
- Bank credentials are entered directly with the bank (Ocrolus never sees them)
- Bank statements are automatically retrieved via Plaid's Asset Reports
- Statements are processed and available in your Ocrolus dashboard
┌─────────────────────────────────────────────────────────────────────────┐
│ DOCUMENT UPLOAD FLOW │
└─────────────────────────────────────────────────────────────────────────┘
1. USER CLICKS "Upload Documents"
│
▼
2. UPLOAD MODAL OPENS
│ User drags & drops or selects files
│ Supported: PDF, JPG, JPEG, PNG (max 200MB)
│
▼
3. USER CLICKS "Submit"
│ → EVENT: USER_FILE_UPLOADER_SUBMIT
│
▼
4. FILES UPLOADED TO OCROLUS
│ HTTP upload to Ocrolus servers
│ → EVENT: USER_UPLOAD_RECEIVED
│ Status: "Received" (processing)
│
▼
5. DOCUMENT PROCESSING
│ • Security scan
│ • Document classification
│ • OCR extraction
│
▼
6. PROCESSING COMPLETE
│ → EVENT: USER_UPLOAD_COMPLETE (success)
│ → EVENT: USER_UPLOAD_FAILED (if error)
│ Status: "Uploaded" or "Error"
│
▼
7. USER CLOSES MODAL
→ EVENT: USER_FILE_UPLOADER_CLOSE
Upload States:
| State | Icon | Description |
|---|---|---|
| Uploading | Spinner | File is being uploaded to Ocrolus |
| Received | Yellow dot | Upload successful, document is being processed |
| Uploaded | Green checkmark | Document processed and available in Ocrolus |
| Error | Red X | Upload or processing failed (see error message) |
┌─────────────────────────────────────────────────────────────────────────┐
│ BANK CONNECTION (PLAID) FLOW │
└─────────────────────────────────────────────────────────────────────────┘
1. USER CLICKS "Connect to Bank"
│
▼
2. PLAID LINK OPENS
│ Secure Plaid modal (not Ocrolus)
│ User searches for their bank
│
▼
3. USER LOGS INTO BANK
│ Credentials entered directly with bank
│ Ocrolus never sees user's bank credentials
│
▼
4. USER SELECTS ACCOUNTS
│ User chooses which accounts to connect
│
▼
5. CONNECTION SUCCESSFUL
│ → EVENT: LINK_SUCCESS
│ Plaid modal closes
│ Widget shows success state
│
▼
6. ASSET REPORT GENERATED (Background)
│ Plaid generates asset report (30 seconds - 5 minutes)
│ Bank statements retrieved automatically
│
▼
7. STATEMENTS AVAILABLE
Documents appear in your Ocrolus dashboard
Webhook notification sent (if configured)
If an error occurs:
PLAID_ERRORevent is emitted with error details- User can retry the connection
- See Events for error handling
- An Ocrolus account with API access
- A widget created in the Ocrolus Dashboard
- Your Widget UUID, Client ID, and Client Secret
- A backend server to securely generate tokens
Create a server endpoint that exchanges your credentials for a widget token. Never expose your Client Secret to the frontend.
const express = require('express');
const app = express();
const WIDGET_UUID = process.env.OCROLUS_WIDGET_UUID;
const CLIENT_ID = process.env.OCROLUS_CLIENT_ID;
const CLIENT_SECRET = process.env.OCROLUS_CLIENT_SECRET;
app.post('/api/widget-token', async (req, res) => {
const { userId, bookName } = req.body;
const response = await fetch(
`https://widget.ocrolus.com/v1/widget/${WIDGET_UUID}/token`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
custom_id: userId, // Your user identifier (links documents to same book)
book_name: bookName, // Folder name in Ocrolus (e.g., "John Smith Application")
grant_type: 'client_credentials'
})
}
);
const { access_token } = await response.json();
res.json({ accessToken: access_token });
});Token Parameters:
| Parameter | Required | Description |
|---|---|---|
client_id |
Yes | Your Widget Client ID |
client_secret |
Yes | Your Widget Client Secret |
custom_id |
Yes | Your user identifier. Documents with the same custom_id go to the same book. Use this to link multiple uploads from the same user. |
book_name |
Yes | Book name in Ocrolus dashboard. This helps you identify the book later (e.g., "John Smith Loan Application"). |
grant_type |
Yes | Always client_credentials |
Tip: Use a consistent
custom_idfor each user session. If a user uploads additional files later, using the samecustom_idwill add them to the existing book. You can search for books by this ID using thexidfield in the Book List API.
Token Response:
{
"access_token": "eyJhbGciOiJSU0...",
"expires_in": 900,
"token_type": "Bearer"
}Note: Tokens have a 15-minute TTL. Implement token refresh logic to handle long user sessions.
- Add the container element where you want the widget to appear:
<div id="ocrolus-widget-frame"></div>- Define the token provider function (BEFORE the widget script):
<script>
window.getAuthToken = async function() {
const response = await fetch('/api/widget-token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
userId: 'user-123', // Your user's ID
bookName: 'Loan Application' // Descriptive name
})
});
const { accessToken } = await response.json();
return accessToken;
};
</script>- Add the widget initializer script (from your dashboard):
<script id="ocrolus-initializer-script">
(function(w, d, s, o, h, u, f, js, fjs) {
w[o] = w[o] || function() { (w[o].q = w[o].q || []).push(arguments); };
(js = d.createElement(s)), (fjs = d.getElementsByTagName(s)[0]);
js.id = o;
js.dataset.host = h;
js.dataset.uuid = u;
js.src = f;
js.async = 1;
fjs.parentNode.insertBefore(js, fjs);
})(
window,
document,
'script',
'ocrolus_script',
'https://widget.ocrolus.com',
'YOUR_WIDGET_UUID',
'https://widget.ocrolus.com/static/initializer-sdk.bundle.js'
);
ocrolus_script('init');
</script>Listen for widget events to track user actions and update your UI:
window.addEventListener('message', (event) => {
const { type, ...data } = event.data;
switch (type) {
case 'USER_UPLOAD_COMPLETE':
// Update your UI to show success
showNotification(`Successfully uploaded ${data.uploads.length} file(s)`);
// Store the bookUuid for later reference
saveBookUuid(data.uploads[0]?.bookUuid);
break;
case 'USER_UPLOAD_FAILED':
// Show error to user
showError(`Upload failed: ${data.uploads[0]?.message}`);
break;
case 'LINK_SUCCESS':
// Bank connection successful
showNotification('Bank connected successfully!');
break;
case 'PLAID_ERROR':
// Handle Plaid errors
if (data.error.errorType === 'USER_ERROR') {
showError(data.error.displayMessage);
} else {
showError('Unable to connect to your bank. Please try again later.');
}
break;
case 'USER_FILE_UPLOADER_CLOSE':
// User closed the modal
console.log(`Session complete: ${data.uploadedFileCount} files uploaded`);
break;
}
});This quickstart provides working examples you can run locally to test the widget integration. It supports both Docker mode and Local mode (without Docker).
The easiest way to get started is using our setup script. This works on macOS, Linux, and Windows (Git Bash/WSL).
# Clone the repository
git clone https://github.com/Ocrolus/widget-quickstart.git
cd widget-quickstart
# Run setup (one-time)
./setup.shThe setup script will:
- ✅ Check prerequisites (mkcert, Docker/Node.js)
- ✅ Guide you through hosts file configuration
- ✅ Prompt for your widget credentials
- ✅ Create the
.envfile - ✅ Update the widget configuration in the frontend
- ✅ Generate SSL certificates automatically
After setup, choose your preferred run mode:
- Docker mode: Easiest setup, everything runs in containers
- Local mode: Run services directly on your machine
# macOS
brew install mkcert
# Windows (with chocolatey)
choco install mkcert
# Linux (Ubuntu/Debian)
sudo apt install libnss3-tools
curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64"
chmod +x mkcert-v*-linux-amd64
sudo mv mkcert-v*-linux-amd64 /usr/local/bin/mkcert- Docker installed and running
Add these entries to your hosts file (the setup script will guide you):
macOS/Linux (/etc/hosts):
sudo sh -c 'echo "127.0.0.1 www.ocrolusexample.com" >> /etc/hosts'
sudo sh -c 'echo "127.0.0.1 auth.ocrolusexample.com" >> /etc/hosts'Windows (Run Notepad as Administrator):
notepad C:\Windows\System32\drivers\etc\hosts
Add:
127.0.0.1 www.ocrolusexample.com
127.0.0.1 auth.ocrolusexample.com
Add www.ocrolusexample.com to your widget's "Allowed URLs" in the Ocrolus Dashboard.
After running ./setup.sh, start all services with Docker:
make run_dockerThis starts:
- Node.js backend server (port 8000)
- Frontend (port 3000)
- Caddy reverse proxy (port 443)
- (Optional) ngrok for webhooks
Visit https://www.ocrolusexample.com in your browser.
To stop all services:
make stop_dockerAfter running ./setup.sh, you need to start three services in separate terminal windows:
make run_caddy_localmake run_nodemake run_frontendVisit https://www.ocrolusexample.com in your browser.
Note: All three services must be running simultaneously. Use
Ctrl+Cin each terminal to stop them.
If you prefer to set up manually without using the scripts:
- Clone and configure:
git clone https://github.com/Ocrolus/widget-quickstart.git
cd widget-quickstart- Create
.envfile with your credentials:
OCROLUS_WIDGET_UUID=your-widget-uuid
OCROLUS_CLIENT_ID=your-client-id
OCROLUS_CLIENT_SECRET=your-client-secret
OCROLUS_WIDGET_ENVIRONMENT=production- Update the widget UUID in frontend:
Open frontend/public/index.html and replace YOUR_WIDGET_UUID with your actual Widget UUID.
- Generate SSL certificates:
make initialize_certs- Run with Docker:
make run_dockerThe widget communicates with your application via browser postMessage events. Listen for these to track user actions and upload status.
window.addEventListener('message', (event) => {
// Verify the event is from Ocrolus (recommended)
if (!event.origin.includes('ocrolus.com')) return;
const { type, ...data } = event.data;
switch (type) {
case 'USER_UPLOAD_COMPLETE':
console.log('Upload complete:', data.uploads);
break;
case 'PLAID_ERROR':
console.error('Plaid error:', data.error);
break;
// Handle other events...
}
});User clicked "Upload Documents" to open the upload modal.
{ type: 'USER_FILE_UPLOADER_OPEN', timestamp: '2024-01-15T10:30:00.000Z' }User clicked "Submit" to upload selected files.
{
type: 'USER_FILE_UPLOADER_START',
timestamp: '2024-01-15T10:30:15.000Z',
uploads: [
{ name: 'bank_statement.pdf', size: 1024000, type: 'application/pdf' }
]
}Files have been received by Ocrolus and processing has started.
{
type: 'USER_UPLOAD_RECEIVED',
timestamp: '2024-01-15T10:30:18.000Z',
uploads: [
{ name: 'bank_statement.pdf', size: 1024000, type: 'application/pdf' }
]
}File has been fully processed and is available in your Ocrolus dashboard.
{
type: 'USER_UPLOAD_COMPLETE',
timestamp: '2024-01-15T10:30:45.000Z',
uploads: [
{
name: 'bank_statement.pdf',
size: 1024000,
type: 'application/pdf',
bookUuid: 'abc123-def456-...' // Book where document was added
}
],
errors: []
}File upload or processing failed.
{
type: 'USER_UPLOAD_FAILED',
timestamp: '2024-01-15T10:30:20.000Z',
uploads: [
{
name: 'corrupted.pdf',
size: 1024000,
type: 'application/pdf',
message: 'Document could not be processed'
}
]
}User closed the upload modal.
{
type: 'USER_FILE_UPLOADER_CLOSE',
timestamp: '2024-01-15T10:31:00.000Z',
uploadedFileCount: 3 // Total successful uploads in this session
}User successfully connected their bank account.
{ type: 'LINK_SUCCESS' }An error occurred during the bank connection flow.
{
type: 'PLAID_ERROR',
error: {
errorType: 'USER_ERROR' | 'INSTITUTION_ERROR' | 'OCROLUS_ERROR',
errorCode: 'INVALID_CREDENTIALS',
errorMessage: 'The credentials were not correct',
displayMessage: 'The provided credentials were not correct. Please try again.',
reason: 'User entered incorrect bank login credentials'
}
}Error Types:
| Error Type | Description | User Action |
|---|---|---|
USER_ERROR |
User action issue (wrong password, timeout, etc.) | User can retry |
INSTITUTION_ERROR |
Bank is temporarily unavailable | Wait and retry later |
OCROLUS_ERROR |
System error | Contact support if persistent |
Common Error Codes:
| Code | Description |
|---|---|
INVALID_CREDENTIALS |
User entered wrong bank login |
INVALID_MFA |
User entered wrong MFA code |
ITEM_LOCKED |
Bank account locked (too many failed attempts) |
INSTITUTION_DOWN |
Bank is experiencing technical issues |
INSTITUTION_NOT_RESPONDING |
Bank is temporarily unavailable |
Send this event to programmatically close the current modal.
window.postMessage({ type: 'DESTROY_MODAL_IFRAME' }, '*');Send this event to completely remove the widget from your page.
window.postMessage({ type: 'DESTROY_OCROLUS_WIDGET' }, '*');Configure webhooks to receive notifications when documents are uploaded and processed. This is especially useful for downloading documents that users upload via the widget back to your own systems.
For detailed webhook documentation, see the Ocrolus Webhooks Guide.
- Go to Ocrolus Dashboard → Settings → Webhooks
- Create a new webhook with your endpoint URL
- Enable the
document.verification_succeededevent
{
"event_name": "document.verification_succeeded",
"book_uuid": "fc8f9719-0089-44f1-b2hf-053320239930",
"book_pk": 98871,
"doc_uuid": "06gh45dh-o83v-86v8-vvvv-v713347b5ed5",
"uploaded_doc_uuid": "06bdw77de-e97l-22f8-vzdd-a710447c4ed5",
"mixed_uploaded_doc_uuid": "e7e35a16-0240-4c46-a290-7f0ad933f93e",
"status": "VERIFICATION_COMPLETE",
"notification_reason": "Document verified",
"notification_type": "STATUS",
"severity": "LOW"
}To determine if a webhook is for a widget-uploaded document:
- Check book_type: Query the Book Info API and check if
book_type === 'WIDGET' - Check xid: If you use the
custom_idparameter, it will appear asxidon the book
After receiving a webhook, you can download the original document:
// 1. Get an API token
const tokenResponse = await fetch('https://auth.ocrolus.com/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
grant_type: 'client_credentials'
})
});
const { access_token } = await tokenResponse.json();
// 2. (Optional) Verify this is a widget book
const bookResponse = await fetch(
`https://api.ocrolus.com/v1/book/info?book_uuid=${bookUuid}`,
{ headers: { Authorization: `Bearer ${access_token}` } }
);
const bookData = await bookResponse.json();
if (bookData.response.book_type === 'WIDGET') {
// 3. Download the document
const docResponse = await fetch(
`https://api.ocrolus.com/v2/document/download?doc_uuid=${docUuid}`,
{ headers: { Authorization: `Bearer ${access_token}` } }
);
const fileBuffer = await docResponse.arrayBuffer();
// Save to your file system
fs.writeFileSync('document.pdf', Buffer.from(fileBuffer));
}POST https://widget.ocrolus.com/v1/widget/{widget_uuid}/token
Request:
{
"client_id": "your_client_id",
"client_secret": "your_client_secret",
"custom_id": "user-identifier",
"book_name": "Application Documents",
"grant_type": "client_credentials"
}Response:
{
"access_token": "eyJhbGciOiJSU0...",
"expires_in": 900,
"token_type": "Bearer"
}For API calls (webhooks, document download):
POST https://auth.ocrolus.com/oauth/token
Request:
{
"client_id": "your_client_id",
"client_secret": "your_client_secret",
"grant_type": "client_credentials"
}GET https://api.ocrolus.com/v2/document/download?doc_uuid={doc_uuid}
Authorization: Bearer {access_token}
GET https://api.ocrolus.com/v1/book/info?book_uuid={book_uuid}
Authorization: Bearer {access_token}
- Widget Documentation: docs.ocrolus.com/docs/widget
- API Documentation: docs.ocrolus.com
- Webhooks Guide: docs.ocrolus.com/docs/configure-and-manage
- Dashboard: dashboard.ocrolus.com
- Plaid Dashboard: dashboard.plaid.com
- Support: Contact your Ocrolus representative or raise a request
Copyright © Ocrolus. All rights reserved.


