File Antivirus Scanning
Discuse scans files for malware before they reach your users. There are two ways to call it: enable check_antivirus on POST /api/v2/check to scan document URLs alongside other checks, or use the dedicated POST /api/v2/scan endpoint to scan a single file and get a full report including the threat name and SHA-256 hash.
Why scan uploaded files?
File uploads are a common attack vector. Distributing malware through your platform risks user data, your reputation, and your own infrastructure. Scanning each file before you store or serve it closes that gap.
How does scanning work?
When you submit a file URL, Discuse downloads the file to an isolated environment, scans it, and returns whether a threat was found. Identical files are cached so repeat scans are fast.
Option 1: scan files inside a /check request
Pass document URLs in content.document_urls and enable check_antivirus. This is the right call when you are moderating a message that also carries an attachment.
curl -X POST https://api.discuse.com/api/v2/check \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"content": {
"document_urls": ["https://example.com/uploaded-file.pdf"]
},
"settings": {
"check_antivirus": true
}
}'
A single request accepts up to 5 document URLs.
Response
{
"has_violations": true,
"cached": false,
"message": "Malware detected in file",
"results": {
"hits": true,
"antivirus": {
"status": "FOUND",
"hit": true,
"details": [
{
"type": "malware",
"details": "Trojan.GenericKD.12345678",
"result": true
}
]
}
}
}
The antivirus result under results.antivirus has three fields: status, hit, and a details array. Each details entry carries type, details (the threat name or message), confidence, and result.
Option 2: scan a single file with /api/v2/scan
The dedicated scan endpoint returns a richer report for one file, including the threat name, the file hash, and scan duration.
curl -X POST https://api.discuse.com/api/v2/scan \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"file_url": "https://example.com/uploaded-file.pdf",
"file_name": "uploaded-file.pdf"
}'
Response
{
"hit": true,
"status": "FOUND",
"description": "Trojan.GenericKD.12345678",
"file_name": "uploaded-file.pdf",
"file_hash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"scan_time_ms": 1250
}
/api/v2/scan request fields
| Field | Type | Notes |
|---|---|---|
api_key |
string | Optional in body; you can send X-API-Key instead |
file_url |
string | URL of the file to scan |
file_name |
string | Optional filename for the report |
Provide file_url. Base64 file_data is defined in the schema but is not yet supported — a request with only file_data returns an error asking you to use file_url.
/api/v2/scan response fields
| Field | Type | Description |
|---|---|---|
hit |
boolean | True when malware was detected |
status |
string | OK (clean), FOUND (malware), or ERROR |
description |
string | Threat name, or an error message |
file_name |
string | The scanned filename |
file_hash |
string | SHA-256 hash of the file |
scan_time_ms |
number | Scan duration in milliseconds |
Usage limits
Antivirus scanning is available on paid plans only. Each file scanned counts once against the quota.
| Plan | Monthly Scans | Overage Rate |
|---|---|---|
| Basic | Not available | - |
| Gold | 500 | $0.001/scan |
| Platinum | 1,500 | $0.00085/scan (15% discount) |
| Ultimate | 3,000 | $0.00075/scan (25% discount) |
If a project has no active subscription, antivirus scans are denied.
Best practices
Scan before storage
Scan a file before storing it permanently, so an infected file never enters your system:
async function handleFileUpload(fileUrl, fileName) {
const result = await scanFile(fileUrl, fileName);
if (result.hit) {
throw new Error('Infected file detected: ' + result.description);
}
await storeFile(fileUrl);
}
Branch on status
Distinguish a clean file from a scan that failed:
const result = await scanFile(fileUrl, fileName);
switch (result.status) {
case 'FOUND': await rejectAndNotify(fileUrl, result.description); break;
case 'ERROR': await quarantineForReview(fileUrl); break;
case 'OK': await storeFile(fileUrl); break;
}
Reuse consistent URLs
Identical files are cached. Using stable file URLs lets repeat scans return from cache instead of re-downloading.
Integration examples
Node.js (dedicated scan endpoint)
async function scanFile(fileUrl, fileName) {
const response = await fetch('https://api.discuse.com/api/v2/scan', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.DISCUSE_API_KEY
},
body: JSON.stringify({ file_url: fileUrl, file_name: fileName })
});
return response.json();
}
Python (dedicated scan endpoint)
import os
import requests
def scan_file(file_url, file_name=None):
response = requests.post(
'https://api.discuse.com/api/v2/scan',
headers={
'Content-Type': 'application/json',
'X-API-Key': os.environ['DISCUSE_API_KEY']
},
json={'file_url': file_url, 'file_name': file_name}
)
return response.json()
Ready to protect your platform from malware? Get started with Discuse.