GraphQLGraphQL API

GraphQL API

The GraphQL endpoint, authentication, pagination and the operations available.

You will finish this page able to drive Argus from a script: authenticate, list and triage findings, trigger scans, export BOMs and SARIF, and evaluate a gate policy in CI.

Throughout, $ARGUS_URL is your Argus instance URL and argus_live_xxx is an API key from Settings → API keys.

export ARGUS_URL="https://your-argus-instance"
export ARGUS_API_KEY="argus_live_xxx"

Authentication

Argus exposes one GraphQL endpoint:

POST $ARGUS_URL/api/graphql

Authenticate with a bearer token:

Authorization: Bearer argus_live_xxx

The bearer may be an API key or a signed-in session token. Only the Bearer scheme is accepted. There is no x-api-key header and no query-parameter token on GraphQL.

An API key authenticates as the machine principal. It can create projects, trigger scans, triage findings, import BOMs and use AI features. It cannot delete projects or change settings, members, billing or keys. See Team and access.

Everything is scoped to the key's organization. There is no way to reach another organization's data.

A first call

curl -sS -X POST "$ARGUS_URL/api/graphql" \
  -H "Authorization: Bearer $ARGUS_API_KEY" \
  -H "content-type: application/json" \
  -d '{"query":"{ GetPostureSummary { totalOpen critical high medium low slaBreaches } }"}'
{"data":{"GetPostureSummary":{"totalOpen":349,"critical":7,"high":146,"medium":121,"low":75,"slaBreaches":142}}}

Conventions

Pagination is 1-based page plus limit. Responses echo total, and usually page and limit.

QueryDefault limit
listProjects, listVulnerabilities, ListDeps, ListBOMs20
listUsers, listScanJobs, ListAPIs, ListThirdParties, ListPolicies50
ListCreditLedger, GetAuditLog, listPullRequests, ListNotifications20 (capped at 100)
AttackPaths8

Dates are ISO 8601 strings. JSON is a custom scalar for free-form objects.

Removed in 2026-08

ListRules, CreateRule, UpdateRule and DeleteRule were removed from the schema. They served a seeded rule catalogue whose enabled flag no scan ever read, so a client calling UpdateRule received a success response while configuring nothing. Detection rules are gated by SetRuleOverride / DeleteRuleOverride, which the scan pipeline does consult, see Policies and gates. Calls to the removed fields now fail with a schema error rather than silently doing nothing.

Vocabularies are plain strings, not GraphQL enums:

ConceptValues
SeverityCRITICAL · HIGH · MEDIUM · LOW · INFO
Finding statusOpen · Resolved · false_positive · accepted_risk
Finding typeSAST · SCA · Secret · IaC · DAST
Scan depthstandard · deep_agentic
Scan job statusQueued · Running · Completed · Failed
Project statusPending · Scanning · Scanned · Failed
Pull request stateopen · closed · merged

Errors come back in the standard GraphQL errors array. These codes are surfaced in extensions.code and are safe to branch on:

CodeMeaning
UNAUTHORIZEDNo valid credential, or no longer a member of the organization
FORBIDDENYour role is not permitted to perform this action
INSUFFICIENT_CREDITSNot enough credits for this AI action
RULE_SCAN_LIMITMonthly rule-scan quota exhausted
BAD_INPUTInvalid arguments
RATE_LIMITEDSlow down. Carries a Retry-After

Other server errors are masked to a generic message.


Projects

{
  listProjects(page: 1, limit: 20, query: "api", status: "Scanned") {
    total page limit
    projects {
      id name repoUrl branch defaultBranch language status lastScan riskScore
      vulnerabilities { critical high medium low info total }
    }
  }
}
{ GetProject(id: "<project-id>") { id name repoUrl status riskScore lastScan } }

Import a connected GitHub repo (free, does not scan):

mutation { CreateProjectFromRepo(fullName: "owner/name") { id name status } }

Create from a Git URL:

mutation {
  CreateProject(name: "my-service", type: "api", repoUrl: "https://github.com/owner/repo", branch: "main") {
    id name status
  }
}

UpdateProject(id, name, branch) renames or re-targets a project. DeleteProject(id) requires Admin and is not available to API keys, a key authenticates as the machine principal, which does not hold project:delete. In the app it lives at Projects → open a project → Settings → Remove project, behind a type-the-name confirmation.

Deleting a project is irreversible and cascading: it removes the project's findings, scan history, BOMs, API inventory and dependency records. Your Git repository is untouched, only Argus's copy of what it found. There is no archive and no undo.


Scans

Trigger:

curl -sS -X POST "$ARGUS_URL/api/graphql" \
  -H "Authorization: Bearer $ARGUS_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "query": "mutation Scan($p: ID!, $d: String) { TriggerScan(projectId: $p, scanDepth: $d) { id status scanType } }",
    "variables": { "p": "<project-id>", "d": "standard" }
  }'

scanDepth is standard (3 credits) or deep_agentic (10 credits). Every scan also consumes one rule-scan slot.

Poll:

{
  listScanJobs(projectId: "<project-id>", limit: 10, status: ["Completed", "Running"]) {
    id status scanType findings branch commit triggeredBy startedAt finishedAt duration
  }
}

A job reports Queued → Running → Completed or Failed. GetProject(id:) reflects the same lifecycle in its status field.


Findings

{
  listVulnerabilities(
    page: 1
    limit: 50
    severity: ["CRITICAL", "HIGH"]
    status: ["Open"]
    type: ["SCA"]
    reachable: true
  ) {
    total page limit
    criticalCount highCount mediumCount lowCount
    vulnerabilities {
      id title severity status type
      cveId cvss epss kevKnownExploited
      priorityScore priorityBand
      reachable reachabilityEvidence
      package file line ruleId scanAgent projectName
      aiConfidence aiReasoning attackScenario
      introducedAt author assignee
    }
  }
}

All filter arguments: projectId, namespaceId, severity[], status[], type[], scanAgent[], query, from_date, to_date, falsePositive, committer, environment, prBranch, reachable, light.

Pass light: true for a slimmer payload when you only need the list, not the AI narrative fields.

Findings are returned in priority order.

{ GetVulnerability(id: "<finding-id>") { id title description remediation severity priorityScore reachable taintChain } }

Triage

mutation { UpdateVulnerabilityStatus(id: "<finding-id>", status: "false_positive") { id status falsePositive } }
mutation { BulkUpdateVulnerabilities(ids: ["<id1>", "<id2>"], status: "accepted_risk") { status message } }
mutation { AssignVulnerability(id: "<finding-id>", userId: "<user-id>") { id assignee } }

Create and assign in a connected tracker:

mutation {
  AssignToExternal(issueIds: ["<id1>"], provider: "linear", assigneeId: "<tracker-user-id>") {
    status message provider key url
  }
}

provider is jira or linear. On success, key and url identify the created ticket, and the URL is written back onto every finding in the selection. On failure, status is error with the provider's real message, and nothing is written.

Valid statuses: Open, Resolved, false_positive, accepted_risk. Setting false_positive also sets the false-positive flag; any other status clears it.

Re-verify with AI

mutation {
  ReverifyFindings(ids: ["<id1>", "<id2>"]) {
    status message reverified confirmed falsePositives uncertain
  }
}

2 credits per call, up to 25 findings.

Remediation

OperationWhat it doesCost
GenerateRemediationTask(vulnId)An agent-ready task brief in markdown and JSONFree
GenerateFixPatch(vulnId)A proposed diff with explanationAI credits
VerifyRemediation(vulnId)Re-checks whether the finding is still reportedFree
TriggerAgenticFix(vulnId)Clone → fix → verify → open a pull request15 credits
GetAgenticFixJob(id)Poll a fix job: status, prUrl, diff, verificationFree
mutation { TriggerAgenticFix(vulnId: "<finding-id>") { id status agent } }
{ GetAgenticFixJob(id: "<job-id>") { status prUrl error diff verification } }

status progresses to one of pr_opened, no_changes, verification_failed, failed.


Posture and reporting

{ GetPostureSummary { totalOpen critical high medium low slaBreaches slaCritical slaHigh } }
{ GetReachabilitySummary { declared reachable unreachable noiseReductionPct } }
{
  GetMTTRMetrics {
    overallAvgHours totalFixed totalOpen oldestOpenDays
    bySeverity { severity avgHours count slaTarget withinSla }
  }
}
{
  GetSLABreaches(limit: 50) {
    total critical high medium
    breaches { vulnId title severity projectName createdAt slaDeadline daysOverdue }
  }
}

Dashboard queries, GetDashboardKpis, GetDashboardTrend, GetDashboardTopRiskyRepos, GetDashboardSLABreaches, GetDashboardFrameworks, GetShiftLeftStats, all accept days (1-365).

{
  AttackPaths(projectId: "<project-id>", limit: 8) {
    id score band likelihood impact
    entry { id label exposure }
    crownJewel { id label kind }
    narrative steps inferred
  }
}

Attack-path edges are inferred rather than observed, and the response says so with inferred.


Dependencies, BOMs, VEX, SARIF

{
  ListDeps(page: 1, limit: 50, has_vulnerabilities: ["true"]) {
    total
    dependencies { id name version latestVersion ecosystem transitive reachable licenses vulnerabilityCount }
  }
}
{ DependencyFindings(dependencyId: "<dep-id>") { id title severity cveId } }
{ ListBOMs(projectId: "<project-id>", bom_type: "SBOM", page: 1, limit: 20) { total boms { id bomSubtype bomType components status createdAt } } }
{ ExportBOM(id: "<bom-id>", format: "cyclonedx-json") { filename mimeType content } }

Formats: cyclonedx-json, cyclonedx-xml, spdx-json, spdx-tv, csv.

{ GenerateVex(projectId: "<project-id>", format: "openvex") { filename mimeType content } }

Formats: openvex, cyclonedx-vex.

{ ExportSarif(projectId: "<project-id>") { filename mimeType content } }
mutation { ImportBOM(projectId: "<project-id>", content: "<document>", format: "cyclonedx", bomSubtype: "SBOM") { id components status } }
mutation { ImportVex(projectId: "<project-id>", content: "<document>") { statements matchedCves suppressed message } }
{ GetBOMDiff(bomId1: "<a>", bomId2: "<b>") { added removed updated unchanged components { name changeType oldVersion newVersion } } }
{ CheckProvenance(package: "sigstore", version: "3.0.0", ecosystem: "npm") { supported hasProvenance signed slsaLevel builderId sourceRepo summary } }

Policies

{ ListGatePolicies { id name description source } }

source is builtin or saved.

{
  EvaluatePolicy(projectId: "<project-id>", policyId: "block-kev-reachable") {
    policyName pass failCount warnCount evaluated
    violations { ruleId ruleName effect findingId severity cveId }
  }
}

PreviewPolicy(projectId:, policy: <JSON>) evaluates an unsaved policy. GetPolicyFacts(projectId:) returns the normalized facts a policy sees.

Gating a pipeline:

PASS=$(curl -sS -X POST "$ARGUS_URL/api/graphql" \
  -H "Authorization: Bearer $ARGUS_API_KEY" \
  -H "content-type: application/json" \
  -d '{"query":"query($p:ID!,$id:String!){EvaluatePolicy(projectId:$p,policyId:$id){pass failCount}}","variables":{"p":"'"$PROJECT_ID"'","id":"block-kev-reachable"}}' \
  | jq -r '.data.EvaluatePolicy.pass')

[ "$PASS" = "true" ] || { echo "Argus gate: blocked"; exit 1; }

Pull requests

{
  listPullRequests(page: 1, limit: 20, state: "merged", mergedWithUnresolvedOnly: true) {
    total
    pullRequests {
      repo number title url state authorLogin
      reviewEvent reviewJobStatus reviewFindingCount
      findingsRaised findingsResolved findingsRemaining
      mergedWithUnresolved mergedUnresolvedCount mergedSnapshotAt snapshotMissing
    }
  }
}
{ GetPullRequest(id: "<pr-id>") { pullRequest { repo number state } findings { id title severity unresolved } } }
mutation { ReviewPullRequest(repo: "owner/name", prNumber: 42) { status message } }
mutation { SyncPullRequests(projectId: "<project-id>", limit: 30) { status message } }

Billing

{ GetBilling { plan planName priceUsd creditBalance monthlyCredits ruleScansUsed ruleScansLimit periodStart periodEnd } }
{ ListPlans { id name priceUsd monthlyCredits ruleScansPerMonth features } }
{ ListCreditLedger(page: 1, limit: 50) { total entries { createdAt reason delta balanceAfter refType refId } } }

Organization and access

{ OrganizationInfo { id name plan members createdAt logoUrl } }
{ getCurrentUser { id email name role } }
{ MyOrganizations { orgId name role plan isActive } }
{ listUsers(page: 1, limit: 50) { id email name role status lastLogin } }
{ ListRoles { id name description assignable isMachine capabilities { id label granted } } }
{ GetOrgSettings { slaCriticalHours slaHighHours slaMediumHours slaLowHours riskWeightCritical riskWeightHigh retentionDays } }
{ GetAuditLog(page: 1, limit: 50) { total entries { createdAt actorEmail action capability outcome } } }
{ ListAPIKeys { id name key createdAt lastUsed status } }

ListAPIKeys returns masked prefixes. The full key is returned exactly once, by GenerateAPIKey(name:).

GetAuditLog requires the Admin role. An API key authenticates as the machine principal and will get FORBIDDEN:

{"errors":[{"message":"Your role (api) is not permitted to perform this action.",
  "extensions":{"code":"FORBIDDEN"}}]}

Read it with an Admin session instead. The same applies to ListInvites and ListOrgDomains.

Admin-only mutations include InviteUser, UpdateUserRole, DeleteUser, GenerateAPIKey, DeleteAPIKey, UpdateOrganization, UpdateOrgSettings, UpdateAIConfig, DeleteProject, the policy mutations, the rule-override mutations (SetRuleOverride, DeleteRuleOverride), and the domain mutations.


REST endpoints

Two endpoints return files rather than GraphQL payloads. Both accept Authorization: Bearer or a ?token= query parameter, which is useful for a browser download.

Project security report, HTML

curl -sS "$ARGUS_URL/api/report/<project-id>" \
  -H "Authorization: Bearer $ARGUS_API_KEY" \
  -o report.html

A complete standalone HTML report: project summary, latest scan, findings, dependencies, API endpoints, third parties and BOM data.

Compliance evidence pack, JSON

curl -sS "$ARGUS_URL/api/compliance/<project-id>?framework=NIST-SSDF&download=1" \
  -H "Authorization: Bearer $ARGUS_API_KEY" \
  -o evidence-pack.json

framework accepts CERT-In (default), EO-14028, NIST-SSDF, EU-CRA. Omit download=1 to receive the JSON inline. The same data is available over GraphQL as GenerateCompliancePack(projectId, framework).

A missing project and a project in another organization both return 404, Argus never reveals that a project exists elsewhere.


MCP endpoint

POST $ARGUS_URL/api/mcp

JSON-RPC 2.0 over Streamable HTTP, with the same bearer authentication. Twelve tools; full reference in Integrations.

List them:

curl -sS -X POST "$ARGUS_URL/api/mcp" \
  -H "Authorization: Bearer $ARGUS_API_KEY" \
  -H "content-type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Call one:

curl -sS -X POST "$ARGUS_URL/api/mcp" \
  -H "Authorization: Bearer $ARGUS_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "jsonrpc": "2.0", "id": 1, "method": "tools/call",
    "params": { "name": "scan_package", "arguments": { "name": "event-stream", "version": "3.3.6", "ecosystem": "npm" } }
  }'

Results carry both content[0].text (pretty-printed JSON) and structuredContent. A tool that fails returns isError: true in the result rather than a protocol error.

scan_diff and scan_package are limited to 30 calls per organization per minute.


Discovering the schema

Introspection is available, so any GraphQL client can generate types and give you autocomplete:

curl -sS -X POST "$ARGUS_URL/api/graphql" \
  -H "Authorization: Bearer $ARGUS_API_KEY" \
  -H "content-type: application/json" \
  -d '{"query":"{ __schema { queryType { fields { name } } } }"}'

There are 97 queries and 86 mutations. This page documents the ones you will reach for most.


Next