diff --git a/docs/ivanti-api-reference.md b/docs/ivanti-api-reference.md new file mode 100644 index 0000000..509d13c --- /dev/null +++ b/docs/ivanti-api-reference.md @@ -0,0 +1,106 @@ +# 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: + +```json +{ + "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) + +```json +{ + "id": 33418832, + "created": "2026-04-08T18:16:08" +} +``` + +Returns a numeric `id` (the workflow batch job ID) and `created` timestamp. No `generatedId` or `uuid` in this response. + +### Other Workflow Endpoints (from Swagger) + +These are available but not currently used by the dashboard: + +| Endpoint | Purpose | +|----------|---------| +| `/workflowBatch/acceptance/request` | Risk acceptance workflow | +| `/workflowBatch/remediation/request` | Remediation workflow | +| `/workflowBatch/severityChange/request` | Severity change workflow | +| `/workflowBatch/{workflowType}/approve` | Approve a workflow (needs `workflowBatchUuid`) | +| `/workflowBatch/{workflowType}/reject` | Reject a workflow | +| `/workflowBatch/{workflowType}/rework` | Send back for rework | +| `/workflowBatch/{workflowType}/update` | Update a workflow | +| `/workflowBatch/{workflowType}/{workflowBatchUuid}/map` | Map findings to workflow | +| `/workflowBatch/{workflowType}/{workflowBatchUuid}/unmap` | Unmap findings | +| `/workflowBatch/{workflowType}/{workflowBatchUuid}/attach` | Attach file to existing workflow | +| `/workflowBatch/{workflowType}/{workflowBatchUuid}/detach` | Detach file | +| `/workflowBatch/model` | Get model/schema | +| `/workflowBatch/filter` | Get available filter fields | +| `/workflowBatch/suggest` | Get suggested values for a filter field | + +## 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) |