Async verification jobs
Non-blocking verification for bulk triage and slow PDFs. Same forensic engine as POST /verify — returns immediately with job_id, then poll or receive a webhook.
When to use: document batches (claims packs, utility bills), worker queues, or integrations that must not hold an HTTP connection for 10–60s. Single-file sync remains POST /verify.
Endpoints
| Method | Path | Purpose |
|---|---|---|
POST | /api/v1/jobs | Enqueue one file |
POST | /api/v1/jobs/batch | Enqueue up to 20 files (shared batch_id) |
GET | /api/v1/jobs/{job_id} | Poll status |
GET | /api/v1/jobs?batch_id=… | List all jobs in a batch + summary counts |
GET | /api/v1/jobs/{job_id}/result | Full result (same shape as /verify) when status=completed |
Job lifecycle
queued → in_progress → completed | failed
Poll response example:
{
"job_id": "JOB-A1B2C3D4E5F6",
"status": "completed",
"batch_id": "BATCH-…",
"analysis_id": "ANL-…",
"pdf_mode": "structural",
"model": "plica_pdf/structural/v1",
"container_hit": true,
"container_signals_flagged": 1,
"narrative_status": "ready",
"narrative_explanation": "The amount on page 1 was changed from $1,250.00 to $5,750.00 after the document was created.",
"progress": "completed",
"links": {
"poll": "https://api.plicaforensic.com/api/v1/jobs/JOB-…",
"result": "https://api.plicaforensic.com/api/v1/jobs/JOB-…/result"
}
}
Create response (PDF) also echoes pdf_mode and model immediately:
{
"job_id": "JOB-…",
"status": "queued",
"poll_url": "https://api.plicaforensic.com/api/v1/jobs/JOB-…",
"pdf_mode": "structural",
"model": "plica_pdf/structural/v1",
"links": { "poll": "…" }
}
Form fields (same as /verify)
image (single job) or repeated files (batch), plus optional:
detail_level—basic|detaileddocument_type,customer_id,additional_info(JSON object string)webhook_url— HTTPS endpoint forplica.analysis.completedagent_fast,pdf_mode,generate_narrative,force_reanalyze,preserve_exif,product_source
Batch — cURL
curl -s -X POST "https://api.plicaforensic.com/api/v1/jobs/batch" \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "files=@statement_jan.pdf" \
-F "files=@statement_feb.pdf" \
-F "files=@utility_bill.pdf" \
-F "detail_level=detailed" \
-F "customer_id=CUST-48291" \
-F 'additional_info={"case_ref":"CLM-48291","queue":"claims"}' \
-F "webhook_url=https://hooks.example.com/plica/analysis"
Response:
{
"batch_id": "BATCH-…",
"jobs": [
{ "job_id": "JOB-…", "status": "queued", "poll_url": "…/jobs/JOB-…" },
…
]
}
Batch — poll aggregate (cURL)
curl -s "https://api.plicaforensic.com/api/v1/jobs?batch_id=BATCH-XXXXXXXXXXXX" \
-H "Authorization: Bearer YOUR_API_KEY"
Batch — Python SDK-style helper
import time
import requests
API = "https://api.plicaforensic.com/api/v1"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}
paths = ["statement_jan.pdf", "statement_feb.pdf", "utility_bill.pdf"]
multipart = [("files", (p, open(p, "rb"), "application/pdf")) for p in paths]
data = {
"detail_level": "detailed",
"customer_id": "CUST-48291",
"additional_info": '{"case_ref": "CLM-48291"}',
"webhook_url": "https://hooks.example.com/plica/analysis",
}
batch = requests.post(f"{API}/jobs/batch", headers=HEADERS, files=multipart, data=data, timeout=120)
batch.raise_for_status()
payload = batch.json()
batch_id = payload["batch_id"]
def poll_batch(batch_id: str, timeout_s: int = 600, interval_s: float = 2.0):
deadline = time.time() + timeout_s
while time.time() < deadline:
r = requests.get(f"{API}/jobs", params={"batch_id": batch_id}, headers=HEADERS, timeout=30)
r.raise_for_status()
status = r.json()
s = status["summary"]
if s["queued"] == 0 and s["in_progress"] == 0:
return status
time.sleep(interval_s)
raise TimeoutError(f"batch {batch_id} not finished in {timeout_s}s")
status = poll_batch(batch_id)
for job in status["jobs"]:
if job["status"] != "completed":
print(job["job_id"], job["status"], job.get("error"))
continue
result = requests.get(job["links"]["result"], headers=HEADERS, timeout=60).json()
claims = result.get("forensic_view", {}).get("claim_lists", {})
critical = len(claims.get("critical") or [])
print(job["analysis_id"], result["verdict"], result["fraud_score"], f"critical={critical}")
Single file async (cURL)
JOB=$(curl -s -X POST "https://api.plicaforensic.com/api/v1/jobs" \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "image=@passport_scan.jpg" \
-F "detail_level=detailed" | jq -r .job_id)
until [ "$(curl -s -H "Authorization: Bearer YOUR_API_KEY" \
"https://api.plicaforensic.com/api/v1/jobs/$JOB" | jq -r .status)" = "completed" ]; do sleep 2; done
curl -s -H "Authorization: Bearer YOUR_API_KEY" \
"https://api.plicaforensic.com/api/v1/jobs/$JOB/result" | jq '.verdict, .fraud_score'
Completion webhook
When webhook_url is set, PLICA POSTs:
{
"event": "plica.analysis.completed",
"version": "1",
"job_id": "JOB-…",
"batch_id": "BATCH-…",
"analysis_id": "ANL-…",
"status": "completed",
"verdict": "suspicious",
"fraud_score": 62,
"links": { "poll": "…", "result": "…" }
}
Optional HMAC: set server env ANALYSIS_JOB_WEBHOOK_SECRET — verify header X-Plica-Signature (same scheme as policy webhooks).
Limits: max 20 files per batch request; same upload size limits as
/verify. Jobs run in-process via background tasks — for very high volume, plan a dedicated worker queue in front of the API.