Skip to content

Manage case files

This page complements the Cases GraphQL API guide. Use it for the file operations that support the case workflow.

To create a case or add evidence during its lifecycle, see Manage the case lifecycle.

Upload, retrieve, download, and delete files attached to a case.

What this covers

This guide shows how to upload a file through its presigned URL. It also shows how to check status, retrieve files, and delete a file.

Prerequisites

  • Create a service principal and get a bearer token. See the Cases GraphQL quickstart.
  • Get the full case ID. File uploads require an open case.
  • Keep the local file available so its size and content type match the upload request.

Workflow

Managing file attachments with the Cases GraphQL API consists of the following steps:

  1. Initialize an upload and save the returned file ID and presigned URL.
  2. Upload the file content to the presigned URL.
  3. Query the file until its status is UPLOADED.
  4. List, download, or delete the file as needed.

Initialize file upload

mutation startCaseFileUpload($input: StartCaseFileUploadInput!) {
    startCaseFileUpload(input: $input) {
        file {
            id
            name
            size
            status
        }
        presignedUrl
    }
}

Variables

{
    "input": {
        "caseId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
        "name": "investigation_report.pdf",
        "size": 1024000,
        "contentType": "application/pdf",
        "isEmbedded": false
    }
}

Response

{
    "data": {
        "startCaseFileUpload": {
            "file": {
                "id": "550e8400-e29b-41d4-a716-446655440500",
                "name": "investigation_report.pdf",
                "size": 1024000,
                "status": "SCHEDULED"
            },
            "presignedUrl": "https://files.example.com/upload-url..."
        }
    }
}

Upload the file before the presigned URL expires. Use the presignedUrl from the response.

Upload the file

curl --request PUT \
    --header "Content-Type: application/pdf" \
    --upload-file ./investigation_report.pdf \
    "<presigned-url>"

The content type and file size must match the values sent to startCaseFileUpload. The service updates the file status after it processes the object-storage event.

Delete file

mutation deleteCaseFile($input: DeleteCaseFileInput!) {
    deleteCaseFile(input: $input) {
        id
        name
        deletedAt
    }
}

Variables

{
    "input": {
        "fileId": "<file_id>"
    }
}

Response example

{
    "data": {
        "deleteCaseFile": {
            "id": "<file_id>",
            "name": "investigation_report.pdf",
            "deletedAt": "2024-08-14T16:05:00Z"
        }
    }
}

Get case files

List files attached to a case. Request downloadURL to get a signed URL for retrieving the file's contents.

query caseFiles($arguments: CaseFilesArguments!) {
    caseFiles(arguments: $arguments) {
        files {
            id
            name
            size
            status
            isEmbedded
            createdAt
            downloadURL
        }
        totalCount
    }
}

Variables

{
    "arguments": {
        "query": "investigationId = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'",
        "page": 1,
        "perPage": 20
    }
}

Response example

{
    "data": {
        "caseFiles": {
            "files": [
                {
                    "id": "550e8400-e29b-41d4-a716-446655440500",
                    "name": "investigation_report.pdf",
                    "size": 1024000,
                    "status": "UPLOADED",
                    "isEmbedded": false,
                    "createdAt": "2024-08-14T15:52:30Z",
                    "downloadURL": "https://files.example.com/signed-url..."
                }
            ],
            "totalCount": 1
        }
    }
}

Get a single file

Use caseFile to fetch one file by ID, for example, to refresh an expired downloadURL.

query caseFile($arguments: CaseFileArguments!) {
    caseFile(arguments: $arguments) {
        id
        name
        size
        status
        downloadURL
    }
}

Variables

{
    "arguments": {
        "fileId": "550e8400-e29b-41d4-a716-446655440500"
    }
}

Response example

{
    "data": {
        "caseFile": {
            "id": "550e8400-e29b-41d4-a716-446655440500",
            "name": "investigation_report.pdf",
            "size": 1024000,
            "status": "UPLOADED",
            "downloadURL": "https://files.example.com/download-url..."
        }
    }
}

Repeat this query until status is UPLOADED or ERROR_UPLOAD. Use downloadURL only after the status is UPLOADED.

Error handling

Check the HTTP status and the GraphQL response body. A request can return HTTP 200 with an errors array.

If the upload fails, check that the file size and content type match the initialization input. An expired presigned URL requires a new upload initialization.

Constraints and considerations

  • isEmbedded indicates whether the file is embedded in case content (for example, images in key findings). Embedded files don't generate audit log entries on download.
  • status is one of SCHEDULED (upload URL issued, file not yet uploaded), UPLOADED, ERROR_UPLOAD, or DELETED.
  • Download URLs are signed and valid for 15 minutes. Re-query caseFile or caseFiles with downloadURL selected to refresh the URL.
  • Requesting download URLs generates audit logs (except for embedded files).
  • caseFiles isn't scoped to a case by default — you must filter it with the query argument using QL. Match the case's id with investigationId = '<case id>', not caseId. Add ... and isEmbedded = false to exclude embedded files.