Skip to content

Manage the case lifecycle

What this covers

This guide explains the main workflow for creating and managing a case. It covers tenant-specific case values, case creation, updates, closure, and evidence.

Use the Cases GraphQL API reference for complete fields, arguments, and language samples.

Prerequisites

  • Create a service principal and get a bearer token. See the Cases GraphQL quickstart.
  • Know the tenant ID for the cases you want to manage.
  • Get evidence IDs before you attach existing detections, events, or assets.

Workflow

1. Resolve the case values

Case types, statuses, and verdicts depend on the tenant's licensed services. Don't hard-code their IDs or assume that every tenant has the same values.

Start with caseTypes. Then call casePrimaryStatuses as a separate request with the selected typeId. To close a case, call casePrimaryVerdicts with the selected typeId and closed primaryStatusId.

query CaseTypes($arguments: CaseTypesArguments!) {
    caseTypes(arguments: $arguments) {
        types {
            id
            name
            title
            managedBy
        }
    }
}
{
    "arguments": {}
}

Response example

{
    "data": {
        "caseTypes": {
            "types": [
                {
                    "id": "<type-id>",
                    "name": "investigation",
                    "title": "Investigation",
                    "managedBy": "CUSTOMER"
                }
            ]
        }
    }
}

Copy the selected types[].id into the status request as typeId.

Use the selected type ID in a separate status request:

query PrimaryStatuses($arguments: CasePrimaryStatusesArguments!) {
    casePrimaryStatuses(arguments: $arguments) {
        primaryStatuses {
            id
            name
            title
            isClosed
        }
    }
}
{
    "arguments": {
        "typeId": "<type-id>"
    }
}

Response example

{
    "data": {
        "casePrimaryStatuses": {
            "primaryStatuses": [
                {
                    "id": "<open-status-id>",
                    "name": "OPEN",
                    "title": "Open",
                    "isClosed": false
                },
                {
                    "id": "<closed-status-id>",
                    "name": "CLOSED",
                    "title": "Closed",
                    "isClosed": true
                }
            ]
        }
    }
}

Carry the selected open status ID into createCase. Copy the closed status ID into the close request in step five.

Use caseSecondaryStatuses and caseSecondaryVerdicts when your workflow uses secondary values. For a permitted transition, pass transitionFromId or caseId to the relevant query.

2. Create the case

Call createCase with the type, severity, title, and initial primary status from step one. Set severity to one of these integers:

Value Severity
2 Informational
4 Low
6 Medium
8 High
10 Critical
mutation CreateCase($input: CreateCaseInput!) {
    createCase(input: $input) {
        id
        shortId
        title
        severity
        primaryStatus {
            id
            name
            isClosed
        }
    }
}
{
    "input": {
        "typeId": "<type-id>",
        "severity": 6,
        "title": "<case-title>",
        "primaryStatusId": "<open-status-id>",
        "assigneeId": "<user-id-or-supported-mention>"
    }
}

Response example

{
    "data": {
        "createCase": {
            "id": "<case-id>",
            "shortId": "CSE00001",
            "title": "<case-title>",
            "severity": 6,
            "primaryStatus": {
                "id": "<open-status-id>",
                "name": "OPEN",
                "isClosed": false
            }
        }
    }
}

Carry data.createCase.id into the case, evidence, comment, file, link, and close requests.

The title can contain up to 256 characters. The service truncates longer titles.

The optional assigneeId can identify a user, client, or supported mention. Use the Users API to find user IDs for the tenant.

Assignment mentions are configured tokens. Don't use a group UUID or create a token from a group display name.

Role mentions are: @customer, @admin, @authorized_contacts, and @sophos. Availability depends on the tenant type, service level, and service agreement.

For details, see Manage case comments.

Your service partner can confirm which mentions are available. Omit assigneeId to leave a new case unassigned, or send an empty string.

Evidence is a detection, event, saved search, or host attached to a case. Evidence supplied during creation becomes genesis evidence, which records the evidence that opened the case. You can attach detection IDs, event IDs, saved searches, and host IDs in the input, including any combination that your workflow needs.

3. Retrieve and update the case

Use case to retrieve one case by its full ID. Use updateCase for partial updates. The id is required, and omitted fields keep their current values.

query GetCase($arguments: CaseArguments!) {
    case(arguments: $arguments) {
        id
        shortId
        title
        severity
        assigneeId
        assigneeSubject {
            id
        }
        primaryStatus {
            id
            name
            isClosed
        }
    }
}
{
    "arguments": {
        "id": "<case-id>"
    }
}

Response example

{
    "data": {
        "case": {
            "id": "<case-id>",
            "shortId": "CSE00001",
            "title": "<case-title>",
            "severity": 6,
            "assigneeId": "<user-id-or-supported-mention>",
            "assigneeSubject": {
                "id": "<user-id>"
            },
            "primaryStatus": {
                "id": "<open-status-id>",
                "name": "OPEN",
                "isClosed": false
            }
        }
    }
}

Use data.case.id as the case ID for the update and later requests.

For example, update the title, severity, or assignee without changing other fields:

mutation UpdateCase($input: UpdateCaseInput!) {
    updateCase(input: $input) {
        id
        title
        severity
        assigneeId
        updatedAt
    }
}
{
    "input": {
        "id": "<case-id>",
        "title": "<updated-case-title>",
        "severity": 8,
        "assigneeId": "<user-id-or-supported-mention>"
    }
}

Response example

{
    "data": {
        "updateCase": {
            "id": "<case-id>",
            "title": "<updated-case-title>",
            "severity": 8,
            "assigneeId": "<user-id-or-supported-mention>",
            "updatedAt": "2024-08-14T16:15:00Z"
        }
    }
}

Before you continue, make sure that data.updateCase.id matches the case ID you sent.

Query permitted next values again before a lifecycle transition. For example, pass the case ID to casePrimaryStatuses to account for the case's current type, entitlements, and caller permissions.

When you send tags, the new list replaces the existing list. Include the current tags if you want to keep them. You can't change the managedBy value after you set it.

For assigneeId, omit the field or send null to leave the assignment unchanged. Send an empty string to clear the assignment, or a non-empty value to set it. Changing the assignee while setting the status to AWAITING_ACTION can trigger a handoff and email notification to the new assignee.

incidentAdvisorId is separate from assigneeId and applies to provider-managed cases. The current schema exposes it in the create and update inputs, subject to tenant and provider permissions.

The incidentAdvisor field can be null if no advisor is set or the service can't resolve the advisor. A routing queue is also separate from the assignee and is restricted to partner and MDR provider users.

4. Add and verify evidence

Use addEvidenceToCase to attach evidence after creation. The operation is asynchronous, and the added evidence isn't genesis evidence. Use removeEvidenceFromCase to remove evidence.

mutation AddEvidence($input: AddEvidenceToCaseInput!) {
    addEvidenceToCase(input: $input) {
        caseId
        detectionIds
        eventIds
        hostIds
    }
}
{
    "input": {
        "caseId": "<case-id>",
        "detectionIds": ["<detection-id>"],
        "eventIds": ["<event-id>"],
        "hostIds": ["<host-id>"]
    }
}

Response example

{
    "data": {
        "addEvidenceToCase": {
            "caseId": "<case-id>",
            "detectionIds": ["<detection-id>"],
            "eventIds": ["<event-id>"],
            "hostIds": ["<host-id>"]
        }
    }
}

Carry data.addEvidenceToCase.caseId into the caseEvidence check. Wait for processing before you expect the evidence to appear.

The addEvidenceToCase and removeEvidenceFromCase operations can queue behind other evidence jobs for the same case. Check the case's processingStatus before you expect the change to appear. Then use caseEvidence to verify the attached evidence and its source IDs.

caseEvidence returns IDs, not records. To read the events and detections those IDs point at, see Get event details from cases and detections.

For comments, files, and links, use the focused guides in Related operations.

5. Close and archive the case

Before closing a case, query compatible verdicts with casePrimaryVerdicts. Pass the selected type and closed status so you don't submit an invalid verdict.

query CloseVerdicts($arguments: CasePrimaryVerdictsArguments!) {
    casePrimaryVerdicts(arguments: $arguments) {
        primaryVerdicts {
            id
            name
            title
        }
    }
}
{
    "arguments": {
        "typeId": "<type-id>",
        "primaryStatusId": "<closed-status-id>"
    }
}

Response example

{
    "data": {
        "casePrimaryVerdicts": {
            "primaryVerdicts": [
                {
                    "id": "<primary-verdict-id>",
                    "name": "TRUE_POSITIVE",
                    "title": "True positive"
                }
            ]
        }
    }
}

Carry the selected primaryVerdicts[].id into primaryVerdictId.

Close the case with updateCase. Set primaryStatusId to the closed status and provide the compatible verdict ID. You can also provide closeReason in the same request.

mutation CloseCase($input: UpdateCaseInput!) {
    updateCase(input: $input) {
        id
        primaryStatus {
            id
            name
            isClosed
        }
        primaryVerdict {
            id
            name
        }
        closeReason
    }
}
{
    "input": {
        "id": "<case-id>",
        "primaryStatusId": "<closed-status-id>",
        "primaryVerdictId": "<primary-verdict-id>",
        "closeReason": "<close-reason>"
    }
}

Response example

{
    "data": {
        "updateCase": {
            "id": "<case-id>",
            "primaryStatus": {
                "id": "<closed-status-id>",
                "name": "CLOSED",
                "isClosed": true
            },
            "primaryVerdict": {
                "id": "<primary-verdict-id>",
                "name": "TRUE_POSITIVE"
            },
            "closeReason": "<close-reason>"
        }
    }
}

Confirm data.updateCase.primaryStatus.isClosed is true before you archive the case.

Archive a closed case by setting isArchived to true. Archiving removes the case from active views without deleting its record. Set isArchived to false to unarchive it.

Error handling

Check both the HTTP status and the GraphQL response body. A query or mutation error can return HTTP 200 with an errors array.

Common causes include an ID that isn't available to the tenant, an invalid lifecycle transition, and a status or verdict that doesn't match the selected case type. Refresh the tenant-specific values before retrying a failed transition.

For transport errors, see Errors.

Constraints and considerations

  • Resolve case values at runtime. Licensed services determine which values are available.
  • Use the full case id for API calls. The human-readable shortId isn't the operation ID.
  • Use 2, 4, 6, 8, or 10 for case severity.
  • An assignee can be a user, client, or supported mention. Tenant permissions determine which values are valid.
  • A closed case needs a compatible primary verdict if the selected workflow requires one.
  • Evidence changes are asynchronous. Wait for processing to complete before checking the case.
  • Don't treat archive as deletion. Archived cases remain in the service and can be restored.