Skip to main content

GraphQL API

Call the Vulnara GraphQL API with a bearer token and the X-Tenant header, page and filter lists, handle errors, and subscribe to live events.

Everything the Vulnara web app does goes through one GraphQL API. You can call it yourself to automate scans, pull findings into other tools, or build your own reports. The CLI, the GitHub Action and the MCP server all use the same API.

What it is for

  • Integrating Vulnara with your own tooling, dashboards and pipelines.
  • Doing in bulk what the web app does one item at a time.
  • Receiving scan progress and notifications live over a WebSocket.

How it works

There is one endpoint, https://vulnara-gw.rso.dev/graphql. It takes queries and mutations as JSON over HTTP POST, and subscriptions over a WebSocket on the same path. Introspection is on, so any GraphQL client or IDE can load the schema.

Every request carries two headers:

  • Authorization: Bearer <access token>.
  • X-Tenant: the id of the workspace the request is about.

Vulnara checks your role in that workspace before it runs each field.

What you can set

On every list query, the list argument takes the same fields:

  • limit: how many items to return. The default is 10, the maximum is 500.
  • skip: how many items to skip, for paging. The default is 0.
  • filters: a list of { field, ... } filters. Use stringEquals or idEquals for an exact match, and min or max for a range.
  • sort: the field to sort by. The default is createdAt.
  • order: ASC or DESC. The default is DESC.
  • search: free text to search for.

A list returns total, the number of items that match the filters, and items, the current page.

graphql
query Findings($scanResultId: ID!) {
  scanFindings(
    list: {
      limit: 50
      skip: 0
      filters: [{ field: "scanResultId", idEquals: $scanResultId }]
      sort: "severity"
      order: DESC
    }
  ) {
    total
    items { id severity file line }
  }
}

Sorting findings by severity ranks them Critical, High, Medium, Low, not alphabetically.

Do it

  1. Get an access token

    Use one of these:

    • A service account, for scripts and CI. Create one (see Service accounts), then exchange its name and token for an access token at the OAuth token endpoint, with the client_credentials grant, client_id set to hl04e6MSMRY60LdpGh5rdMRQjkPxvldAYoqXdzo4, username set to the service account name, password set to its token, and scope=profile. The endpoint is the token-url default on the GitHub Action reference. The response carries access_token and expires_in.
    • Your own sign-in, for trying things out. Run vulnara login with the CLI. It stores your access token in ~/.config/vulnara/jwt/access_token.
  2. Find your workspace id

    Query myUser. Its tenants field lists the workspaces you belong to, with their ids.

    graphql
    query { myUser { tenants { id } } }
  3. Send a request

    sh
    curl https://vulnara-gw.rso.dev/graphql \
      -H "Content-Type: application/json" \
      -H "Authorization: Bearer $VULNARA_TOKEN" \
      -H "X-Tenant: my-workspace" \
      -d '{"query":"{ repositories(list: { limit: 5 }) { total items { id repositoryName } } }"}'

Every query, mutation and type, with examples, is on the GraphQL reference.

The X-Tenant header

X-Tenant must name a workspace you belong to. If it names one you do not belong to, the request is refused. If you leave it out, Vulnara uses the first workspace in your list, so always send it when you belong to more than one.

Roles guard every field

Each workspace member holds a role: VIEWER, EDITOR or ADMIN. A higher role includes the lower ones.

  • VIEWER reads: repositories, scans, findings, the dashboard and statistics.
  • EDITOR also changes things: adds and deletes repositories, networks and git workspaces, starts and cancels scans, manages schedules, alert rules and triage decisions.
  • ADMIN also manages credentials, people and money: git tokens, service accounts, members, invitations, roles, transfers between workspaces, plans and payment methods.

A field your role does not allow returns an AUTHORIZATION_ERROR and does nothing. Service accounts hold the VIEWER role. See Teams and roles.

Errors

Errors come back in the standard GraphQL errors array. Each carries a stable code in extensions.code:

  • AUTHENTICATION_ERROR: no token, an invalid or expired token, or an X-Tenant you do not belong to.
  • AUTHORIZATION_ERROR: your role does not allow this field.
  • NOT_FOUND: the id does not exist in this workspace.
  • VALIDATION_ERROR: the input broke a rule. extensions.fields lists each field and why, for example TOO_SHORT, INVALID_PATTERN or DUPLICATE_ENTRY.
  • LIMIT_EXCEEDED: you hit a rate limit.
  • NEW_TENANT_INVALID: a transfer named the workspace the resource is already in.
  • INTERNAL_ERROR: something failed on Vulnara's side.

Some errors carry a more specific code, such as REPOSITORY_LIMIT_EXCEEDED when your plan's repository cap stops you, or GIT_TOKEN_NOT_VALID. Handle unknown codes as generic failures.

Rate limits

checkGitToken and gitTokenExpiration are limited to 20 calls a minute per user, with a burst of 5. Over the limit they return LIMIT_EXCEEDED.

Subscriptions

Open a WebSocket to wss://vulnara-gw.rso.dev/graphql using the graphql-ws protocol. Pass the same two values as connection parameters: Authorization with your bearer token, and X-Tenant.

  • taskEvents: live progress of scans and imports in the workspace. Each event has a type (STATE_UPDATED or TASK_CANCELLED), a taskId and the task's current state.
  • notifications: new in-app notifications for the workspace as they are created. See Notifications.
graphql
subscription { taskEvents { type taskId } }

You only receive events for the workspace you connected with. To follow another workspace, open another connection.

Good to know

  • Send JSON: POST requests need Content-Type: application/json. Requests that look like a plain browser form are refused.
  • Batching works: you can send several operations as a JSON array in one request.
  • Secrets are write-only: git token values and webhook header values are never returned after you set them.
  • Some reads are role-dependent: member email addresses are hidden from viewers, except their own.
  • Plan limits apply: creating repositories or networks, enabling repositories and starting scans can be refused when your plan's limit is reached. See Plans, usage and billing.
  • Token lifetime: service account tokens expire. Read expires_in and exchange again before it runs out.

Manage Your Cookie Preferences

We use cookies to enhance your experience. You can accept all cookies, decline non-essential cookies, or manage preferences below. Privacy Policy