NLDocSearch (NLDS) is a platform that ingests mortgage documents, indexes them using AI-powered OCR and extraction, and enables natural language queries against those documents — for example: "latest signed Closing Disclosure for Mike Johnson".
Read the Getting Started guide first. It explains all constructs (company, product, case, external IDs, upload flow, query semantics, webhook) with annotated examples before you start calling APIs.
All endpoints require an OAuth2 Bearer token obtained via the AuthX machine client credentials flow.
Token endpoint:
POST /authx/oauth2/token
grant_type=client_credentials
client_id=<your-client-id>
client_secret=<your-client-secret>
scope=nldocsearch.api
Tokens expire after 30 minutes. Re-request before expiry. Cache locally — do not store in source code or version control.
Discover the product catalog for your company. Products are admin-managed and define the document context for cases. Integration clients read products; they do not create or modify them.
| companyId required | integer <int64> Company tenant identifier |
| X-Request-ID | string <uuid> Optional client-provided request correlation ID |
{- "meta": {
- "code": 200
}, - "data": [
- {
- "productCode": "MORTGAGE",
- "productName": "Residential Mortgage",
- "productStatus": "active",
- "createdAt": "2024-06-01T00:00:00Z"
}, - {
- "productCode": "HELOC",
- "productName": "Home Equity Line of Credit",
- "productStatus": "active",
- "createdAt": "2024-06-01T00:00:00Z"
}
]
}| companyId required | integer <int64> Company tenant identifier |
| productCode required | string Product identifier code |
| X-Request-ID | string <uuid> Optional client-provided request correlation ID |
{- "meta": {
- "code": 200
}, - "data": {
- "productCode": "MORTGAGE",
- "productName": "Residential Mortgage",
- "productStatus": "active",
- "description": "Mortgages for residential property",
- "createdAt": "2024-06-01T00:00:00Z",
- "updatedAt": "2024-06-01T00:00:00Z"
}
}Create and inspect loan application cases.
Each case is scoped to a product and aggregates all uploaded documents.
Use externalCaseId to make case creation idempotent with your upstream system.
| companyId required | integer <int64> Company tenant identifier |
| X-Request-ID | string <uuid> Optional client-provided request correlation ID |
Case creation request
| productCode required | string Product identifier |
| externalCaseId | string or null <= 256 characters Your system's case identifier (optional; enables idempotency) |
| displayName | string or null <= 256 characters Human-readable case label |
| metadata | object or null Arbitrary non-sensitive business metadata |
{- "productCode": "MORTGAGE",
- "externalCaseId": "LOS-998877",
- "displayName": "Johnson Residential Mortgage - 2024",
- "metadata": {
- "borrowerName": "Mike Johnson",
- "loanPurpose": "purchase"
}
}{- "meta": {
- "code": 0
}, - "data": {
- "casePublicId": "6e32fc0a-3656-4840-a65e-87b981967f16",
- "companyId": 0,
- "productCode": "string",
- "productName": "string",
- "externalCaseId": "string",
- "displayName": "string",
- "caseStatus": "open",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}
}| companyId required | integer <int64> Company tenant identifier |
| casePublicId required | string <uuid> Case public identifier |
| X-Request-ID | string <uuid> Optional client-provided request correlation ID |
{- "meta": {
- "code": 200
}, - "data": {
- "casePublicId": "8d5a3e7c-9f21-4a2c-b8e1-d7c6f5a4e3b2",
- "companyId": 123456,
- "productCode": "MORTGAGE",
- "productName": "Residential Mortgage",
- "externalCaseId": "LOS-998877",
- "displayName": "Johnson Residential Mortgage - 2024",
- "caseStatus": "ingesting",
- "createdAt": "2024-12-15T14:32:00Z",
- "updatedAt": "2024-12-15T15:00:00Z",
- "documentSummary": {
- "total": 45,
- "registered": 2,
- "upload_pending": 0,
- "uploaded": 8,
- "validation_pending": 5,
- "validation_failed": 0,
- "ingestion_queued": 3,
- "indexed": 40,
- "failed": 0
}
}
}Register documents, receive SAS upload URLs, and signal completion. Documents are uploaded directly to Azure Blob Storage — the API never proxies file bytes. Validation runs automatically after upload completion.
Register 1–100 documents for a case and receive one short-lived SAS URL per document for direct-to-blob upload. Documents are uploaded directly to Azure Blob Storage using the SAS URL — the API never proxies file bytes.
If a document with the same externalDocumentId is already registered under the same
(companyId, productId), the existing record is returned with its current status.
A new SAS URL is issued only for documents in registered status or earlier.
| companyId required | integer <int64> Company tenant identifier |
| casePublicId required | string <uuid> Case public identifier |
| X-Request-ID | string <uuid> Optional client-provided request correlation ID |
Document registration request
required | Array of objects (DocumentRegistrationItem) [ 1 .. 100 ] items |
{- "documents": [
- {
- "originalFilename": "Closing_Disclosure_signed.pdf",
- "externalDocumentId": "DOC-CD-2024-001",
- "contentLength": 1024576,
- "declaredContentType": "application/pdf",
- "sourceSystemPath": "closing-package/CD_signed_final.pdf"
}, - {
- "originalFilename": "Promissory_Note.pdf",
- "externalDocumentId": "DOC-PN-2024-002",
- "contentLength": 512000,
- "declaredContentType": "application/pdf"
}
]
}{- "meta": {
- "code": 207
}, - "data": {
- "results": [
- {
- "documentPublicId": "f2e1d3c4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
- "externalDocumentId": "DOC-CD-2024-001",
- "originalFilename": "Closing_Disclosure_signed.pdf",
- "sasExpiresAt": "2024-12-15T14:47:00Z",
- "uploadStatus": "registered",
- "status": "created",
- "error": null
}, - {
- "documentPublicId": "a1b2c3d4-e5f6-47g8-9h0i-1j2k3l4m5n6o",
- "externalDocumentId": "DOC-PN-2024-002",
- "originalFilename": "Promissory_Note.pdf",
- "sasExpiresAt": "2024-12-15T14:47:00Z",
- "uploadStatus": "registered",
- "status": "created",
- "error": null
}
]
}
}Call this endpoint after the blob PUT to sasUploadUrl succeeds.
NLDS will verify the blob exists in landing storage and begin validation asynchronously.
Response returns immediately with uploadStatus: upload_pending.
Poll the document status endpoint to track validation progress.
| companyId required | integer <int64> Company tenant identifier |
| casePublicId required | string <uuid> Case public identifier |
| documentPublicId required | string <uuid> Document public identifier |
| X-Request-ID | string <uuid> Optional client-provided request correlation ID |
Upload completion request (optional)
| confirmedContentLength | integer or null <int64> Confirmed file size in bytes |
| confirmedContentType | string or null Confirmed MIME type |
{- "confirmedContentLength": 1024576,
- "confirmedContentType": "application/pdf"
}{- "meta": {
- "code": 200
}, - "data": {
- "documentPublicId": "f2e1d3c4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
- "casePublicId": "8d5a3e7c-9f21-4a2c-b8e1-d7c6f5a4e3b2",
- "companyId": 123456,
- "productCode": "MORTGAGE",
- "externalDocumentId": "DOC-CD-2024-001",
- "originalFilename": "Closing_Disclosure_signed.pdf",
- "uploadStatus": "upload_pending",
- "validationStatus": "pending",
- "createdAt": "2024-12-15T14:32:00Z",
- "updatedAt": "2024-12-15T14:33:00Z"
}
}| companyId required | integer <int64> Company tenant identifier |
| casePublicId required | string <uuid> Case public identifier |
| documentPublicId required | string <uuid> Document public identifier |
| X-Request-ID | string <uuid> Optional client-provided request correlation ID |
{- "meta": {
- "code": 200
}, - "data": {
- "documentPublicId": "f2e1d3c4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
- "casePublicId": "8d5a3e7c-9f21-4a2c-b8e1-d7c6f5a4e3b2",
- "companyId": 123456,
- "productCode": "MORTGAGE",
- "externalDocumentId": "DOC-CD-2024-001",
- "originalFilename": "Closing_Disclosure_signed.pdf",
- "uploadStatus": "indexed",
- "validationStatus": "passed",
- "ingestionStatus": "indexed",
- "createdAt": "2024-12-15T14:32:00Z",
- "updatedAt": "2024-12-15T15:00:00Z"
}
}Check whether documents are already registered before uploading.
Use externalDocumentId (your system's document reference) to look up
existing registration status and avoid duplicate uploads.
Check whether documents identified by your system's externalDocumentId values are
already registered in NLDS. Use this before batch registration to identify
which documents to skip, retry, or re-upload.
Uniqueness is (companyId, productCode, externalDocumentId). Documents registered under
different products are not returned.
Unknown externalDocumentId values are not included in the response — absence means
not yet registered.
| companyId required | integer <int64> Company tenant identifier |
| X-Request-ID | string <uuid> Optional client-provided request correlation ID |
Document lookup request
| productCode required | string Product identifier |
| externalDocumentIds required | Array of strings [ 1 .. 100 ] items List of document IDs to look up |
{- "productCode": "MORTGAGE",
- "externalDocumentIds": [
- "DOC-CD-2024-001",
- "DOC-PN-2024-002",
- "DOC-APPRAISAL-2024-001"
]
}{- "meta": {
- "code": 200
}, - "data": {
- "results": {
- "DOC-CD-2024-001": {
- "documentPublicId": "f2e1d3c4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
- "casePublicId": "8d5a3e7c-9f21-4a2c-b8e1-d7c6f5a4e3b2",
- "externalDocumentId": "DOC-CD-2024-001",
- "uploadStatus": "indexed",
- "validationStatus": "passed",
- "ingestionStatus": "indexed",
- "lastUpdatedAt": "2024-12-15T15:00:00Z"
}, - "DOC-APPRAISAL-2024-001": {
- "documentPublicId": "a1b2c3d4-e5f6-47g8-9h0i-1j2k3l4m5n6o",
- "externalDocumentId": "DOC-APPRAISAL-2024-001",
- "uploadStatus": "validation_failed",
- "validationStatus": "failed",
- "lastUpdatedAt": "2024-12-15T14:50:00Z",
- "validationDiagnostics": [
- {
- "code": "FILE_TOO_LARGE",
- "message": "File size (751 MB) exceeds 500 MB limit",
- "severity": "error"
}
]
}
}
}
}Submit structured document checklist queries against a case's indexed documents. Queries run asynchronously. Poll the status endpoint or configure a webhook to receive results when the query pipeline completes.
| companyId required | integer <int64> Company tenant identifier |
| X-Request-ID | string <uuid> Optional client-provided request correlation ID |
| casePublicId required | string <uuid> The case to query |
required | Array of objects (DocTypeQueryItem) [ 1 .. 20 ] items List of document types to find, each with optional attribute filters |
| previousJobId | string or null <uuid> Prior job ID for conversational context carry-over |
{- "casePublicId": "87654321-4321-4321-4321-210987654321",
- "checklist": [
- {
- "docType": "closing_disclosure",
- "filters": {
- "isSigned": true,
- "borrowerName": "Mike Johnson"
}
}, - {
- "docType": "promissory_note",
- "filters": {
- "isSigned": true
}
}
], - "previousJobId": null
}{- "meta": {
- "code": 202
}, - "data": {
- "jobId": "11111111-1111-1111-1111-111111111111",
- "casePublicId": "87654321-4321-4321-4321-210987654321",
- "status": "processing",
- "createdAt": "2024-06-01T10:00:00Z",
- "pollUrl": "/api/v1/101/query/11111111-1111-1111-1111-111111111111/status",
- "resultsUrl": "/api/v1/101/query/11111111-1111-1111-1111-111111111111/results"
}
}| companyId required | integer <int64> Company tenant identifier |
| jobId required | string <uuid> Query job identifier |
| X-Request-ID | string <uuid> Optional client-provided request correlation ID |
{- "meta": {
- "code": 200
}, - "data": {
- "jobId": "11111111-1111-1111-1111-111111111111",
- "casePublicId": "87654321-4321-4321-4321-210987654321",
- "status": "processing",
- "filesIndexed": 25,
- "filesTotal": 40,
- "elapsedSeconds": 120,
- "createdAt": "2024-06-01T10:00:00Z",
- "updatedAt": "2024-06-01T10:02:00Z"
}
}Call only after status endpoint returns complete or partial.
Returns the full result set. isPartial: true when status is partial.
Jobs expire 24 hours after creation — after expiry, results are no longer available.
| companyId required | integer <int64> Company tenant identifier |
| jobId required | string <uuid> Query job identifier |
| X-Request-ID | string <uuid> Optional client-provided request correlation ID |
{- "meta": {
- "code": 200
}, - "data": {
- "jobId": "11111111-1111-1111-1111-111111111111",
- "casePublicId": "87654321-4321-4321-4321-210987654321",
- "status": "complete",
- "isPartial": false,
- "filesIndexed": 40,
- "filesTotal": 40,
- "resultCount": 2,
- "queriedAt": "2024-06-01T10:10:00Z",
- "results": [
- {
- "docType": "closing_disclosure",
- "found": true,
- "resultCount": 1,
- "results": [
- {
- "docId": "22222222-2222-2222-2222-222222222222",
- "docType": "closing_disclosure",
- "externalDocumentId": "DOC-CD-2024-001",
- "sourceSystemPath": "closing-package/CD_signed_final.pdf",
- "pageRanges": [
- "22-37"
], - "fileId": "33333333-3333-3333-3333-333333333333",
- "segmentId": "segment-001",
- "needsReview": false,
- "matchedOn": {
- "isSigned": true,
- "borrowerName": "Mike Johnson"
}
}
]
}, - {
- "docType": "promissory_note",
- "found": true,
- "resultCount": 1,
- "results": [
- {
- "docId": "44444444-4444-4444-4444-444444444444",
- "docType": "promissory_note",
- "externalDocumentId": "DOC-PN-2024-002",
- "sourceSystemPath": "closing-package/PN_signed.pdf",
- "pageRanges": [
- "5-8"
], - "fileId": "55555555-5555-5555-5555-555555555555",
- "segmentId": "segment-002",
- "needsReview": false,
- "matchedOn": {
- "isSigned": true
}
}
]
}
]
}
}Retrieve short-lived access URLs for document viewing/download. Search results contain identifiers only — never direct storage URLs. Call this endpoint when a user selects a result to open the document.
Retrieve a short-lived access URL for a result document. Search results contain identifiers only — no direct storage URLs. Call this endpoint when presenting a result to a user who wants to open the document.
The access URL is generated at request time after re-checking tenant and document
authorization. It expires in minutes (see expiresAt). Do not cache accessUrl
beyond expiry — request a new one for each access.
| companyId required | integer <int64> Company tenant identifier |
| docId required | string <uuid> Document identifier |
| X-Request-ID | string <uuid> Optional client-provided request correlation ID |
{- "meta": {
- "code": 200
}, - "data": {
- "docId": "22222222-2222-2222-2222-222222222222",
- "fileId": "33333333-3333-3333-3333-333333333333",
- "filename": "closing_disclosure.pdf",
- "blobRelativePath": "cases/87654321-4321-4321-4321-210987654321/documents/22222222-2222-2222-2222-222222222222/closing_disclosure.pdf",
- "pageRanges": [
- "22-37"
], - "expiresAt": "2024-06-01T10:40:00Z"
}
}Configure a webhook endpoint to receive query results via push delivery
instead of polling. NLDS posts NldsQueryResultEvent to your configured
URL when a query job reaches a terminal state.
Configure or update the webhook endpoint that NLDS calls when a query reaches
a terminal state (complete, partial, or failed). There is one webhook
configuration per company. Calling PUT again updates the existing configuration.
NLDS will send HTTP POST to your url with an NldsQueryResultEvent payload
and an X-Webhook-Signature header (HMAC-SHA256 of the request body using your
secret). Verify this signature before processing the event.
| companyId required | integer <int64> Company tenant identifier |
| X-Request-ID | string <uuid> Optional client-provided request correlation ID |
| url required | string <uri> HTTPS endpoint URL where NLDS will POST webhook events |
| secret required | string >= 16 characters Shared secret for HMAC-SHA256 signature verification |
| active | boolean Default: true Enable or pause webhook delivery |
{- "secret": "your-shared-secret-minimum-16-characters",
- "active": true
}{- "meta": {
- "code": 200
}, - "data": {
- "companyId": 101,
- "active": true,
- "createdAt": "2024-06-01T10:00:00Z",
- "updatedAt": "2024-06-01T10:00:00Z"
}
}| companyId required | integer <int64> Company tenant identifier |
| X-Request-ID | string <uuid> Optional client-provided request correlation ID |
{- "meta": {
- "code": 200
}, - "data": {
- "companyId": 101,
- "active": true,
- "createdAt": "2024-06-01T10:00:00Z",
- "updatedAt": "2024-06-01T10:00:00Z"
}
}| companyId required | integer <int64> Company tenant identifier |
| X-Request-ID | string <uuid> Optional client-provided request correlation ID |
{- "meta": {
- "code": 401,
- "message": "Invalid or expired token",
- "type": "unauthorized"
}
}This endpoint is IMPLEMENTED BY THE CLIENT. NLDS will POST to the URL you configured via PUT /webhooks/query-result. This schema documents the expected payload structure and client responsibilities.
Your implementation must:
X-Webhook-Signature header by computing HMAC-SHA256
of the raw request body using your configured secret.eventId may be delivered multiple times; use it for
deduplication on your side.NLDS retry policy:
Example verification code (Node.js):
const crypto = require('crypto');
const signature = req.headers['x-webhook-signature'];
const computed = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
if (signature !== computed) {
return res.status(401).send('Signature invalid');
}
| X-Webhook-Signature required | string HMAC-SHA256 of request body signed with your webhook secret (hex-encoded) |
| eventId required | string Unique event delivery identifier (use for idempotency) |
| eventType required | string Enum: "NLDOCSEARCH_QUERY_COMPLETED" "NLDOCSEARCH_QUERY_PARTIAL" "NLDOCSEARCH_QUERY_FAILED" Event classification |
| companyId required | integer <int64> Company identifier |
| casePublicId required | string <uuid> Case identifier |
| jobId required | string <uuid> Query job identifier |
| status required | string (QueryStatus) Enum: "processing" "complete" "partial" "failed" Query job status |
| occurredAt required | string <date-time> Event timestamp |
object (NldsResultSummary) | |
Array of objects or null (QueryResultItem) Full result set for COMPLETED and PARTIAL; null for FAILED | |
| errorCode | string or null Error code for FAILED events |
| errorMessage | string or null Error message for FAILED events |
{- "eventId": "string",
- "eventType": "NLDOCSEARCH_QUERY_COMPLETED",
- "companyId": 0,
- "casePublicId": "6e32fc0a-3656-4840-a65e-87b981967f16",
- "jobId": "9d222c6d-893e-4e79-8201-3c9ca16a0f39",
- "status": "processing",
- "occurredAt": "2019-08-24T14:15:22Z",
- "resultSummary": {
- "resultCount": 0,
- "filesIndexed": 0,
- "filesTotal": 0,
- "isPartial": true
}, - "results": [
- {
- "docId": "b394169a-e7cd-41ad-9e47-a6d4a11d664b",
- "docType": "string",
- "externalDocumentId": "string",
- "sourceSystemPath": "string",
- "pageRanges": [
- "string"
], - "fileId": "a1c6a2ab-4b01-4253-b4c9-70e04b3b48fc",
- "segmentId": "string",
- "needsReview": true,
- "matchedOn": {
- "isSigned": true,
- "borrowerName": "string",
- "docDateFrom": "2019-08-24",
- "docDateTo": "2019-08-24",
- "executionStatus": "string"
}
}
], - "errorCode": "string",
- "errorMessage": "string"
}