Files
cve-dashboard/docs/api/ivanti-api-reference.md

8.4 KiB

Ivanti / RiskSense API Reference

Base URL: https://platform4.risksense.com/api/v1 Swagger: https://platform4.risksense.com/doc/swagger.json

Auth: x-api-key header. Error codes: 401 bad key, 419 insufficient privileges, 429 rate limited.

Endpoints Used

Search Workflow Batches

POST /client/{clientId}/workflowBatch/search
Content-Type: application/json

Standard JSON body with filters, projection, sort, page, size. Used by ivantiWorkflows.js for the daily sync.

Create False Positive Workflow

POST /client/{clientId}/workflowBatch/falsePositive/request
Content-Type: multipart/form-data

This endpoint does NOT accept JSON. It requires multipart/form-data with the following fields:

Field Type Required Notes
name string yes Workflow batch name (max 255)
reason string yes Reason for the FP determination
description string yes Description (can be empty string but field must be present)
expirationDate string yes ISO-8601 date, e.g. 2026-06-01
overrideControl string yes AUTHORIZED, NONE, or AUTOMATED. Use AUTHORIZED for standard FP workflows. NONE with isEmptyWorkflow=true is rejected (400).
isEmptyWorkflow boolean yes true if no findings attached, false otherwise
subjectFilterRequest string yes Stringified JSON (see format below)
files file no Attachments sent inline in the same request

subjectFilterRequest format

This is the critical field. It must be a stringified JSON object with this exact structure:

{
  "subject": "hostFinding",
  "filterRequest": {
    "filters": [
      {
        "field": "id",
        "exclusive": false,
        "operator": "IN",
        "value": "2283734550,2283734551"
      }
    ]
  }
}

Key details:

  • subject must be "hostFinding" — without this, the API returns 500
  • filters is nested inside filterRequest, NOT at the top level — {"filters":[]} at the top level returns 500
  • value for multiple IDs is comma-separated as a single string, not an array
  • operator values: EXACT, IN, LIKE, WILDCARD, RANGE, CIDR
  • For empty workflows, use {"subject":"hostFinding","filterRequest":{"filters":[]}} with isEmptyWorkflow=true

Response (200/202)

{
  "id": 33418832,
  "created": "2026-04-08T18:16:08"
}

Returns HTTP 200 or 202 (Accepted — async job creation). Response contains a numeric id (the workflow batch job ID) and created timestamp. No generatedId or uuid in this response.

Map Findings to Existing Workflow (tested 2026-04-13)

POST /client/{clientId}/workflowBatch/falsePositive/{workflowBatchUuid}/map
Content-Type: application/json

Maps additional host findings to an existing FP workflow batch. Used by the FP submission editing feature to add findings after initial creation.

Critical: one finding per call. The map endpoint only reliably maps one finding per request. Sending multiple finding IDs via the IN operator or comma-separated values results in only the first finding being mapped. The multipart/form-data format (used by the create endpoint) returns 500 on this endpoint.

Request body

{
  "subject": "hostFinding",
  "filterRequest": {
    "filters": [
      {
        "field": "id",
        "exclusive": false,
        "operator": "EXACT",
        "value": "2283734550"
      }
    ]
  }
}

Key details:

  • Must be application/json (NOT multipart/form-data — returns 500)
  • Use EXACT operator with a single finding ID per call
  • IN operator with comma-separated IDs only maps the first finding
  • Loop through findings and make one API call per finding
  • The workflowBatchUuid in the URL is the UUID from the search endpoint (not the numeric batch ID from create)

Response (200)

Returns the updated workflow batch object on success.

UUID resolution

The workflowBatchUuid required in the URL is NOT returned by the create endpoint. To obtain it:

  1. Search via POST /client/{clientId}/workflowBatch/search with { field: 'name', operator: 'EXACT', value: '<workflow_name>' }
  2. Use projection: 'internal' to get full batch objects
  3. The UUID is in the uuid field of the returned batch object
  4. Cache the UUID locally after first resolution (stored in ivanti_fp_submissions.ivanti_workflow_batch_uuid)

Implementation in dashboard

The resolveWorkflowBatchUuid() helper in backend/routes/ivantiFpWorkflow.js handles UUID resolution:

  • Returns cached UUID if available in the local submission record
  • Otherwise searches Ivanti by workflow name, extracts batch.uuid, and caches it for future use

The findings map loop in the POST /submissions/:id/findings endpoint:

  • Iterates through each finding ID individually
  • Makes one JSON POST per finding with EXACT operator
  • Tracks which findings succeeded vs failed
  • Only marks queue items as complete for successfully mapped findings
  • Returns both addedFindings and failedFindings arrays in the response

Other Workflow Endpoints (from Swagger)

These are available but not all are currently used by the dashboard:

Endpoint Purpose Status
/workflowBatch/acceptance/request Risk acceptance workflow Not used
/workflowBatch/remediation/request Remediation workflow Not used
/workflowBatch/severityChange/request Severity change workflow Not used
/workflowBatch/{workflowType}/approve Approve a workflow (needs workflowBatchUuid) Not used
/workflowBatch/{workflowType}/reject Reject a workflow Not used
/workflowBatch/{workflowType}/rework Send back for rework Not used
/workflowBatch/{workflowType}/update Update a workflow Not used
/workflowBatch/{workflowType}/{workflowBatchUuid}/map Map findings to workflow Used (FP editing)
/workflowBatch/{workflowType}/{workflowBatchUuid}/unmap Unmap findings Not used
/workflowBatch/{workflowType}/{workflowBatchUuid}/attach Attach file to existing workflow Broken — see note
/workflowBatch/{workflowType}/{workflowBatchUuid}/detach Detach file Not used
/workflowBatch/model Get model/schema Not used
/workflowBatch/filter Get available filter fields Not used
/workflowBatch/suggest Get suggested values for a filter field Not used

Known Limitations

Attach endpoint does not work (tested 2026-04-13)

The /workflowBatch/{workflowType}/{workflowBatchUuid}/attach endpoint is listed in the Swagger spec but returns HTTP 400 (Bad Request) for all tested request formats:

  • multipart/form-data with field name file (singular) — 400
  • multipart/form-data with field name files (plural) — 400
  • Tested with Content-Type: application/octet-stream and image/png — both 400
  • Tested with both ivantiMultipartPost and ivantiFormPost helpers — both 400

The Ivanti response is a generic Spring Boot error with no detail message:

{"timestamp":"...","status":400,"error":"Bad Request","path":"/api/v1/client/1550/workflowBatch/falsePositive/{uuid}/attach"}

Workaround: File attachments can only be uploaded during the initial workflow creation (sent inline with the /workflowBatch/falsePositive/request endpoint). To add attachments to an existing workflow, users must upload them directly in the Ivanti platform UI.

Search by numeric batch ID does not work

The /workflowBatch/search endpoint does not support filtering by the numeric id returned from the create endpoint. Searching with { field: 'id', operator: 'EXACT', value: '33432541' } returns 0 results. Searching by name field works and returns the workflow batch object including the uuid field needed for map/attach operations.

UUID not returned by create endpoint

The /workflowBatch/falsePositive/request create endpoint returns only { id: <number>, created: <timestamp> }. The uuid needed for map/attach/approve/reject operations must be obtained separately via the search endpoint.

Environment Variables

Variable Default Description
IVANTI_API_KEY Required. API key for authentication
IVANTI_CLIENT_ID 1550 Client ID in the Ivanti platform
IVANTI_SKIP_TLS false Set true to skip TLS verification
IVANTI_FIRST_NAME Used for workflow search filter (sync)
IVANTI_LAST_NAME Used for workflow search filter (sync)