Skip to main content

API Usage Guide

The VirtueRed API is a REST interface for running AI safety scans programmatically. The primary endpoint is Scan Start (POST /v1/scan/start), which launches safety evaluations against any OpenAI-compatible model endpoint.

Prerequisites

To use the VirtueRed API, you'll need:

  • A base URL — Each organization receives a dedicated deployment URL (e.g., https://<your-org>.virtueai.io). In all examples below, replace <your-base-url> with this URL.
  • A VirtueRed account — An account provisioned for your organization.
  • An API key — Used to authenticate every request via the X-API-Key header.

VirtueRed is currently available on-demand. Contact VirtueAI to receive your organization's deployment URL, account, and API key.

Quickstart

1) Start a scan

curl -X POST "https://<your-base-url>/v1/scan/start" \
-H "X-API-Key: <your-virtuered-api-key>" \
-H "Content-Type: application/json" \
-d '{
"scan_name": "quickstart-scan",
"model": {
"base_url": "https://api.openai.com/v1",
"api_key": "<your-model-api-key>",
"model_name": "gpt-4o"
},
"datasets": [
{"name": "Healthcare Risks"}
]
}'

Expected response:

{
"scan_id": "scan-12345",
"status": 200
}

Save the returned scan_id for subsequent requests.

2) Check progress

curl -X POST "https://<your-base-url>/v1/scan/progress" \
-H "X-API-Key: <your-virtuered-api-key>" \
-H "Content-Type: application/json" \
-d '{"scan_id": "scan-12345"}'

3) Get summary

Run this after scan status is Finished.

curl -X POST "https://<your-base-url>/v1/scan/summary" \
-H "X-API-Key: <your-virtuered-api-key>" \
-H "Content-Type: application/json" \
-d '{"scan_id": "scan-12345"}'

4) Download full artifacts

This step is optional.

curl -X POST "https://<your-base-url>/v1/scan/download" \
-H "X-API-Key: <your-virtuered-api-key>" \
-H "Content-Type: application/json" \
-d '{"scan_id": "scan-12345"}' \
-o scan-results.zip

Scan Lifecycle

StepEndpointWhen to use
StartPOST /v1/scan/startLaunch a standard scan with explicit datasets
Start (Sandbox)POST /v1/sandbox/scan/startQuick connection validation with pre-configured tests
MonitorPOST /v1/scan/progressPoll execution progress/status or list all scans (optional since)
SummarizePOST /v1/scan/summaryRetrieve aggregate scores and failures when finished
DownloadPOST /v1/scan/downloadExport ZIP artifacts (results.jsonl, summary.json, report.pdf)
PausePOST /v1/scan/pausePause an active scan
RetryPOST /v1/scan/retryResume from checkpoint when status is Paused, Failed, or Error
UsagePOST /v1/scan/usageRetrieve scan count for the current billing cycle, all time, or a custom range

Scan status reference

StatusMeaningNext action
InitializingSetup in progressKeep polling progress
ScanningRunning testsKeep polling progress
PausedStopped by userRetry when ready
FinishedCompleted successfullyGet summary and download artifacts
FailedRuntime failureRetry from checkpoint
ErrorUnexpected system errorRetry; if repeated, contact support

Endpoint Reference

Start a Scan

POST /v1/scan/start

Launch a safety scan with your model and dataset configuration.

Request body (JSON)

Content-Type: application/json.

FieldTypeRequiredDescription
scan_namestringYesUnique scan name
modelobjectYesModel provider configuration (see model object)
datasetsarrayYesDatasets to evaluate (see datasets[] item)
algorithmsarrayNoAdditional attack algorithms (see Available Additional Algorithms)
input_modalitiesarrayNoInput modalities (default: ["text"])
output_modalitiesarrayNoOutput modalities (default: ["text"])
qpmnumberNoQueries-per-minute limit against the target model
concurrencynumberNoMaximum concurrent requests to the target model

Rate-limit note: If qpm is not set, no explicit QPM cap is applied. Default concurrency is 8. Tune qpm and concurrency based on your target endpoint capacity and provider limits.

model object

FieldTypeRequiredDescription
base_urlstringYesOpenAI-compatible API base URL
api_keystringYesModel provider API key
model_namestringYesModel identifier (e.g., gpt-4o)
temperaturenumberNoSampling temperature (default 0.7)
max_tokensnumberNoMaximum output tokens (default 2048)
extra_headersobjectNoAdditional headers forwarded to the upstream model endpoint

Custom header note: For self-hosted or custom inference endpoints, set provider-specific headers in model.extra_headers. See Example: Custom endpoint with extra headers.

datasets[] item

FieldTypeRequiredDescription
namestringYesDataset name

Available Datasets

TypeRisk Categories
Regulation-based RisksEU Artificial Intelligence Act, AI Company Policies, General Data Protection Regulation, OWASP LLM Top 10, NIST AI RMF, MITRE ATLAS, FINRA
Use-Case-Driven RisksBias, Over Cautiousness, Hallucination, Societal Harmfulness, Privacy, Robustness, Finance Brand Risk, Education Brand Risk, Health Care Brand Risk
Domain-specific RisksHealthcare Risks, Finance Risks, Retail Policy Compliance, IT/Tech Policy Compliance
Full datasets payload
"datasets": [
{"name": "Healthcare Risks"},
{"name": "Finance Risks"},
{"name": "Retail Policy Compliance"},
{"name": "IT/Tech Policy Compliance"},
{"name": "EU Artificial Intelligence Act"},
{"name": "AI Company Policies"},
{"name": "General Data Protection Regulation"},
{"name": "FINRA"},
{"name": "MITRE ATLAS"},
{"name": "NIST AI RMF"},
{"name": "OWASP LLM Top 10"},
{"name": "Bias"},
{"name": "Over Cautiousness"},
{"name": "Hallucination"},
{"name": "Societal Harmfulness"},
{"name": "Privacy"},
{"name": "Robustness"},
{"name": "Finance Brand Risk"},
{"name": "Education Brand Risk"},
{"name": "Health Care Brand Risk"}
]

Available Additional Algorithms

By default, every scan runs our curated set of red teaming tests combining multiple attack algorithms. You can optionally select additional attack algorithms to apply together with the defaults.

AlgorithmDescription
DarkCiteAuthority citation-driven jailbreak
Bijection LearningLearns custom encoding to bypass filters
Crescendo AttackMulti-turn progressive jailbreak
Flip AttackText reversal/denoising jailbreak
Language GameEncodes harmful prompts with transformations
BoN AttackBest-of-N augmented prompt search
Humor AttackHarmful requests hidden in playful context
Emoji AttackHarmful token substitution with emojis
All algorithms payload
"algorithms": [
"DarkCite",
"Bijection Learning",
"Crescendo Attack",
"Flip Attack",
"Language Game",
"BoN Attack",
"Humor Attack",
"Emoji Attack"
]

Request body (TOML)

Content-Type: application/toml.

Send TOML as the raw request body. scan_name must be present in TOML (top-level or under [scan]).

See detailed keys and examples in TOML Configuration Reference.

Download a ready-to-use template: virtuered-scan-template.toml

curl -X POST "$BASE_URL/v1/scan/start" \
-H "X-API-Key: $VIRTUERED_API_KEY" \
-H "Content-Type: application/toml" \
--data-binary @/path/to/scan-config.toml

Example request

curl -X POST "$BASE_URL/v1/scan/start" \
-H "X-API-Key: $VIRTUERED_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"scan_name": "my-safety-scan",
"model": {
"base_url": "https://api.openai.com/v1",
"api_key": "sk-...",
"model_name": "gpt-4o",
"temperature": 0.7,
"max_tokens": 2048
},
"datasets": [{"name": "Healthcare Risks"}],
"input_modalities": ["text"],
"output_modalities": ["text"],
"qpm": 60,
"concurrency": 16
}'

Example: Custom endpoint with extra headers

Use model.extra_headers when your upstream endpoint requires additional request headers.

curl -X POST "https://<your-base-url>/v1/scan/start" \
-H "X-API-Key: $VIRTUERED_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"scan_name": "custom-endpoint-scan",
"model": {
"base_url": "https://your-inference-api.example.com/openai/v1",
"api_key": "your-model-api-key",
"model_name": "meta-llama/Llama-3.1-8B-Instruct",
"temperature": 1.0,
"max_tokens": 256,
"extra_headers": {
"setup": "lora-adapter",
"X-Request-Context": "{\"service-name\": \"my-service\"}"
}
},
"datasets": [
{"name": "Bias"}
],
"input_modalities": ["text"],
"output_modalities": ["text"],
"qpm": 60,
"concurrency": 16
}'

Example response

{
"scan_id": "scan-12345",
"status": 200
}
FieldTypeDescription
scan_idstringUnique scan identifier
statusnumberHTTP status code (200 on success)

Start a Sandbox Scan

POST /v1/sandbox/scan/start

Launch a quick validation scan with pre-configured safety tests. Use this to verify your model connection before running a full scan.

Minimum requirements

Only scan_name and model are required. Inside model, include base_url, api_key, and model_name.

Custom header note: Sandbox scans support model.extra_headers for provider-specific upstream headers.

Example request

curl -X POST "$BASE_URL/v1/sandbox/scan/start" \
-H "X-API-Key: $VIRTUERED_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"scan_name": "my-sandbox-test",
"model": {
"base_url": "https://api.example.com/v1",
"api_key": "sk-...",
"model_name": "my-model",
"extra_headers": {
"X-Provider-Routing": "sandbox"
}
}
}'

Example response

{
"scan_id": "scan-12345",
"status": 200
}

Check Scan Progress

POST /v1/scan/progress

Returns progress for a specific scan, or for all scans the authenticated user can access (both ongoing and finished).

  • Without scan_id — Returns all scans, sorted by created_at ascending. Use the optional since filter to narrow results by creation time.
  • With scan_id — Returns progress for that specific scan.

Request body

FieldTypeRequiredDescription
scan_idstringNoScan identifier. If omitted, returns all scans
sincestringNoISO 8601 datetime (e.g., 2024-01-15T00:00:00Z). When scan_id is omitted, excludes scans created before this time

Example request (single scan)

curl -X POST "$BASE_URL/v1/scan/progress" \
-H "X-API-Key: $VIRTUERED_API_KEY" \
-H "Content-Type: application/json" \
-d '{"scan_id": "scan-12345"}'

Example request (all scans, optional since)

# All scans
curl -X POST "$BASE_URL/v1/scan/progress" \
-H "X-API-Key: $VIRTUERED_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'

# All scans since a given time
curl -X POST "$BASE_URL/v1/scan/progress" \
-H "X-API-Key: $VIRTUERED_API_KEY" \
-H "Content-Type: application/json" \
-d '{"since": "2024-01-15T00:00:00Z"}'

Example response (single scan)

{
"scan_id": "scan-12345",
"scan_time": "2024-01-15T10:30:00Z",
"created_at": "2024-01-15T10:30:00Z",
"status": "Scanning",
"passed_line": 91,
"remaining_time": 120,
"percentage": 45.5
}

Example response (all scans)

Returns an array of the same shape, sorted by created_at ascending:

[
{
"scan_id": "scan-111",
"scan_time": "2024-01-14T08:00:00Z",
"created_at": "2024-01-14T08:00:00Z",
"status": "Finished",
"passed_line": 200,
"remaining_time": 0,
"percentage": 100
},
{
"scan_id": "scan-12345",
"scan_time": "2024-01-15T10:30:00Z",
"created_at": "2024-01-15T10:30:00Z",
"status": "Scanning",
"passed_line": 91,
"remaining_time": 120,
"percentage": 45.5
}
]

Response fields

FieldTypeDescription
scan_idstringScan identifier
scan_timestringScan start timestamp (ISO 8601)
created_atstringWhen the scan was created (ISO 8601)
statusstringLifecycle status (Initializing, Scanning, Paused, Finished, Failed, Error)
passed_linenumberNumber of completed test cases
remaining_timenumberEstimated seconds remaining
percentagenumberCompletion percentage (0100)

Get Scan Summary

POST /v1/scan/summary

Retrieves aggregate scores, failure examples, and risk-level metadata for a completed scan.

Note: The scan must have a Finished status; otherwise returns HTTP 400.

Request body

FieldTypeRequiredDescription
scan_idstringYesScan identifier

Example request

curl -X POST "$BASE_URL/v1/scan/summary" \
-H "X-API-Key: $VIRTUERED_API_KEY" \
-H "Content-Type: application/json" \
-d '{"scan_id": "scan-12345"}'

Key response fields

FieldTypeDescription
averagesobjectAverage score per dataset
averages_subobjectAverage score per dataset subcategory
averages_attackobjectAverage score by attack type
failuresobjectFailed test cases grouped by dataset
scoresobjectCounts by risk category
risk_levelstringOverall risk level (Low, Medium, High)
avg_scorenumberOverall average safety score

Download Scan Results

POST /v1/scan/download

Downloads the complete scan artifacts as a ZIP archive.

Note: The scan must have a Finished status; otherwise returns HTTP 400.

Request body

FieldTypeRequiredDescription
scan_idstringYesScan identifier

Example request

curl -X POST "$BASE_URL/v1/scan/download" \
-H "X-API-Key: $VIRTUERED_API_KEY" \
-H "Content-Type: application/json" \
-d '{"scan_id": "scan-12345"}' \
-o scan-results.zip

Archive contents

FileDescription
results.jsonlPer-case test outputs
summary.jsonAggregated summary
report.pdfFormatted report

Pause a Scan

POST /v1/scan/pause

Pauses a running scan.

Only scans in Scanning or Initializing status can be paused.

Request body

FieldTypeRequiredDescription
scan_idstringYesScan identifier

Example request

curl -X POST "$BASE_URL/v1/scan/pause" \
-H "X-API-Key: $VIRTUERED_API_KEY" \
-H "Content-Type: application/json" \
-d '{"scan_id": "scan-12345"}'

Response

{
"status": "paused",
"scan_id": "scan-12345"
}

Retry a Scan

POST /v1/scan/retry

Resumes an interrupted scan from its last checkpoint.

Retry-eligible statuses

  • Failed
  • Error
  • Paused

Scans in Finished, Scanning, or Initializing status cannot be retried.

Request body

FieldTypeRequiredDescription
scan_idstringYesScan identifier

Example request

curl -X POST "$BASE_URL/v1/scan/retry" \
-H "X-API-Key: $VIRTUERED_API_KEY" \
-H "Content-Type: application/json" \
-d '{"scan_id": "scan-12345"}'

Response

{
"scan_id": "scan-12345",
"status": 200
}

Get Scan Usage

POST /v1/scan/usage

Returns the number of scans consumed by the authenticated user within a specified time window. Use this endpoint to monitor usage against your plan quota. Only finished, non-sandbox scans contribute to the count — in-progress, paused, failed, and sandbox scans are excluded.

Note: The count returned by this endpoint is for informational purposes only. Actual billed amounts are determined by the official invoice issued at the close of each billing cycle.

Request body

FieldTypeRequiredDescription
modestringYesOne of: current_billing_cycle, all_time, custom
startstringNoISO 8601 datetime. For custom mode, at least one of start or end is required
endstringNoISO 8601 datetime. When both start and end are provided, end must be ≥ start

Modes

  • current_billing_cycle — Counts scans in the current calendar month (UTC). period reflects the billing window.
  • all_time — Counts all scans. period spans from the first scan to the last.
  • custom — Counts scans within [start, end]. At least one of start or end is required; the other defaults to the first or last scan as applicable.

Example request

# Current billing cycle
curl -X POST "$BASE_URL/v1/scan/usage" \
-H "X-API-Key: $VIRTUERED_API_KEY" \
-H "Content-Type: application/json" \
-d '{"mode": "current_billing_cycle"}'

# All time
curl -X POST "$BASE_URL/v1/scan/usage" \
-H "X-API-Key: $VIRTUERED_API_KEY" \
-H "Content-Type: application/json" \
-d '{"mode": "all_time"}'

# Custom range
curl -X POST "$BASE_URL/v1/scan/usage" \
-H "X-API-Key: $VIRTUERED_API_KEY" \
-H "Content-Type: application/json" \
-d '{"mode": "custom", "start": "2024-01-01T00:00:00Z", "end": "2024-01-31T23:59:59Z"}'

Example response

{
"user_id": "user-abc",
"mode": "current_billing_cycle",
"period": {
"start": "2024-01-01T00:00:00.000000Z",
"end": "2024-01-31T23:59:59.999999Z"
},
"total_scans": 12,
"as_of": "2024-01-18T14:30:00.123456Z"
}

Response fields

FieldTypeDescription
user_idstringUser identifier
modestringRequested mode
periodobject or null{ "start": "<ISO 8601>", "end": "<ISO 8601>" }; null when there are no scans
total_scansnumberNumber of finished, non-sandbox scans in the period
as_ofstringISO 8601 timestamp indicating when this usage snapshot was taken

TOML Configuration Reference

/v1/scan/start and /v1/sandbox/scan/start accept TOML via Content-Type: application/toml.

Download a ready-to-use template: virtuered-scan-template.toml

Required keys

KeyLocationDescription
scan_nametop-level or [scan]Unique scan name
base_url[model]OpenAI-compatible base URL
api_key[model]Model provider API key
model_name[model]Model identifier

Optional keys

KeyLocationDescription
temperature[model]Sampling temperature
max_tokens[model]Maximum output tokens
extra_headers[model.extra_headers]Additional upstream HTTP headers
datasets[scan]Dataset configurations
algorithms[scan]Additional attack algorithms
input_modalities[scan]Input modalities
output_modalities[scan]Output modalities
qpm[scan]Queries per minute limit
concurrency[scan]Concurrent request limit
is_user_model[scan]Whether the model is user-provided
application_id[scan]Application identifier
continue_flag[scan]Resume from checkpoint

Example TOML

[model]
base_url = "https://api.openai.com/v1"
api_key = "sk-..."
model_name = "gpt-4o"
temperature = 0.7
max_tokens = 2048

[model.extra_headers]
setup = "lora-adapter"
X-Request-Context = '{"service-name": "my-service"}'

[scan]
scan_name = "toml-config-scan"
datasets = [
{name = "Healthcare Risks"},
{name = "Bias"}
]
input_modalities = ["text"]
output_modalities = ["text"]
qpm = 10
concurrency = 2

Error Handling and Troubleshooting

Standard status codes

Status CodeDescription
200Success
400Bad request, invalid state, or operation attempted before scan completion
401Missing or invalid API key
404Unknown scan ID
500Internal server error

Common issues

  • Missing X-API-Key header
  • Missing required fields (scan_name, model, scan_id)
  • Invalid state transitions (e.g., pausing a Finished scan)
  • Calling summary or download before the scan has completed

Debug checklist

  1. Confirm request headers (X-API-Key, Content-Type)
  2. Confirm scan status via POST /v1/scan/progress
  3. Verify the target model endpoint is healthy and reachable
  4. Verify the requested lifecycle transition is valid
  5. Retry from checkpoint for Paused, Failed, or Error scans

Support

For API access, tenant provisioning, or platform support, contact VirtueAI support.