# Attachments

Source: https://docs.pitchpointsolutions.com/developer_guides/findings/attachments/

**Beta**

## Overview

An attachment is a supporting document, such as a screenshot or a source PDF, that backs up a finding's review. You upload one, download it back, and remove it when it's no longer relevant. This guide covers all three, plus the shape and limits of each.

A finding only signals `hasAttachments` (a boolean). The metadata and download links come from listing the finding's attachments.

## Listing what's already attached

```bash
curl -X GET "https://api.pointservices.com/riskinsight-services-ws/resources/v1/findings/workflows/wfpop_0000000000002082611/findings/fndg_0000000000006051384/attachments" \
  -H "Authorization: Bearer your_access_token_here"
```

```json
{
  "workflowId": "wfpop_0000000000002082611",
  "findingId": "fndg_0000000000006051384",
  "attachments": [
    {
      "attachmentId": "jattmt_0000000000006099213",
      "name": "paystub-august.pdf",
      "contentType": "application/pdf",
      "modifiable": true,
      "updatedAt": "2026-08-07T15:01:12Z",
      "links": {
        "download": {
          "src": "https://api.pointservices.com/riskinsight-services-ws/resources/v1/findings/workflows/wfpop_0000000000002082611/findings/fndg_0000000000006051384/attachments/jattmt_0000000000006099213/content"
        }
      }
    }
  ]
}
```

A removed attachment doesn't appear here in any form, and there's no `deleted` flag to filter on. A finding with none returns an empty array.

`modifiable` is `true` for a document a user uploaded and `false` for one the system captured.

## Uploading a document

This is the one call on the whole Findings API that isn't JSON. It's `multipart/form-data`, with two parts:

```bash
curl -X POST "https://api.pointservices.com/riskinsight-services-ws/resources/v1/findings/workflows/wfpop_0000000000002082611/findings/fndg_0000000000006051384/attachments" \
  -H "Authorization: Bearer your_access_token_here" \
  -F "name=paystub-august.pdf" \
  -F "file=@paystub-august.pdf;type=application/pdf"
```

`name` becomes the file name you'll see later in the attachments list. `file` is the document's own bytes, as a binary multipart part. Set its `Content-Type` accurately: it's stored exactly as sent and published as `contentType` on every later read, and the service doesn't check it against the bytes.

> **Before Starting:** Have `curl`'s `-F` build the multipart body for you rather than constructing it by hand: `-F "file=@path;type=..."` sets both the filename and the part's `Content-Type` header correctly in one flag.

> **Warning:** **A file over 10 MiB (10,485,760 bytes) is rejected with a 400, never stored cut short.** If your integration handles large source documents, check the file size before uploading so you fail fast instead of spending the whole upload only to have it rejected. An empty file is a 400 too. `name`, and the `file` part's `Content-Type`, are each limited to 255 UTF-8 bytes.

The response is the finding itself, after the upload, without its `category`. It signals `hasAttachments: true` but doesn't embed the new attachment's metadata. Fetch the attachments list (above) afterward to get its `attachmentId` and download link.

## Downloading a document

Follow `links.download.src` from the attachments list rather than building this URL yourself:

```bash
curl -X GET "https://api.pointservices.com/riskinsight-services-ws/resources/v1/findings/workflows/wfpop_0000000000002082611/findings/fndg_0000000000006051384/attachments/jattmt_0000000000006099213/content" \
  -H "Authorization: Bearer your_access_token_here" \
  -o paystub-august.pdf
```

The response is the document's raw bytes, with `Content-Type` set to the recorded media type and `Content-Disposition: attachment` naming the file. Only when no usable type was recorded does the service detect one from the bytes. An error is a problem document (`application/problem+json`), so check the status before you save the body as the document.

> **Warning:** **The `download` link's address is in `src`.** `src` (as in `<img src>`) means "fetch this for content": it returns the document's bytes, not JSON.

This endpoint authorizes you on every request the same way every other call here does, and it is **not** a signed or expiring URL. That means it's safe to store or hand to another part of your own system, as long as that system can present a valid bearer token. A copy of the URL alone, without a token, gets you nothing.

## Removing a document

```bash
curl -X DELETE "https://api.pointservices.com/riskinsight-services-ws/resources/v1/findings/workflows/wfpop_0000000000002082611/findings/fndg_0000000000006051384/attachments/jattmt_0000000000006099213" \
  -H "Authorization: Bearer your_access_token_here"
```

Once removed, the attachment stops appearing in the list and stops counting toward `hasAttachments`. You can't undo a removal through this API.

> **Warning:** **There's no in-place edit for an attachment's content or metadata.** To replace one, remove it and upload the replacement as a new attachment. The new one gets its own `attachmentId`, unrelated to the one you removed.

## Permissions

Uploading and removing both need `riskinsight:JournalResolvedValueUpdateAttachment` in the finding's `authorization.allow`. See [Reviewing a Finding](https://docs.pitchpointsolutions.com/developer_guides/findings/reviewing#permissions) for how `authorization.allow` works. If the token is absent, expect both calls to be refused with a 403. Even when it is present, a call can still be refused with a 403, for example when you may not update the workflow.

Listing and downloading attachments need only permission to read the workflow.

If another request changes the finding while your upload or removal is in flight, yours is refused with a 409 of type `https://pointservices.com/problems/concurrent-modification` and nothing is applied. Read the finding again before you retry.
