Skip to content
Patronus
Website

Results and evidence

Read scan status, category results, and evidence, and handle incomplete analysis.

A scan has two separate outcomes: whether the analysis finished, and what it found. Check both before acting on the content.

Every scan uses the same status, input, extraction, coverage, usage, and jobs envelope. A completed anonymous request contains its jobs directly; those inline jobs have no status_url and cannot be polled. An authenticated URL or file request normally returns 202 accepted with job references and uses GET /api/v1/scan/{job_id} for completed jobs. A URL has one job per extracted segment; a file has one job per file. Top-level metadata describes the submission, while categories, decision, and safety_status describe each completed job. Completed inline responses also include those fields at the top level for existing clients.

URL responses include segments: each item has a stable id, a kind describing the extracted surface, its graphemes, and, once complete, its category results and evidence. The canonical text sent to Ark starts with a short source label for the segment kind; chunk offsets refer to that labeled text. Static URL extraction reports partial on both access paths because dynamic page content is not scanned. This coverage metadata does not change an otherwise complete final classification.

The job status is only lifecycle state: running, completed, or failed. The decision summarizes how an application should handle the result:

DecisionMeaning
allowFinal classifications for the requested categories are benign.
blockAt least one requested category reported a finding.
reviewA final classification is missing or inconclusive.

safety_status is attack for a final attack. With a benign final result it is review only when the category result has level: "L3", decision_evidence.stage is union, and a decisive attack chunk from source: "l3" differs from the final class with confidence strictly greater than 0.1; otherwise it is benign. With a benign final result, decision stays allow. Extraction coverage is reported separately; clients can apply their own policy to partial coverage.

For PDFs, coverage.input_tokens counts the extracted text sent to analysis, not the binary file size. Images, fonts, layout data, and compression increase PDF bytes without becoming text tokens. pages_scanned / pages_total shows how many pages supplied a readable text layer; pages without one produce partial coverage because OCR is not currently performed.

  1. Job status: continue polling running; stop on completed or failed.
  2. Extraction and coverage: partial means some supplied content was not represented in canonical scan text; make a separate coverage decision.
  3. Completion: inspect completion.state and any failures. A completed job can still have degraded analysis.
  4. Categories: read each requested category’s classification and evidence. Missing results do not establish that an input is clean.
  5. Application policy: decide what to allow, block, redact, or send for review.
SituationInterpretationSuggested handling
Completed, complete analysis, expected safe classificationThe requested analysis finished without a reported failure.Apply your policy; a scan cannot guarantee the absence of risk.
Completed, complete analysis, detection reportedAnalysis finished and found a signal.Use category results and evidence to choose an action.
Completed, degraded analysisSome analysis failed or was unavailable.Review or use a fallback; do not silently treat it as clean.
Failed job or failed completionAnalysis did not complete successfully.Handle the failure explicitly.
Missing completion or requested categoryInsufficient information for a complete result.Use your fallback policy.

These are handling examples, not fixed classifications. class_name, decision, model identifiers, and evidence labels are open strings. Do not infer an allow decision from HTTP 200, accepted, or confidence alone.

This example shows why a terminal job still needs a completion check. Its classification values are illustrative.

{
"job_id": "job_0123456789abcdef0123456789abcdef",
"source": "text",
"status": "completed",
"input": {"kind": "text", "media_types": ["text/plain"]},
"extraction": {"version": "document-v1", "state": "complete", "documents_total": 1, "documents_scanned": 1, "warnings": []},
"coverage": {"state": "complete", "input_tokens": 12, "documents_total": 1, "documents_scanned": 1},
"usage": {"input_tokens": 12, "scan_units": 1, "request_units": 1, "unit_version": "scan-unit-v1"},
"progress": {},
"categories": {
"injection": {
"class_name": "safe",
"confidence": 0.99,
"evidence_spans": []
}
},
"completion": {
"state": "degraded",
"failures": [{"stage": "l2", "kind": "timeout", "retryable": true}]
}
}

The two evidence fields answer different questions:

  • decision_evidence locates the analyzed chunk associated with a decision candidate. For injection and other document-level classifiers, use decisive_chunks[].span to locate it in the canonical scan text. These offsets are character offsets.
  • evidence_spans contains detected entities with a label, score, text, and optional byte or character offsets. This is the primary evidence shape for PII and DLP. It can legitimately be empty for an injection result even when decision_evidence identifies a decisive chunk.

detectors lists every model that reported a result for each category. The category’s model is the final model selected for the verdict; an earlier detector such as native:dlp can therefore appear in detectors.dlp even when a later DLP model determines the final classification. Both synchronous and asynchronous account jobs include this field.

decision_evidence can also describe a rejected risk candidate. A chunk may have class_name: "attack" while the final injection category has class_name: "benign", accepted: false, and final_result.class_name: "benign". With category level: "L3", evidence stage: "union", chunk source: "l3", and attack confidence above 0.1, the response is decision: "allow" and safety_status: "review". At 0.1 or below, at another stage or level, or from another chunk source, it is allow and benign. A final attack yields decision: "block" and safety_status: "attack".

Public responses include only decisive chunks. Per-chunk contributor diagnostics are internal because they do not explain the final verdict and can make document responses very large.

Preserve the original input when using offsets to highlight text; changing whitespace or Unicode normalization can move the corresponding positions. For extracted documents, offsets refer to the canonical text produced by extraction, not necessarily the original binary file layout.

final_result, when present, provides the final category classification. timings.total_ms is the end-to-end scan latency reported by the scan backend; the other timing keys break down processing stages. Timing fields are diagnostic data, not an additional safety decision.

Optional fields may be absent. Clients should tolerate additional response fields in future versions and avoid depending on specific model names or timing keys for enforcement.

FieldTypeDetails
statusstringRequired.
inputInputMetadataRequired.
extractionExtractionMetadataRequired.
coverageCoverageMetadataRequired.
segmentsUrlSegment[]Optional. Extracted URL surfaces, with results after completion.
jobsAcceptedJob[]Required.
usageScanUsageRequired. Usage for the submission; supplied on successful POST responses.
FieldTypeDetails
job_idstringRequired.
sourcestringRequired.
status_urlstringRequired. Relative API path. Poll with the same account and scan:read permission.
FieldTypeDetails
jobsSubmissionJob[]Required.
statusstringRequired.
inputInputMetadataRequired.
extractionExtractionMetadataRequired.
coverageCoverageMetadataRequired.
segmentsUrlSegment[]Optional.
categoriesMap<string, CategoryResult>Optional.
decisionallow | review | blockOptional.
safety_statusSafetyStatusOptional.
usageScanUsageRequired.
FieldTypeDetails
job_idstringRequired.
sourcestringRequired.
statusrunning | completed | failedRequired.
progressMap<string, Progress>Required.
categoriesMap<string, CategoryResult>Required. Results keyed by requested category: injection, dlp, pii, or threat.
detectorsobjectOptional. Model identifiers that reported results, keyed by category, including detectors superseded by a later category result.
completionCompletionOptional.
decisionstringOptional.
safety_statusSafetyStatusOptional.
segmentsUrlSegment[]Optional.
timingsTimingsOptional.
FieldTypeDetails
job_idstringRequired.
sourcestringRequired.
statusrunning | completed | failedRequired.
inputInputMetadataRequired.
extractionExtractionMetadataRequired.
coverageCoverageMetadataRequired.
progressMap<string, Progress>Required.
categoriesMap<string, CategoryResult>Required. Results keyed by requested category: injection, dlp, pii, or threat. Check every requested category; missing results do not establish clean input.
jobsSubmissionJob[]Optional.
segmentsUrlSegment[]Optional.
detectorsobjectOptional. Model identifiers that reported results, keyed by category, including detectors superseded by a later category result.
completionCompletionOptional.
decisionstringOptional. Scan decision, when present. The client remains responsible for enforcing its policy.
safety_statusSafetyStatusOptional.
timingsTimingsOptional.
usageScanUsageRequired. Usage for the submission; supplied on successful POST responses.
FieldTypeDetails
kindtext | url | mcp_server | documentsRequired.
media_typesstring[]Required.
FieldTypeDetails
versionstringRequired.
statecomplete | partialRequired.
documents_totalintegerRequired.
documents_scannedintegerRequired.
pages_totalintegerOptional.
pages_scannedintegerOptional.
warningsstring[]Required.
FieldTypeDetails
statecomplete | partialRequired.
input_tokensintegerRequired.
documents_totalintegerRequired.
documents_scannedintegerRequired.
pages_totalintegerOptional.
pages_scannedintegerOptional.
FieldTypeDetails
input_tokensintegerRequired. Input tokens summed across the prepared scan contents.
scan_unitsintegerRequired. Billable Scan Units: max(1, ceil(input_tokens / 1000)) for every input type.
request_unitsintegerRequired. Compatibility alias for scan_units.
unit_versionstringRequired.
FieldTypeDetails
statecomplete | degraded | failedRequired.
failuresFailure[]Optional.
FieldTypeDetails
stagestringRequired.
kindstringRequired.
retryablebooleanRequired.
levelstringOptional.
detector_idstringOptional.
FieldTypeDetails
categorystringOptional.
class_namestringOptional.
levelstringOptional.
modelstringOptional.
confidencenumberOptional.
duration_msnumberOptional.
acceptedbooleanOptional.
final_resultFinalResultOptional.
decision_evidenceDecisionEvidenceOptional.
evidence_spansEvidenceSpan[]Required.
FieldTypeDetails
class_namestringRequired.
confidencenumberRequired.
sourcestringRequired.
FieldTypeDetails
labelstringRequired.
textstringRequired.
scorenumberRequired.
start_byteintegerOptional.
end_byteintegerOptional.
start_charintegerOptional.
end_charintegerOptional.
FieldTypeDetails
stagestringOptional.
decisive_chunksEvidenceItem[]Optional.
FieldTypeDetails
chunk_idintegerOptional.
sourcestringOptional.
class_namestringOptional.
confidencenumberOptional.
spanobjectOptional.
FieldTypeDetails
categorystringOptional.
modelstringOptional.
stagestringOptional.
completed_chunksintegerOptional.
total_chunksintegerOptional.
inferred_chunksintegerOptional.
propagated_chunksintegerOptional.
cache_hitsintegerOptional.
early_exitbooleanOptional.
coveragenumberOptional.
FieldTypeDetails
queue_wait_msnumber | nullOptional.
worker_submit_msnumber | nullOptional.
worker_msnumber | nullOptional.
total_msnumber | nullOptional.
l2_msnumber | nullOptional.
l2_cache_hitnumber | boolean | nullOptional.