Skip to main content
Documentation
Learning Center

Master content moderation with comprehensive guides, tutorials, and API documentation

Quick Links

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.

Written by the Discuse Team · Last updated June 2026

Related Articles

Text Analysis and Sentiment Detection

Detect spam, toxicity, profanity, and analyze sentiment in text content

Image NSFW Detection

Automatically detect and filter inappropriate images and adult content

Spam Detection

AI-powered spam filtering for text and messages