Three composable services for lenders and onboarding teams — extract structured data from documents, verify their authenticity, and turn the result into ready-to-consume fraud signals.
Versionv1Updated12 August 2026FormatREST / JSON
Introduction
PowerCred Document Intelligence is a three-stage pipeline that takes a raw document (PDF or image) and returns a structured, scored, lender-ready verdict. Each stage is exposed as its own REST API and can be adopted independently.
1
OCR
Convert a payslip, employment certificate, ID, or bank statement into structured JSON fields.
›
2
Tamper Detection
Verify the source PDF matches a registered template for the document type using metadata, fonts, and layout.
›
3
Fraud Signals
Run arithmetic, credibility, cross-document and statutory checks. Emit categorised verdicts and lender signals.
Supported Document Types & Services
The table below summarizes the supported document types across the platform and which services are available for each type.
Document Type (document_type)
Description
OCR
Tamper Detection
Fraud Signals
payslip
Payslips and pay stubs from all employers
✓ Supported
✓ Supported
✓ Supported
employment_certificate
Certificate of Employment (COE) / proof of employment
✓ Supported
✓ Supported
✓ Supported
national_id
Government national IDs (KTP, UMID, National ID PH, Passport)
✓ Supported
✓ Supported
N/A
bank_statement
Savings, current, or business bank account statements
✓ Supported
✓ Supported
✓ Supported
Authentication
Each service has its own API key. Keep them separate — they map to different products in our backend.
OCR
?apikey=…
Pass the API key as a apikeyquery parameter on every request. Do not use the Authorization header.
Tamper Detection
?apikey=…
Pass the API key as a apikeyquery parameter on every request. Do not use the Authorization header.
Fraud Signals
?apikey=…
Pass the API key as a apikeyquery parameter on every request. Do not use the Authorization header.
Important: Treat all keys as production secrets even on staging. Never embed them in client-side code or commit them to source control.
Environments
Use staging/dev during integration; promote to production once your verification flow is finalised.
Service
Staging / Dev
Production
OCR
https://mock.powercred.io/idp/v1
https://dev.powercred.io/idp/v1
Fraud Signals
https://mock.powercred.io/cms/v1
https://dev.powercred.io/cms/v1
Tamper Detection
https://mock.powercred.io/idp/fraud
https://dev.powercred.io/idp/fraud
End-to-end workflow
A typical loan-onboarding flow chains all three services. The OCR id is the linchpin — once you have it, the same identifier is reused as a session reference by Fraud Signals.
Why the two options at step 4?Option A is one round-trip simpler — the service fetches the OCR result for you using the id. Option B lets you score data you already hold (e.g. cached payloads or data extracted by a non-PowerCred OCR). Both produce an identical response shape.
1OCR
Asynchronous structured extraction from PDF, JPEG, and PNG documents. Submit once, poll for the result. Supports payslips, employment certificates, government IDs, and bank statements.
Async by design. Submission returns 202 immediately with an id. Use either polling (GET /get?id=…) or supply callback_url on submit to receive a webhook when extraction is complete.
POST/idp/v1/readSubmit a document for extraction
Upload a document (or supply a public file_url) and start extraction. Validation rules are determined by document_type — your account is configured with the schemas you need.
The endpoint accepts two request styles — pick whichever is easier from your caller:
Multipart / form-data — use when you're uploading the file bytes directly (file). Other params travel on the query string.
JSON body — use when the document is already hosted somewhere accessible (send file_url) or when you'd rather keep everything in the request body. Set Content-Type: application/json.
Option A · Multipart form-data
Params on the query string, file in form-data.
Query parameters
Name
Type
Required
Description
reference_id
string
required
Your correlation ID for the end-user or loan application.
document_type
enum (string)
required
Defined document type enum. Supported values: payslip, employment_certificate, national_id, bank_statement, passport, invoice, utility_bill.
file_url
string
optional
Public URL of the document. Use instead of multipart file.
callback_url
string
optional
Webhook to receive the final payload on completion.
tamper_check
bool
optional
Run lightweight inline tamper indicators on the document.
get_image_quality
bool
optional
Include image quality assessment (blur, brightness, flash, DPI) for camera images.
Form-data
Name
Type
Required
Description
file
binary
conditional
PDF, JPEG or PNG. Required if file_url is not provided.
Option B · JSON body
Set Content-Type: application/json and pass the Authorization: Bearer <apikey> header. All fields move into the JSON body.
Your correlation ID for the end-user or loan application.
document_type
enum (string)
required
Defined document type enum (e.g., payslip, employment_certificate, national_id, bank_statement).
file_url
string
required
Public URL of the document (PDF/JPEG/PNG). Since JSON can't carry file bytes, this is the mandatory source in this mode.
callback_url
string
optional
Webhook to receive the final payload on completion.
tamper_check
bool
optional
Run lightweight inline tamper indicators on the document.
get_image_quality
bool
optional
Include image quality assessment for camera images.
Body wins ties. If a field is present both on the query string and in the JSON body, the JSON body value is used. Any body field can also be passed as a query parameter — the two forms are interchangeable except for file, which must go through multipart.
When the submitted file bundles payslips from multiple pay periods (e.g. the last three months in one PDF), data becomes an array — one object per detected payslip, ordered by pay date. All other top-level fields (id, document_type, status) are unchanged. Iterate over data in your client code and treat each element as a full payslip record.
Feeding this into Fraud Signals. Pass the data array straight through to POST /cms/v1/fraud-detection/{document_type}/run — wrap each element as { "type": "{document_type}", "data": <element> } in the documents[] array. Cross-document checks (employer consistency, pay period continuity, income stability) will then engage automatically.
Status semantics
Code
Meaning
Action
200
Extraction complete; payload returned.
Consume data.
202
Extraction still in progress.
Retry after 2–3 seconds.
404
Document was unreadable.
Resubmit a clearer copy.
500
Extraction failed.
Resubmit; if persistent, contact support.
DELETE/idp/v1/deleteDelete an OCR record
Permanently remove an OCR record and its stored extraction data. Useful for right-to-be-forgotten flows or purging test data. The delete is scoped to the account associated with your API key — records belonging to other customers cannot be deleted.
Irreversible. Once deleted, the record and its extracted payload can no longer be retrieved via GET /get. If a downstream Fraud Signals run has already been executed against this ID, that run's result is preserved separately and is unaffected.
2Document Tamper Detection
Register reference PDFs for each document type your organisation accepts. Incoming PDFs are then verified against the stored metadata fingerprint, font set, and layout signature. Doctored or fabricated ePDFs are caught before they reach scoring.
How it works
You upload one or more clean reference PDFs per document type (e.g. an employer's payslip template). The service extracts a fingerprint composed of PDF metadata patterns, embedded fonts, and a layout signature, and stores it as the active version. Every incoming PDF is checked against the stored fingerprint, with regex-based pattern matching for metadata and a cosine similarity score for layout.
Auth reminder. Tamper Detection endpoints do not use Bearer headers (No Auth). Pass your API key via query parameter apikey=<key> (e.g. ?apikey=<key>) on every request.
File input — two ways. All four file-taking endpoints (POST /check, POST /templates/{id}/auto, POST /templates/{id}, PUT /templates/{id}) accept either:
multipart/form-data — upload raw PDF bytes in the file / files field.
application/json — send file_url (single) or file_urls (list). The server downloads each PDF (max 5 MB each, PDF only) and processes it identically.
If a request supplies both, the multipart file(s) win and file_url(s) is ignored.
GET/documentsList registered document types
Query parameters
Name
Type
Required
Description
apikey
string
required
API key passed as a query parameter (Set Auth to No Auth in headers).
Response fields
Field
Type
Description
documents
string[]
All document type identifiers registered under your account.
POST/templates/{document_id}/autoAuto-extract a template
Upload reference PDFs and let the service derive every validation rule automatically. Metadata patterns and font lists are taken directly from the PDFs:
Single unique value across all files → exact-match regex.
Public URLs of the PDF templates. Each downloaded server-side; same 5 MB and PDF-only rules apply.
similarity_threshold
float
optional
Layout cosine-similarity threshold, default 0.85.
Response fields
Field
Type
Description
version_id
string
Identifier of the newly created version (e.g. v1). This version becomes active immediately.
document_id
string
Echo of the path parameter.
template_count
integer
Number of PDFs stored in the new version.
extracted_config
object
Configuration derived from the uploaded PDFs.
extracted_config.metadata_config
object<string,string>
Regex patterns per PDF metadata field (producer, title, author, creator, subject, keywords, creation_date). Fields absent from all uploads are omitted.
extracted_config.fonts
string[]
Union of font family names found across the uploaded PDFs.
extracted_config.similarity_threshold
float
Layout cosine-similarity threshold stored on the version.
API key passed as a query parameter (Set Auth to No Auth in headers).
Response fields
Field
Type
Description
document_id
string
Document type identifier.
active_version
string
The currently active version for this document type.
metadata_config
object<string,string>
Regex patterns evaluated against the PDF's metadata fields. Keys: producer, title, author, creator, subject, keywords, creation_date. Only keys with rules are present.
fonts
string[]
Font families that must appear in any matching PDF.
{
"matched": false,
"is_epdf": false,
"message": "PDF appears to be scanned/image-based; tamper checks skipped.",
"results": []
}
Reading the result. A top-level matched: true means the PDF matched at least one registered document type. The per-document reasons object explains which sub-checks passed or failed — useful for surfacing actionable feedback to operators.
3Fraud Signals
Take structured document data and emit categorised fraud verdicts plus lender-ready financial signals. Currently supports payslips and employment certificates.
What's evaluated
Category
What it checks
Arithmetic Integrity
Net pay reconciliation, cross-field consistency, income stability across periods.
Document Credibility
Trust score, format reliability, employment formality, pay frequency cadence.
Cross-Document Checks
Employer consistency and pay period continuity when multiple documents are submitted.
Statutory Compliance
Region-specific contribution and tax compliance (SSS, PhilHealth, Pag-IBIG, withholding, minimum wage for region=ph).
POST/cms/v1/fraud-detection/{document_type}/runEvaluate from JSON payload
Use this when you already hold the structured document data (typically the response body from OCR).
At least one document. Every item's type must match the path.
documents[].type
string
required
Document type; must equal {document_type}.
documents[].document_id
string
optional
Your identifier. Defaults to doc_0, doc_1, …
documents[].data
object
required
Extracted structured data for the document.
Response fields
All three Fraud Signals endpoints (/run, /run-from-session, GET /runs/{run_id}) return the same FraudDetectionResponse shape documented below. For the full enumeration of payslip checks, per-status examples, and HTTP error responses, see Possible responses — payslip.
Field
Type
Description
run_id
string (UUID)
Unique identifier for this evaluation. Persist this — you can refetch the result later with GET /runs/{run_id}.
document_type
string
payslip or employment_certificate. Echoes the path parameter.
timestamp
string (ISO-8601)
When the run was executed (UTC).
overall_status
enum
Worst status across all categories: pass, warn, fail, or inconclusive.
categories[]
object[]
One entry per category evaluated. See Category below.
signals
object
Lender-ready summary derived from the document. See Signals below.
gcs
object<string,string>
Internal storage references for the audit trail; safe to ignore in client code.
metadata
object
Request context and processing details. See Metadata below.
session_data
object[] · nullable
Present only for /run-from-session. Lists the upstream sessions resolved: session_id, product, document_type.
Issues raised by the check. Each finding has: severity (high/medium/low), code (stable identifier), message (description), evidence (object with relevant raw values).
Signals
Field
Type
Description
signals.financial_summary
object
Normalised financial figures pulled from the document. Each field is nullable.
financial_summary.gross_pay
number
Gross compensation for the period.
financial_summary.net_pay
number
Take-home amount.
financial_summary.basic_pay
number
Base salary component.
financial_summary.total_deductions
number
Sum of all deductions.
financial_summary.sss_contribution
number
SSS contribution (PH).
financial_summary.philhealth_contribution
number
PhilHealth contribution (PH).
financial_summary.pagibig_contribution
number
Pag-IBIG contribution (PH).
financial_summary.withholding_tax
number
Withholding tax for the period.
financial_summary.takehome_ratio
number
net_pay / gross_pay — useful for affordability scoring.
signals.lender_signals[]
object[]
Cards summarising the document for underwriting. Each entry has: name (identifier), value (any), status (pass/warn/fail/inconclusive), evidence (object · nullable).
The upstream product key. Errors include the list of supported values.
Response fields
Same FraudDetectionResponse shape as POST /run, with the session_data field populated.
Field
Type
Description
session_data
object[]
One entry per resolved upstream session.
session_data[].session_id
string
The session ID supplied in the request.
session_data[].product
string
The upstream product key (e.g. idp).
session_data[].document_type
string
Document type of the fetched record. Must match the path parameter.
All other fields (run_id, overall_status, categories, signals, metadata) are identical to the /run response.
Example — 200 OK
{
"run_id": "3f8c…",
"document_type": "payslip",
"timestamp": "2026-06-16T09:14:02Z",
"overall_status": "pass",
"session_data": [
{
"session_id": "3b41c2e0-9d2a-4e5e-bb37-2b8e0c0fbb12",
"product": "idp",
"document_type": "payslip"
}
],
"categories": [ /* same shape as /run */ ],
"signals": { /* same shape as /run */ },
"metadata": { /* same shape as /run */ }
}
GET/cms/v1/fraud-detection/runs/{run_id}Fetch a past run
Refetch a previously executed run by run_id.
Path parameters
Name
Type
Description
run_id
string (UUID)
The run_id returned by a previous /run or /run-from-session call under the same account.
Response fields
Returns the same FraudDetectionResponse shape as POST /run. If the original run was session-based, session_data will be populated as well.
Errors
Code
Reason
404
No run with that run_id exists under your account.
Possible responses — payslipChecks catalog, per-status examples, and errors
Every payslip check the pipeline can emit, one worked example body per overall_status, and the HTTP error responses shared by all three Fraud Signals endpoints. Applies to document_type = payslip with region = ph; other regions and document types have their own catalogs.
Payslip check catalog
Each check surfaces under its parent category with a stable name, a status, and — when a rule fires — one or more findings. Cross-document checks are only meaningful when two or more payslips are submitted in the same request; with a single payslip they resolve to inconclusive, which is not a negative signal. Finding code values are stable identifiers; new codes may be added as the pipeline evolves, so treat the illustrative codes in the examples below as representative rather than exhaustive.
Category
Check name
What it evaluates
Possible outcomes
Arithmetic Integrity
net_pay_reconciliation
gross_pay − total_deductions = net_pay.
pass when the identity holds · fail on mismatch · inconclusive if any of the three figures is missing.
cross_field_consistency
Individual deductions sum to the stated total_deductions; related figures agree with each other.
pass · warn or fail depending on how many sub-checks disagree.
Detected pay cycle; unknown means dates could not be resolved.
Example — 200 OK · overall_status: "pass"
Single payslip; everything balances and every mandatory deduction is present. Cross-document checks resolve to inconclusive (single document) but do not affect the overall verdict because they are not evaluated in the aggregate.
Reading inconclusive. An inconclusive verdict is not a fraud signal — it means the pipeline could not decide. Typical causes: single-payslip submission (cross-document checks cannot run), missing key fields (SSS number, pay-period dates), or statutory rate tables unavailable for the payslip's year or region. Requesting a more complete or more recent payslip is usually the right response.
HTTP error responses
All three Fraud Signals endpoints share the same error surface. The body shape is { "detail": … } — either a string (validation errors) or an object with error_code and message (upstream and downstream failures).
Code
When
Example body
401
Missing or invalid Authorization: Bearer token.
{"detail": "Invalid API key."}
404
GET /runs/{run_id}: no run with that ID under this account. /run-from-session: the upstream session_id was not found for the given product.
{"detail": "Run not found."}
422
Body validation failed. Includes: empty documents[], an item whose type disagrees with the path document_type, unknown product on /run-from-session, or the resolved session's document type not matching the path.
{"detail": "All documents must be of type 'payslip'. Got: ['employment_certificate']"}
500
Unhandled pipeline error. Safe to retry — runs are idempotent per submission.
# Initial template — let the service derive everything (No Auth header, use ?apikey=)
curl -X POST "https://mock.powercred.io/idp/fraud/templates/{document_id}/auto?apikey=$TAMPER_KEY" \
-F "files=@./template_v1.pdf" \
-F "similarity_threshold=0.85"
# → { "version_id": "v1", ... }
# Update template → upload new reference, append to existing
curl -X PUT "https://mock.powercred.io/idp/fraud/templates/{document_id}?apikey=$TAMPER_KEY" \
-F "files=@./template_v2.pdf" \
-F "file_mode=append"
# → { "version_id": "v2", ... }
# Roll back to the previous version
curl -X PATCH "https://mock.powercred.io/idp/fraud/templates/{document_id}/versions/v1/activate?apikey=$TAMPER_KEY"
HTTP status codes
Code
Meaning
200
Successful request. Payload included.
201
Resource created (template versions).
202
Accepted / processing not yet complete.
400
Bad request — usually a malformed file or unsupported MIME type.
401
Missing or invalid authentication credential.
404
Resource not found.
422
Validation failed — missing required field, invalid document type, etc.
500
Server error. Safe to retry idempotent calls.
Frequently Asked Questions (FAQ)
Answers to common questions regarding authentication, document types, and pipeline integration.
1. How does authentication work across the Document Intelligence APIs?
Pass your API key as a query parameter (?apikey=<apikey>) on every request across all services (OCR, Tamper Detection, and Fraud Signals). Do not send the Authorization: Bearer header.
2. Is document_type limited to payslips?
No. {document_type} is a path/query parameter across all services. Supported document types include payslip, employment_certificate, national_id, and bank_statement. Each unique document layout should use a distinct document_id when registering templates.
3. How do I link an OCR result to Fraud Signals without re-uploading data?
When you submit a document to POST /idp/v1/read, the server returns a unique job id. Once complete, call POST /cms/v1/fraud-detection/{document_type}/run-from-session with "session_id": "<id>" and "product": "idp". The platform automatically resolves the OCR output internally.
4. What happens when a scanned PDF or photo of a document is submitted to Tamper Detection?
Tamper Detection verifies digital ePDF features (embedded fonts, PDF metadata patterns, and layout grid). If a document contains fewer than 300 characters of extractable text, it is classified as a scanned document. The service returns is_epdf: false and matched: true with an informational message so your pipeline can continue to OCR.
5. How does template versioning work for Tamper Detection?
Every create (`POST`), update (`PUT`), or file delete (`DELETE`) creates a new immutable version (`v1`, `v2`, `v3`). Old versions are retained for audit history. The latest version becomes active automatically, but you can switch active versions anytime via `PATCH /templates/{document_id}/versions/{version_id}/activate`.
6. What are the file size and format limits?
The maximum upload file size is 5 MB per document across all endpoints. OCR accepts PDF, JPEG, and PNG files. Tamper Detection requires vector/text PDF documents.