Phoenix Public API

This document describes the public API surface exposed to customers.

Base URL

/api/v1

Authentication

Public APIs accept:

Public APIs enforce tier-based response filtering for sensitive data.

Scoring formulas, weights, and internal rationale are not exposed in the public API.

Swagger

Public Swagger UI is available in debug/non-production environments only (returns 404 in production):

The public OpenAPI schema is built from a default-deny allow-list of public endpoint prefixes. Admin (/api/v1/admin/*), any path with an /admin segment, the Vuln Weekly pipeline (/api/v1/vw/*), the MPI admin backend (/api/v1/malware-intel/*), exploit-recreation actions, and all /internal/* PAI routes are excluded — a new router does not appear in the public docs until explicitly allow-listed. The internal API docs (/internal/docs, /internal/openapi.json, /internal/redoc) are also debug-only and 404 in production.

Tiered Access

Tier Access
FREEMinimal fields
REGISTEREDStandard fields
PROExpanded fields
ENTERPRISEFull fields (public plane only)

Core Endpoints

CVE Search & Intelligence

GET /api/v1/cves

The base collection route is the CVE search endpoint (there is no /cves/search path). Available to FREE (subject to the search daily quota); results are tier-filtered.

ParamTypeNotes
qstringCVE ID prefix or description substring
yearint (1999–2030)Filter by CVE year
severitystringCRITICAL / HIGH / MEDIUM / LOW
kev_onlyboolOnly CISA KEV CVEs
epss_minfloat (0–1)Minimum EPSS score
sortstringpublished[:desc|:asc] or cvss[:desc|:asc]; default year-descending
published_afterYYYY-MM-DDInclusive lower publish-date bound
published_beforeYYYY-MM-DDInclusive upper publish-date bound
has_analysisbooltrue → only Phoenix-analyzed CVEs; false → only un-analyzed
limitint (1–200)Page size (Data-Shield pagination applies)
offsetintPagination offset

Every result carries a has_analysis flag. Recipes:

Related FREE reads: GET /api/v1/cves/by-product?vendor=&product=, /cves/trending, /cves/kev, /cves/homepage/intel, /cves/stats/{year}. Curated "Phoenix Intelligence" is GET /api/v1/phoenix/high-profile (REGISTERED — auth required).

Gating v2 (enable_cve_search_gating_v2, default off): when enabled, by-product requires an authenticated identity (401 otherwise), and trending, kev, homepage/intel, and kev-public/combined-exploited consume the search daily quota.

Analysis Data

GET /api/v1/analysis/kev-public/combined-exploited

Returns the public combined exploited vulnerability catalog used by exploited-vulnerabilities.html. The backend reads web/data/kev_public/combined_exploited.json. Unavailable data returns 503.

CWE / OWASP Top 10 / Threat-Frequency Tiered Data

Note: Added 2026-07-10. Gated by feature flag ENABLE_CWE_OWASP_THREAT_TIERED_API (default false). When the flag is off, all five endpoints below behave exactly as they did before this feature — fully open, no tier shaping — so this is a safe no-op until enabled. This is a payload-shaping feature, not a hard gate: none of these endpoints return 403 for tier reasons, and every response is HTTP 200.
MethodPathSource dataset
GET/api/v1/analysis/cwecwe_analysis.json
GET/api/v1/analysis/cwe-top25cwe_top25_analysis.json
GET/api/v1/analysis/kev-cwekev_cwe_analysis.json
GET/api/v1/analysis/owasp-top10owasp_top10_analysis.json
GET/api/v1/analysis/reference-threat-frequenciessee note below — the data source itself is flag-gated, not just the shaping

Each has a full-fidelity PAI mirror under /internal/v1/analysis/* — see PAI_INTERNAL_API.html.

Tier behavior (flag ON)

CallerPayload
Anonymous / Free / Registered / Pro (JWT or API key) Shaped: CWE/OWASP category mappings, names, rankings, and MITRE links are unchanged (public reference data). Proprietary numeric fields — raw NVD/H1/KEV/VulnCheck/GitHub PoC counts, per-CWE and per-category statistics, threat_mapping counts, and yearly-trend raw numbers — are converted to percentage shares only. The raw counts and the totals used to derive a percentage are stripped together, so a caller cannot recompute the original count from the shaped response.
Enterprise (JWT) or global admin Full numeric payload: raw counts, baseline_ratio, cross_dataset_analysis, and full yearly trends.
PAI (X-PAI-Key, /internal/v1/analysis/*) Always full numeric payload — PAI access is itself the Enterprise gate; no additional tier parameter or header is needed.

Because there is no 403 on these base routes, the existing public pages that already call them (owasp-top10-analysis.html, cwe-top25-analysis.html, cwe.html, kev-cwe-analysis.html, threat-analytics.html, index.html) continue to work unmodified at every tier — an Enterprise upgrade only changes response richness, not endpoint availability.

reference-threat-frequencies — data source is also flag-gated

Unlike the other four endpoints (which only gate the shaping step against an always-available source file), /api/v1/analysis/reference-threat-frequencies also switches its underlying data source on the same flag:

For every distribution in this dataset (the baseline threat-type/threat-impact distributions, and each per-dataset threat_type_frequencies/threat_impact_frequencies), Pro-and-below callers receive %-only — count is never returned, and baseline_ratio/cross_dataset_analysis are stripped/emptied. Threat-type and threat-impact frequency of vulnerabilities is exposed as percentages only to any caller below Enterprise.

Premium Package Intelligence

POST /api/v1/packages/intel

Returns package-version CVE intelligence plus premium MPI malware intelligence for Pro and Enterprise Intelligence API keys.

Gated by feature flag enable_package_intel_api (default false). When the flag is off the endpoint returns 404 (not 403). Registered/basic keys return 403.

Successful calls are counted as REST API calls and as mpi_package_intel_lookups in MPI usage accounting.

Request

{
  "ecosystem": "Maven",
  "name": "com.h2database:h2",
  "version": "1.3.176"
}

Response fields

FieldNotes
cvesVersion-filtered OSV CVE/advisory records.
vulnerabilitiesAlias of cves.
malwarePremium MPI malware summary. Raw signals, IOCs, scan IDs, and internal evidence are not exposed on the public plane.
ps_oss_scoreDerived package risk score.
source_attributionSources used to build the response.

Token Hub BYOK Registration

/api/token-hub-registration

Gated by feature flag enable_token_hub_byok_registration (default false). When the flag is off these endpoints return 404 (not 403).

Tenant identity is derived server-side from the authenticated user's database tenant membership. Callers must not send customerId, orgId, or userId.

BYOK onboarding, key registration, key revocation, and key listing require an authenticated user with Token Hub organization membership. Token Hub upstream availability/auth failures return 503; invalid upstream responses return 502; revoke for a missing key returns 404.

MethodPathPurpose
POST/api/token-hub-registration/byok/onboardingRegister tenant BYOK in one Token Hub onboarding call.
POST/api/token-hub-registration/byok/api-keyRegister or replace a BYOK API key.
POST/api/token-hub-registration/byok/keys/{platform}/{vendor}/revokeRevoke an active BYOK key.
GET/api/token-hub-registration/byok/keysList BYOK key summaries; secretRef is never returned.

Accepted platform values: blue, purple, green. Accepted vendor values: openai, anthropic, gemini.

BYOK key list response

{
  "orgId": "bb610723-161c-4e63-807a-0ca0d207606f",
  "keys": [
    {
      "orgId": "bb610723-161c-4e63-807a-0ca0d207606f",
      "platform": "blue",
      "vendor": "anthropic",
      "displayName": "Anthropic Production Key",
      "apiKey": "sk-ant-...here"
    }
  ]
}

apiKey is a masked hint from Token Hub, not the raw secret. secretRef remains internal to Token Hub client DTOs and is stripped before returning public API responses.

Frontier Model Attribution (FMA)

GET /api/v1/ai-attributed-model-cves

Returns CVEs discovered, reported, or co-reported with the assistance of frontier AI models (Claude, GPT, Gemini, Llama, Grok) and AI-native security firms (AISLE).

Gated by feature flag enable_frontier_model_cve_intel (default false). When the flag is off the endpoint returns 404 (not 403) so the feature's existence is not leaked.

Auth is optional: Free tier is rate-limited; Pro+ unlocks bulk access.

Query parameters

ParamTypeDescription
providercsv stringFilter by provider(s): anthropic,openai,google,meta,xai,aisle
programcsv stringFilter by program: glasswing,quiltworks,madbugs
min_confidence_tierint (1–3)Default 1
min_cvssfloatDefault none
sinceISO dateFilter discovered_date >= since
cursorstringPagination cursor
limitintDefault 50, max 200

Tier-restricted fields are filtered through ResponseTransformer: confidence_tier and credit_text/collaboration_chain require Pro+, the numeric confidence_score requires Enterprise. The CSV variant GET /api/v1/ai-attributed-model-cves.csv returns 501 Not Implemented in v1.0.

SCF Seat-Based Licensing

Flag: enable_scf_seat_licensing (default true in the current rollout). These endpoints return 404 when the flag is off.

GET /api/v1/firewall/license

Returns the authenticated caller's organization SCF license, computed quota, and live usage.

Auth: Requires an authenticated user bound to an SCF organization.

{
  "license": { "org_key": "acme", "tier": "registered", "base_seats": 3, "purchased_seats": 0, "endpoints_per_seat": null, "calls_per_seat": null, "tenant_id": "..." },
  "quota":   { "tier": "registered", "total_seats": 3, "endpoints_per_seat": 3, "calls_per_seat": 1000, "endpoint_topup": 0, "pooled": false, "endpoint_quota": 0, "call_quota": 0, "per_seat_endpoint_quota": 3, "per_seat_call_quota": 1000 },
  "usage":   { "seats_used": 2, "endpoints_used": 7, "calls_used": 1234 }
}

POST /api/v1/firewall/license/seat-request

Submit a seat-purchase request. Global admins approve or reject the request; callers cannot allocate seats directly.

Request body: { "requested_seats": 10, "note": "optional" }. requested_seats must be greater than 0; note max 500 chars.

Response 200: Creates an scf_seat_request row with status: "pending".

Related: docs/Individual_Feature/2026-06-17-scf-seat-licensing.md

Note: Refer to Public Swagger for full endpoint definitions for the public surface only. Admin (/api/v1/admin/*) and PAI/internal (/internal/v1/*) endpoints are excluded from the public schema.

SCF Seat-Based Licensing

Flag: enable_scf_seat_licensing (default false). All endpoints below return 404 when off.

GET /api/v1/firewall/license

Returns the authenticated user's organization SCF license, computed quota, and live usage.

Auth: Requires authenticated user with an SCF organization.

Response 200:

{
  "license": { "org_key": "acme", "tier": "pro", "base_seats": 25, "purchased_seats": 0, "endpoints_per_seat": null, "calls_per_seat": null, "tenant_id": "..." },
  "quota":   { "tier": "pro", "total_seats": 25, "endpoints_per_seat": 10, "calls_per_seat": 50000, "pooled": false, "endpoint_quota": 0, "call_quota": 0, "per_seat_endpoint_quota": 10, "per_seat_call_quota": 50000 },
  "usage":   { "seats_used": 2, "endpoints_used": 7, "calls_used": 1234 }
}

POST /api/v1/firewall/license/seat-request

Submit a seat-purchase request (global admin approves; caller cannot allocate seats directly).

Request body: { "requested_seats": 10, "note": "optional" }requested_seats must be > 0, note max 500 chars.

Response 200: Created scf_seat_request row with status: "pending".

Related: docs/Individual_Feature/2026-06-17-scf-seat-licensing.md

MPI Bulk-Process Queue

Server-side sequential batch worker for draining Kanban columns. Admin-only (requires MPI pipeline enabled).

POST /api/v1/malware-intel/queue/bulk-process

Schedule a bulk job; returns immediately with a job_id.

FieldTypeDefaultDescription
kanban_statusstring"awaiting_review"Source column
max_packagesint50Pipeline cap 1 000; approve_malicious cap 100 000
modestring"pipeline"pipeline = analyst+judge; approve_malicious = confirm by score
min_scoreint|nullnullOnly include packages with heuristic_score ≥ min_score
batch_sizeint50Items per batch (approve_malicious mode)
pause_secondsfloat0.0Pause between batches
pipeline_stepsint21=analyst, 2=analyst+judge, 3=+verifier
retry_inconsistentbooltrueRe-run pair on analyst/judge disagreement
reasonstring""Operator note for audit log

400: invalid mode, max_packages exceeds cap, pipeline_steps not in {1,2,3}. 429: job already running.

GET /api/v1/malware-intel/queue/bulk-process/{job_id}/status

Poll job progress. Returns { job_id, status, processed, total, batch_index, batch_total, started_at, finished_at, errors, mode }. status: queued → running → complete | error.

Related: docs/Individual_Feature/2026-06-26-mpi-score-batch-confirm.md