curl --request POST \
--url https://api.scanoss.com/v3/wfp/scan \
--header 'Content-Type: application/octet-stream' \
--data '<string>'import requests
url = "https://api.scanoss.com/v3/wfp/scan"
payload = "<string>"
headers = {"Content-Type": "application/octet-stream"}
response = requests.post(url, data=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/octet-stream'},
body: '<string>'
};
fetch('https://api.scanoss.com/v3/wfp/scan', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.scanoss.com/v3/wfp/scan",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "<string>",
CURLOPT_HTTPHEADER => [
"Content-Type: application/octet-stream"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.scanoss.com/v3/wfp/scan"
payload := strings.NewReader("<string>")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/octet-stream")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.scanoss.com/v3/wfp/scan")
.header("Content-Type", "application/octet-stream")
.body("<string>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.scanoss.com/v3/wfp/scan")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/octet-stream'
request.body = "<string>"
response = http.request(request)
puts response.read_body{
"scan_id": "018f7b2c-9a3e-7d41-8b6a-2c1d4e5f6a7b",
"status": "uploading",
"received_bytes": 123,
"total_bytes": 123,
"upload_percent": 123,
"phase": "<string>",
"phase_done": 123,
"phase_total": 123,
"files_total": 123,
"result": {
"server": {
"api_version": "<string>",
"knowledge_base": {
"monthly_version": "<string>",
"daily_version": "<string>"
},
"hostname": "<string>",
"elapsed_ms": 123
},
"files": [
{
"path": "<string>",
"source_hash": "<string>",
"file_hash": "<string>",
"match_type": "file",
"matches": [
{
"url_hash": "<string>",
"confidence": "LOW",
"oss_file_path": "<string>",
"match_percentage": 123,
"input_line_ranges": [
{
"start_line": 123,
"end_line": 123
}
],
"oss_line_ranges": [
{
"start_line": 123,
"end_line": 123
}
]
}
]
}
],
"components": {}
},
"error": "<string>"
}{
"scan_id": "018f7b2c-9a3e-7d41-8b6a-2c1d4e5f6a7b",
"status": "uploading",
"received_bytes": 123,
"total_bytes": 123,
"upload_percent": 123,
"phase": "<string>",
"phase_done": 123,
"phase_total": 123,
"files_total": 123,
"result": {
"server": {
"api_version": "<string>",
"knowledge_base": {
"monthly_version": "<string>",
"daily_version": "<string>"
},
"hostname": "<string>",
"elapsed_ms": 123
},
"files": [
{
"path": "<string>",
"source_hash": "<string>",
"file_hash": "<string>",
"match_type": "file",
"matches": [
{
"url_hash": "<string>",
"confidence": "LOW",
"oss_file_path": "<string>",
"match_percentage": 123,
"input_line_ranges": [
{
"start_line": 123,
"end_line": 123
}
],
"oss_line_ranges": [
{
"start_line": 123,
"end_line": 123
}
]
}
]
}
],
"components": {}
},
"error": "<string>"
}{
"status": "error",
"error": {
"code": "INVALID_SCAN_ID",
"message": "invalid scan id: not a UUIDv7"
},
"timestamp": "2026-05-19T11:39:56Z"
}{
"status": "error",
"error": {
"code": "RANGE_CONFLICT",
"message": "block does not continue from received_bytes"
},
"timestamp": "2026-05-19T11:39:56Z"
}{
"status": "error",
"error": {
"code": "PAYLOAD_TOO_LARGE",
"message": "upload exceeds max size"
},
"timestamp": "2026-05-19T11:39:56Z"
}{
"status": "error",
"error": {
"code": "INTERNAL_ERROR",
"message": "..."
},
"timestamp": "2026-05-19T11:39:56Z"
}{
"status": "error",
"error": {
"code": "ENGINE_UNAVAILABLE",
"message": "scan engine not available"
},
"timestamp": "2026-05-19T11:39:56Z"
}{
"status": "error",
"error": {
"code": "TIMEOUT",
"message": "request timed out"
},
"timestamp": "2026-05-19T11:39:56Z"
}Scan a WFP — synchronous single-shot, or one block of a chunked async upload
Dual-mode upload, selected by the presence of the X-Scan-Id request
header. Both modes return the same ScanEnvelope envelope, and the
result payload is byte-identical to the polled async result (both run
the same batchScanner pipeline → same Report).
Mode B — synchronous single-shot (no X-Scan-Id). The request body
is one complete WFP (application/octet-stream, capped at
scan.max_upload_size), scanned inline. Responds 200 with a
ScanEnvelope already at status: completed, carrying result,
files_total and a fresh scan_id (for traceability only — no session
is created, nothing to poll). Content-Range is ignored. An
empty/whitespace body → 400 INVALID_BODY. The request blocks for the
whole scan, so this mode is bounded by the request timeout and intended
for small inputs.
Mode A — chunked async (X-Scan-Id present). One block of a
multi-block upload too large for a single request. The client generates
the session id — a canonical lowercase UUIDv7 — and sends it on
every block (it is what a load balancer hashes on to keep all blocks
on one instance). Content-Range: bytes <start>-<end>/<total> is
required. Blocks are reassembled by their declared byte offset, so
they may be uploaded in any order or concurrently; the first
received block need not start at offset 0. Each block responds 202
with a ScanEnvelope (status: uploading). When the received ranges
fully cover [0, total) with no gaps, the upload is sealed and the
batchScanner pipeline is queued (its stages call the scan API over
HTTP); poll GET /v3/wfp/scan/{id} for progress/result.
received_bytes reports the total distinct bytes covered so far.
Re-POSTing an already-received range with identical bytes is an
idempotent 202 no-op.
Mode A errors: a non-canonical / non-UUIDv7 id → 400 INVALID_SCAN_ID;
missing Content-Range → 400 MISSING_RANGE; malformed Content-Range
(or a body length that disagrees with the range) → 400 INVALID_RANGE;
an overlapping block carrying conflicting bytes, or reuse of a
sealed id → 409 RANGE_CONFLICT (note: out-of-order arrival alone is
not a conflict); a block or declared total over the configured
limits → 413 PAYLOAD_TOO_LARGE.
curl --request POST \
--url https://api.scanoss.com/v3/wfp/scan \
--header 'Content-Type: application/octet-stream' \
--data '<string>'import requests
url = "https://api.scanoss.com/v3/wfp/scan"
payload = "<string>"
headers = {"Content-Type": "application/octet-stream"}
response = requests.post(url, data=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/octet-stream'},
body: '<string>'
};
fetch('https://api.scanoss.com/v3/wfp/scan', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.scanoss.com/v3/wfp/scan",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "<string>",
CURLOPT_HTTPHEADER => [
"Content-Type: application/octet-stream"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.scanoss.com/v3/wfp/scan"
payload := strings.NewReader("<string>")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/octet-stream")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.scanoss.com/v3/wfp/scan")
.header("Content-Type", "application/octet-stream")
.body("<string>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.scanoss.com/v3/wfp/scan")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/octet-stream'
request.body = "<string>"
response = http.request(request)
puts response.read_body{
"scan_id": "018f7b2c-9a3e-7d41-8b6a-2c1d4e5f6a7b",
"status": "uploading",
"received_bytes": 123,
"total_bytes": 123,
"upload_percent": 123,
"phase": "<string>",
"phase_done": 123,
"phase_total": 123,
"files_total": 123,
"result": {
"server": {
"api_version": "<string>",
"knowledge_base": {
"monthly_version": "<string>",
"daily_version": "<string>"
},
"hostname": "<string>",
"elapsed_ms": 123
},
"files": [
{
"path": "<string>",
"source_hash": "<string>",
"file_hash": "<string>",
"match_type": "file",
"matches": [
{
"url_hash": "<string>",
"confidence": "LOW",
"oss_file_path": "<string>",
"match_percentage": 123,
"input_line_ranges": [
{
"start_line": 123,
"end_line": 123
}
],
"oss_line_ranges": [
{
"start_line": 123,
"end_line": 123
}
]
}
]
}
],
"components": {}
},
"error": "<string>"
}{
"scan_id": "018f7b2c-9a3e-7d41-8b6a-2c1d4e5f6a7b",
"status": "uploading",
"received_bytes": 123,
"total_bytes": 123,
"upload_percent": 123,
"phase": "<string>",
"phase_done": 123,
"phase_total": 123,
"files_total": 123,
"result": {
"server": {
"api_version": "<string>",
"knowledge_base": {
"monthly_version": "<string>",
"daily_version": "<string>"
},
"hostname": "<string>",
"elapsed_ms": 123
},
"files": [
{
"path": "<string>",
"source_hash": "<string>",
"file_hash": "<string>",
"match_type": "file",
"matches": [
{
"url_hash": "<string>",
"confidence": "LOW",
"oss_file_path": "<string>",
"match_percentage": 123,
"input_line_ranges": [
{
"start_line": 123,
"end_line": 123
}
],
"oss_line_ranges": [
{
"start_line": 123,
"end_line": 123
}
]
}
]
}
],
"components": {}
},
"error": "<string>"
}{
"status": "error",
"error": {
"code": "INVALID_SCAN_ID",
"message": "invalid scan id: not a UUIDv7"
},
"timestamp": "2026-05-19T11:39:56Z"
}{
"status": "error",
"error": {
"code": "RANGE_CONFLICT",
"message": "block does not continue from received_bytes"
},
"timestamp": "2026-05-19T11:39:56Z"
}{
"status": "error",
"error": {
"code": "PAYLOAD_TOO_LARGE",
"message": "upload exceeds max size"
},
"timestamp": "2026-05-19T11:39:56Z"
}{
"status": "error",
"error": {
"code": "INTERNAL_ERROR",
"message": "..."
},
"timestamp": "2026-05-19T11:39:56Z"
}{
"status": "error",
"error": {
"code": "ENGINE_UNAVAILABLE",
"message": "scan engine not available"
},
"timestamp": "2026-05-19T11:39:56Z"
}{
"status": "error",
"error": {
"code": "TIMEOUT",
"message": "request timed out"
},
"timestamp": "2026-05-19T11:39:56Z"
}Headers
Absent → Mode B (synchronous single-shot). Present → Mode A (chunked async): a client-generated canonical lowercase UUIDv7, sent on every block of the session.
Required in Mode A (X-Scan-Id present): byte range of this block, e.g. bytes 0-8388607/26214400 (end inclusive, 0-based). Ignored in Mode B.
Body
The body is of type file.
Response
Mode B only. The WFP was scanned inline; ScanEnvelope is at status: completed with result and files_total populated.
Unified status envelope returned by POST /v3/wfp/scan (both modes) and GET /v3/wfp/scan/{id}. Mode B returns it synchronously already at status: completed; Mode A returns it per block (uploading) and on each poll. Zero-valued fields are omitted, so an uploading session carries no scan counters and a running scan carries no result.
Canonical lowercase UUIDv7 session id. In Mode A this is the client-supplied id; in Mode B it is a server-generated id for traceability only (no session is created).
"018f7b2c-9a3e-7d41-8b6a-2c1d4e5f6a7b"
uploading, queued, scanning, completed, failed, expired Bytes received so far (Mode A upload; omitted when 0).
Declared total upload size (Mode A; omitted when 0).
Exact upload progress percent (Mode A; omitted when 0).
Current pipeline pass while scanning, e.g. 'Pass 1: scan files'.
Items processed in the current phase.
Total items in the current phase.
File count of the report, filled in on completion.
Populated only when status is completed. The batchScanner report — byte-identical whether obtained synchronously (Mode B) or by polling an async session to completion (Mode A).
Show child attributes
Show child attributes
Populated only when status is failed.