POST /api/v1/verify

Compliance & analyst path. Images and PDFs with the full presentation layer — evidence lifecycle, lineage-driven HITL, and export-ready forensic detail for regulated workflows.

Choose this when: MLRO/compliance needs custody artifacts, analysts read forensic_view, or lineage must force review on an otherwise-clean file. See endpoint comparison on the overview.
POST /api/v1/verify
Content-Type: multipart/form-data
Authorization: Bearer {api_key}

Input (multipart)

FieldRequiredDescription
imageone of image / image_urlFile upload — JPEG, PNG, HEIC, WebP, or PDF
image_urlone of image / image_urlPublic HTTPS URL to same formats
detail_levelnobasic (fast) or detailed (full EXIF + PDF phases). Default: basic
preserve_exifnotrue if bytes still carry EXIF (document upload). Default: false
document_typenoHint: passport, bank_statement, utility_bill, … Enables ID barcode checks on PDFs
sourcenoPlatform hint: linkedin, telegram, …
product_sourcenoCaller id for telemetry. Default: fraudlens_api
force_reanalyzenoSkip UFID cache, run fresh analysis
agent_fastnoDeprecated. PDF: same as pdf_mode=structural
pdf_modenoPDF only: metadata_fast | structural | full (default full). Response echoes pdf_mode + model (e.g. plica_pdf/structural/v1)
generate_narrativenoPDF only: async LLM narrative over structured container findings (default false)
include_visionnoReserved paid tier — not used in v1

Document scan images (JPEG / PNG)

Messaging-app scans (sterile EXIF, flat uploads) need explicit document routing:

curl -s -X POST "https://api.plicaforensic.com/api/v1/verify" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "image=@scan.jpg" \
  -F "detail_level=detailed" \
  -F "preserve_exif=true" \
  -F "document_type=utility_bill"

Modes

Images

PDFs

PDF analysis is two-layer — not a single thumbnail check:

  1. Container forensics — structure, revisions, OCR/text layer, XObjects, math checks (details.pdf_container, forensic_view.pdf_container).
  2. Embedded rasters — JPEG/PNG extracted from page content; pixel forensics run on selected images and merge into composite embedded score.
Which raster is verified? Default mode (pdf_mode=full): the largest embedded raster by pixel area (largest_only). Fragment mode (heavy XObject splitting): up to 12 fragments. This is independent of visual order in the PDF. Container signals remain primary for text/template edits.

Downloaded forensic PDF reports reference the source PDF file in “Analyzed Evidence” — they do not embed a raster preview of an extracted page image.

Limits & quotas

LimitPLICANotes
Max PDF upload50 MBMulti-page container forensics on full document
Max image upload20 MBJPEG, PNG, HEIC, WebP
Offline evidence verifyFEM + Packet AnalyzerPLICA-only offline path

PDF container fields (top-level)

PDF responses include integrator-friendly fields alongside details.pdf_container and forensic_view.pdf_container:

Set generate_narrative=true on PDF uploads to enqueue async narrative (LLM over structured findings only). Poll job status or re-fetch analysis until narrative_status=ready.

OperationCredits
Image basic1
Image detailed3
PDF metadata_fast1
PDF structural2 × pages
PDF full5 × pages

Detection stack (summary)

  1. Definitive proof — AI tool markers, temporal paradoxes, C2PA where present
  2. Evidence scoring — weighted EXIF / metadata signals
  3. Intrinsic forensics — pixel-level (works without metadata)
  4. Layer 1 maps — localized edit candidates on document scans (tamper_regions)
  5. PDF container — editing software, incremental updates, text/OCR anomalies
  6. Lineage — XMP ID cross-submission signals → review escalation

Example (Python)

import requests

with open("statement.pdf", "rb") as f:
    r = requests.post(
        "https://api.plicaforensic.com/api/v1/verify",
        headers={"Authorization": "Bearer YOUR_API_KEY"},
        files={"image": ("statement.pdf", f, "application/pdf")},
        data={
            "detail_level": "detailed",
            "document_type": "bank_statement",
        },
        timeout=120,
    )
    r.raise_for_status()
    result = r.json()
    print(result["verdict"], result["fraud_score"])
    print(result["analyst_summary"]["verdict_tier"])

Related endpoints