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:
Decision
Meaning
allow
Final classifications for the requested categories are benign.
block
At least one requested category reported a finding.
review
A 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.
The requested analysis finished without a reported failure.
Apply your policy; a scan cannot guarantee the absence of risk.
Completed, complete analysis, detection reported
Analysis finished and found a signal.
Use category results and evidence to choose an action.
Completed, degraded analysis
Some analysis failed or was unavailable.
Review or use a fallback; do not silently treat it as clean.
Failed job or failed completion
Analysis did not complete successfully.
Handle the failure explicitly.
Missing completion or requested category
Insufficient 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.
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.
Required. Results keyed by requested category: injection, dlp, pii, or threat. Check every requested category; missing results do not establish clean input.
jobs
SubmissionJob[]
Optional.
segments
UrlSegment[]
Optional.
detectors
object
Optional. Model identifiers that reported results, keyed by category, including detectors superseded by a later category result.
completion
Completion
Optional.
decision
string
Optional. Scan decision, when present. The client remains responsible for enforcing its policy.
safety_status
SafetyStatus
Optional.
timings
Timings
Optional.
usage
ScanUsage
Required. Usage for the submission; supplied on successful POST responses.