Security Center API
List vulnerability findings in the packages your services run, work through them, and read scan coverage, the security score and the settings.
The Security Center checks the npm packages your services run, and the Node.js version of each running instance, against the GitHub Advisory Database and Node.js CVEs from NVD. The SDK reports the packages; nothing needs a repository connection. See Find vulnerable packages you run for how it works in the app.
Availability: the Security Center is being rolled out; while it is off for an account every route here answers 404 with SECURITY_CENTER_NOT_ENABLED. Plan: included on every plan. Permissions: any member, or a user or administrator API token, can read findings, coverage and the score, and acknowledge, resolve or reopen findings. Accepting a risk and changing the settings need an owner or admin, or an administrator API token. A token with access scopes needs security:read to read and security:write for the rest.
The finding object
A finding is one vulnerable package version (or Node.js version) for one advisory, in one bucket and environment.
-
sid (string, required): The finding’s identifier (
sfnd_followed by 27 letters and digits). -
bucketSid (string, required): The bucket.
-
environmentSid (string | null): The environment, or
nullfor reports from a token without one. -
kind (string, required):
package(an npm package) ornode(a Node.js runtime CVE, matched against each instance’s own Node version). -
packageName (string, required): The package, or
node. -
version (string, required): The vulnerable version that runs.
-
advisoryId (string, required): The GitHub advisory id (
GHSA-...) for a package, or the CVE id for Node.js.cveIdslists the CVE ids andsummarythe advisory’s summary. -
severity (string, required):
critical,high,mediumorlow. -
kev (boolean, required): In CISA’s Known Exploited Vulnerabilities catalog: exploited in the wild.
-
loaded (string, required):
truewhen a running instance has loaded the package,falsewhen it is installed but not loaded,unknownfor ES modules (which Node does not list). -
direct (boolean, required): Listed in the app’s own
package.json. -
introducedBy (object): One finding only, for a package that runs: how it got there.
directwhen the app lists it itself, andchains, the shortest ways from a package the app lists to it, each a list of{ name, version }from the direct dependency to the vulnerable package ([email protected], then[email protected]), anduncertainwhen a package on a chain is installed at more than one version (dependencies are reported by name, so which version a parent pulls in cannot be told).treeholds the same ways merged on the packages they share: each node is{ name, version, children }, withlistedon a top node (true: the app lists it;false: no way reaches a package the app lists, so the top is a package nothing installed declares, in a monorepo one the repository root or another app lists),targeton the vulnerable package andmultipleVersionson a package installed at more than one version.truncatedwhen more ways of that length exist than the 8 shown. Read from the declared dependency names of what runs in the finding’s bucket and environment now. -
fixedVersion (string | null): The first version that fixes it, when the advisory names one.
-
status (string, required):
open,acknowledged,accepted_risk,fix_deployed,resolvedorexcluded. Fix deployed is set when no running instance reports the vulnerable version any more; a person then resolves it. Nothing resolves on its own, except a finding whose advisory was withdrawn (closedReasonis thenadvisory_withdrawn) or no longer covers a version that still runs (not_covered); if that changes back it returns on its own to the status it had, with its note and any accepted risk’s expiry, as an Excluded finding does. Excluded: the account leaves the package out of scanning. It is never resolved, is left out of the score, the open counts and the default list, takes no action, and when the package is let in again goes back to the status it had before, with the same note,statusByand, for an accepted risk, the sameacceptedUntil(openif that has passed meanwhile). The history shows both changes (excluded, thentracked_again). -
statusNote (string | null): The note or reason given with the last change, with
statusBy(the user) andstatusAt. -
acceptedUntil (string | null): For an accepted risk, when it returns to Open.
-
present (boolean, required): Whether a running instance reports the vulnerable version now.
-
services (object[], required): The services that run it:
serviceSid(orserviceKeybefore the service is known) andinstances.instanceCountis the total. -
production (boolean): Whether its environment counts as production.
-
priority (string[]): Why it sits where it does in the list:
malicious,kev,severity:,loaded,production,not_protected(the WAF does not protect against it, or only detects it). -
malicious (boolean): A package published with malicious code (a GitHub malware advisory). Always critical; its WAF coverage is
not_addressable. -
wafCoverage (string | object): When the plan includes WAF coverage (
securityWafCoverage): in a list, its state; for one finding, the coverage per policy (see Get a finding). States:protected,detecting(monitor mode),fixable(not protected, with the step),switched_off(ApiTraffic switched a covering rule off for everyone),no_policy,not_addressable(no WAF rule can help) andunknown(not known yet: the vulnerability has not been triaged for WAF coverage). -
firstSeen (string, required): When it was first found, with
lastSeen(when a running instance last reported it).
List findings
GET https://api.apitraffic.io/v1/accounts/{accountSid}/security/findings
Findings in priority order: a malicious package first, then in KEV, severity, loaded, in production, not protected by the WAF, newest. Returns total and findings, and advisoriesLoaded: false while the platform’s GitHub advisory sync has not yet succeeded once, when npm findings cannot appear yet, so an empty list is not a clean one (left out when it cannot be read just now).
Query parameters
-
bucketSid (query, string): Only this bucket.
-
segmentSid (query, string): Only the buckets of this segment, in place of
bucketSid. See Segments. -
environmentSid (query, string): Only this environment.
-
status (query, string): Statuses, separated by commas:
open,acknowledged,accepted_risk,fix_deployed,resolved,excluded. Default:open,acknowledged,accepted_risk,fix_deployed. -
severity (query, string): Severities, separated by commas.
-
kind (query, string):
packageornode. -
kev (query, boolean): Only findings in KEV.
-
packageName (query, string): Only this package.
-
wafCoverage (query, string): WAF coverage states, separated by commas (needs WAF coverage in the plan).
-
limit (query, integer, default 50): 1 to 200.
-
offset (query, integer, default 0): Findings to skip.
Get a finding
GET https://api.apitraffic.io/v1/accounts/{accountSid}/security/findings/{findingSid}
The finding with advisory (source, cvssScore, cvssVector, cwes, references, publishedAt, withdrawnAt) and history: every status change, oldest first, each with action, fromStatus, toStatus, userSid (the person, or null for an API token or the Security Center), note, acceptedUntil and at. People’s actions are acknowledged, risk_accepted, resolved and reopened; the Security Center’s are opened, fix_deployed, version_returned (the vulnerable version runs again), tracked_again (one the Security Center had closed or excluded is tracked again: its advisory covers the version again, or the package is scanned again), risk_expired, withdrawn, not_covered and excluded.
With WAF coverage in the plan, wafCoverage is an object: state; cves, each CVE with its triage verdict (covered, new_rule, not_waf_addressable, or null when not known yet), rules, since (the first ruleset revision with them) and reason; rules, every rule that covers it; policy, the policy the SDK runs here (the most specific one); policies, every policy that applies, each with sid, name, mode, runs, state and its rules (ruleId, state, reason and fix for each); and fixes, the steps that would protect it in the policy that runs.
Attack attempts
GET https://api.apitraffic.io/v1/accounts/{accountSid}/security/findings/{findingSid}/attack-attempts
Matches of the rules that cover a finding on requests to its bucket and environment, from the WAF analytics: windows for the last 7 and 30 days, each with matched, blocked, wouldBlock (by a policy in monitor mode), tagged, lastSeen and the same per rule in rules. A request two covering rules matched counts for each. rules is empty when no rule is known to cover it; available is false when the analytics could not be read just now. A token with access scopes needs waf:read too. The plan gate is securityWafCoverage.
Coverage updates
GET https://api.apitraffic.io/v1/accounts/{accountSid}/security/coverage-updates
Takes bucketSid, environmentSid and limit (1 to 200, default 50). Returns total and items, newest first, each with kind, id and at:
ruleset_released:rulesetId,version, how many rules itadded,patchedanddeprecated, thecvesits rules cover, andpolicies(withIt: policies tracking latest or pinned to it or later;pinnedBelow).cve_covered:cve,severity,kev,verdict,rulesetId,since,rules, andfindings(your active findings for it:countand up to fiveexamples).rule_switched_offandrule_switched_on:ruleId,rulesetId,rulesetVersions(null: every revision), thecvesit covers and yourfindingsfor them; a switch-off says whether it is stillactive. Only rules one of your policies runs.
The plan gate is securityWafCoverage.
Work through findings
POST https://api.apitraffic.io/v1/accounts/{accountSid}/security/findings/acknowledge
POST https://api.apitraffic.io/v1/accounts/{accountSid}/security/findings/accept-risk
POST https://api.apitraffic.io/v1/accounts/{accountSid}/security/findings/resolve
POST https://api.apitraffic.io/v1/accounts/{accountSid}/security/findings/reopen
Each takes one finding or up to 100 at once, and checks each on its own. The reply lists changed (sid, from, to) and refused (sid, reason: not_found or invalid_status, with the finding’s status).
| Action | From | Body |
|---|---|---|
| Acknowledge | Open | findingSids, optional note |
| Accept risk (admins) | Open, Acknowledged, Accepted risk | findingSids, reason (required), optional expiresAt (90 days by default, at most a year) |
| Resolve | any status but Resolved | findingSids, optional note |
| Reopen | Acknowledged, Accepted risk, Fix deployed, Resolved | findingSids, optional note |
An accepted risk returns to Open when it expires. A finding whose vulnerable version comes back after it went reopens on its own, with its history kept.
curl -X POST https://api.apitraffic.io/v1/accounts/acc_.../security/findings/accept-risk \
-H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" \
-d '{"findingSids": ["sfnd_..."], "reason": "Only reachable inside the VPN", "expiresAt": "2027-03-01T00:00:00Z"}'
Overview and scan coverage
GET https://api.apitraffic.io/v1/accounts/{accountSid}/security/overview
Takes bucketSid (or segmentSid) and environmentSid. Returns:
findings:open(active findings by severity, withtotal),kev,loaded,malicious(malicious packages still active), andbyStatus; with WAF coverage in the plan,waf: active findings by coverage state (byState), how many the WAF does not protect (notProtected) and how many are not known yet (unknown).coverage:scanningEnabled,totals, andbuckets, each with itsservices:serviceSid,runninginstances, how many report acompleteinventory, apartialone or abundledapp the SDK cannot read, how many of the complete ones are bundles scanned from a CLI upload (cli),notScanned, and thereason:switched_off,old_sdk(an SDK that cannot report packages; its versions inoldSdkVersions),not_reporting(an SDK that can, reporting nothing: checkAPI_TRAFFIC_DEPENDENCY_INVENTORYand the settings),bundled(withcliUploaded: the service has CLI uploads, none of them for the release that runs; andreleaseReported: false when its servers report no commit or version, so no upload can be matched to them),partial, orcli_stale(a CLI upload whose list predates a scope let back in: upload again).sync: when GitHub advisories (githubAt) and NVD (nvdAt) were last synced, andadvisoriesLoaded:falseuntil the GitHub advisory sync has succeeded once (package vulnerability data has not loaded yet, and findings for npm packages appear after the first sync),nullwhen that cannot be read just now.
Security score
GET https://api.apitraffic.io/v1/accounts/{accountSid}/security/score
Takes bucketSid (or segmentSid) and environmentSid. The score from 0 to 100 now, with grade (A 90 and up, B 80, C 70, D 60, F below), categories (each with weight, score, deducted, assessed and the items that cost points, each with its cost and link), cap (when incomplete scanning caps the score: limit, reason and the points it takes), biggestWins (the three items that would raise it most, with the gain), coverage and change against 7 and 30 days ago. The categories always add up to the score.
Vulnerabilities (40 points: each open finding costs 10, 5, 2 or 0.5 by severity, doubled in KEV, halved when not loaded, halved outside production, halved when the WAF protects against it in block mode (scoring version 3, with WAF coverage in the plan; each item’s factors.waf and wafCoverage say so); an accepted risk costs half, fix deployed and resolved nothing). WAF protection (25), SDK and runtime (10), Certificates (10) and Account access (15) are scored by the posture checks, each item at its rule’s cost; an item’s rule names it (waf.no_policy, account.mfa, …). Every category is assessed (scoring version 2; version 1 kept WAF protection, Certificates and Account access at their full weight), unless the plan leaves the checklist out (notInPlan) or a posture category could not be assessed (unassessed: sample or monitoring_off). The 7 and 30 day change compares only with scores of the same scoring version that assessed the same posture categories (so the checklist joining or leaving the plan, or certificate monitoring switched off, is not shown as a change). When the posture checks cannot be read, the request fails with SECURITY_SCORE_UNAVAILABLE (2977, status 503) rather than scoring without them. With the checklist in the plan, posture is the checklist as the posture endpoint returns it. Below 90% of instances reporting a complete inventory the score is capped at 80; with no inventory at all, at 60. Until the GitHub advisory sync has succeeded once, the score may leave npm findings out and is provisional (reason advisories_not_loaded, with a message): show it as provisional, not final. Otherwise provisional is null.
Posture
GET https://api.apitraffic.io/v1/accounts/{accountSid}/security/posture
Takes bucketSid (or segmentSid) and environmentSid. Returns checks, every posture check whether it passes or not: rule, category (waf, sdk, certificates or account), title (what passing means), status (pass, attention, or not_assessed with a reason: sample for the sample data, whose members and tokens are its own, or monitoring_off while certificate monitoring is switched off), cost (the points each item costs) and its items, each with id, label (what is wrong), why, fix, cost and link (where it is fixed: waf, waf_policy with sid, services, node with major, certificate with host and watchSid, account_security or tokens). summary counts the items, the passing checks and those notAssessed, and the points the items take off the score (each category’s total, at most its weight). Needs security:read. The plan gate is securityPosture.
GET https://api.apitraffic.io/v1/accounts/{accountSid}/security/score/history
The daily score, 90 days by default (days, 1 to 365), oldest first, with scoringChanges: the days the scoring itself changed.
Settings
GET https://api.apitraffic.io/v1/accounts/{accountSid}/security/settings
PATCH https://api.apitraffic.io/v1/accounts/{accountSid}/security/settings
dependencyScanningEnabled (on by default), excludedScopes (scopes such as @acme/* and package names the SDK leaves out), and environments: each environment’s isProduction, decided by its name (production, prod, live) unless set. PATCH takes any of dependencyScanningEnabled, excludedScopes and environments ([{ "sid": "...", "isProduction": true | false | null }], null going back to the name). Admins only. API_TRAFFIC_DEPENDENCY_INVENTORY=false switches reporting off for one process.
Segments
The findings list, the Overview, the score and the posture checklist take segmentSid in place of bucketSid: they then cover the buckets of that segment as it is when they run, members its label rule adds included. The Security Center reads whole buckets, so a bucket the segment narrows to some of its services counts in full. Each answer adds scope: the segment (sid, name), the buckets read, outsideAccess (how many of its buckets the credential cannot read, left out) and narrowed (how many it narrows to services). The score has no change against 7 and 30 days ago on a segment, and the daily history is kept per bucket and for the account, so score/history answers no days for one. Segments are not offered on the sample data.