# Edit a Finding

Source: https://docs.pitchpointsolutions.com/api/findings/edit_workflow_finding/

**Beta**

Change a finding's review note, its reviewed state and its result override, in one call. It is a partial update: send only the fields you want to change. The address is published on every finding as `editStates.edit`, with `method` `POST`.

Each field needs its own permission, listed in the finding's `authorization.allow`:

| Field | Needs |
|---|---|
| `reviewNote` | `riskinsight:JournalResolvedValueUpdateNote` |
| `reviewed` | `riskinsight:JournalResolvedValueReview` |
| `overrideResult` | `riskinsight:JournalResolvedValueReview` |

A body whose fields need different permissions needs all of them. If any is missing, the call is refused with a 403 and nothing is changed.

> **Before Starting:** Read the finding first ([Get One Finding](https://docs.pitchpointsolutions.com/api/findings/get_workflow_finding)), and offer only the changes its `authorization.allow` permits. `editStates.edit` is present whatever your permissions, so it tells you where to send an edit, not whether you may.

### Sample

Record a note and mark the finding reviewed in one call:

```bash
url="https://api.pointservices.com/riskinsight-services-ws/resources/v1/findings/workflows/wfpop_0000000000002082611/findings/fndg_0000000000006051384"
curl -X POST "${url}" \
  -H "Authorization: Bearer your_access_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "reviewNote": "Confirmed with borrower via phone on 8/7; income supported by attached paystub.",
    "reviewed": true
  }'
```

#### Header Properties

| Property | Value | Required? |
|----------|-------|-----------|
| Authorization | Bearer *your token* | true |
| Content-Type | application/json | true |

#### Path Parameters

| Property | Description | Type |
|----------|-------------|------|
| workflowId | The workflow's id, exactly as this API returned it. | string |
| findingId | The finding's id, exactly as published in `findingId`. It must belong to `workflowId`. | string |

> **Warning:** `workflowId`, `findingId` and `attachmentId` are opaque. Send each one back exactly as this API returned it: don't parse it, build one yourself, or add or remove its prefix. A `findingId` or `attachmentId` that isn't exactly as published, including one with its prefix removed, returns a 404 as if it didn't exist.

#### Request Data Properties

Every field is optional. A field you leave out is left unchanged, and a JSON `null` is treated the same as leaving it out. A body with none of these fields, including `{}`, changes nothing and returns the finding as it is.

| Property | Description | Type | Required |
|----------|-------------|------|----------|
| reviewNote | The finding's new review note. It **replaces** the current note; it is not appended. Leading and trailing whitespace is trimmed. Send an empty or blank string to clear the note, after which the finding has no `reviewNote`. Earlier notes stay on the finding's [history](https://docs.pitchpointsolutions.com/api/findings/get_workflow_finding_history). Setting the note doesn't change `reviewed`. | string | no |
| reviewed | `true` marks the finding reviewed; `false` returns it to unreviewed. It doesn't change the note or `reviewRequired`, and it has no precondition: you can mark a finding reviewed with or without a note. | boolean | no |
| overrideResult | A reviewer's corrected outcome for the finding. It is published as `resultOrOverriddenResult`, while `result` keeps the check's original outcome. An override can be replaced by sending another, but can't be removed. A string that is empty or only whitespace is a 400. | string | no |

Other bodies you may send:

| To | Send |
|---|---|
| Replace the note only | `{"reviewNote": "Income re-verified against the September paystub."}` |
| Clear the note | `{"reviewNote": ""}` |
| Return the finding to unreviewed | `{"reviewed": false}` |
| Override the outcome and mark it reviewed | `{"overrideResult": "Pass", "reviewed": true}` |

Setting `reviewed` to `false` doesn't clear the note. To undo both, send `{"reviewed": false, "reviewNote": ""}`, which needs both permissions.

### Responses

**200**

The finding after the edit, in exactly the shape [Get One Finding](https://docs.pitchpointsolutions.com/api/findings/get_workflow_finding) returns. See [List a Workflow's Findings](https://docs.pitchpointsolutions.com/api/findings/get_workflow_findings_list) for the full `Finding` field table.

```json
{
  "workflowId": "wfpop_0000000000002082611",
  "finding": {
    "findingId": "fndg_0000000000006051384",
    "code": "HP.ST",
    "displayName": "High-priority stated income mismatch",
    "category": ["Income"],
    "references": [
      { "correlationId": "borrower-1", "type": "person" },
      { "correlationId": "subject-property", "type": "property" }
    ],
    "severity": "HIGH",
    "result": "Fail",
    "resultOrOverriddenResult": "Fail",
    "userMessage": ["Stated income does not match the verified source document."],
    "userSuggestion": ["Confirm the applicant's income against the attached document before proceeding."],
    "reviewed": true,
    "reviewRequired": true,
    "reviewer": "Jane Reviewer",
    "reviewNote": "Confirmed with borrower via phone on 8/7; income supported by attached paystub.",
    "updatedAt": "2026-08-07T15:04:09Z",
    "hasAttachments": true,
    "hasHistory": true,
    "links": {
      "self": { "href": "https://api.pointservices.com/riskinsight-services-ws/resources/v1/findings/workflows/wfpop_0000000000002082611/findings/fndg_0000000000006051384" },
      "history": { "href": "https://api.pointservices.com/riskinsight-services-ws/resources/v1/findings/workflows/wfpop_0000000000002082611/findings/fndg_0000000000006051384/history" },
      "attachments": { "href": "https://api.pointservices.com/riskinsight-services-ws/resources/v1/findings/workflows/wfpop_0000000000002082611/findings/fndg_0000000000006051384/attachments" }
    },
    "editStates": {
      "edit": {
        "href": "https://api.pointservices.com/riskinsight-services-ws/resources/v1/findings/workflows/wfpop_0000000000002082611/findings/fndg_0000000000006051384",
        "method": "POST"
      }
    },
    "authorization": {
      "allow": [
        "riskinsight:JournalResolvedValueReview",
        "riskinsight:JournalResolvedValueUpdateNote",
        "riskinsight:JournalResolvedValueUpdateAttachment"
      ]
    }
  }
}
```

`reviewer` appears because `reviewed` is now `true`. `reviewRequired` is unchanged, so this finding has moved from outstanding to review done. Each field you changed adds its own entry to the finding's [history](https://docs.pitchpointsolutions.com/api/findings/get_workflow_finding_history).

**400**

The body is malformed (not JSON, or a field of the wrong type), or `overrideResult` is present but blank. Nothing is changed.

The body is an RFC 9457 problem document, served as `application/problem+json`, with `type` set to `about:blank`. `detail` names the field at fault, for a person reading your logs.

A `Content-Type` header that names a character set the service can't use is refused before the body is read. That 400 carries the type `https://pointservices.com/problems/invalid-request` instead, and says nothing about the body's content.

| Property | Description | Type |
|----------|-------------|------|
| type | An absolute URI identifying the problem type. This is the stable value to match on; compare it as a string. | string |
| title | A short summary, written for a person. | string |
| status | The HTTP status code, repeated in the body. | number |
| detail | An explanation of this occurrence, written for your logs. Never match on it. | string |
| instance | A URI identifying this occurrence, written as `urn:pps:request:<id>`. The id is the same value as the `x-pps-request-id` response header. Quote it when you report a problem to PPS. | string |

```json
{
  "type": "about:blank",
  "title": "Bad Request",
  "status": 400,
  "detail": "overrideResult must not be blank",
  "instance": "urn:pps:request:0000000000000000000000000000000000000000000000000000"
}
```

**401**

No credential the service could accept reached it. The body is an RFC 9457 problem document, served as `application/problem+json`, and the response carries a `WWW-Authenticate` challenge. The request body is never read, so a 401 says nothing about whether the rest of your request would have been accepted.

Two problem types tell the cases apart:

| `type` | Meaning | `WWW-Authenticate` |
|--------|---------|--------------------|
| `about:blank` | No credential was presented. | `Bearer realm="Helix"` |
| `https://pointservices.com/problems/invalid-token` | A credential was presented and rejected. The response doesn't say why: an expired, revoked, badly signed and malformed token all look the same. | `Bearer realm="Helix", error="invalid_token"` |

```json
{
  "type": "https://pointservices.com/problems/invalid-token",
  "title": "The access token was not accepted",
  "status": 401,
  "detail": "The access token was rejected.",
  "instance": "urn:pps:request:0000000000000000000000000000000000000000000000000000"
}
```

> **Warning:** Refresh your token on a 401, and only on a 401. An expiring token moves a call from 200 to 401, never to 403, so a fresh token is the fix here and never the fix for a 403.

**403**

You may not read this workflow, or you lack a permission that a field in this body needs, or you may not update this workflow.

You are authenticated, and the answer is still no. This is an authorization decision, not a statement about your token, so refreshing the token won't turn it into a 200. Nothing is changed: a refused request is all or nothing, so there is nothing to clean up on your side.

The body is an RFC 9457 problem document, served as `application/problem+json`. Two problem types tell the cases apart:

| `type` | Meaning |
|--------|---------|
| `about:blank` | The request was refused before the operation ran. |
| `https://pointservices.com/problems/not-authorized` | The operation ran, and policy won't let you act on the workflow or finding you named. |

```json
{
  "type": "https://pointservices.com/problems/not-authorized",
  "title": "Not authorized",
  "status": 403,
  "detail": "The authenticated identity is not permitted to perform this action.",
  "instance": "urn:pps:request:0000000000000000000000000000000000000000000000000000"
}
```

A 403 can also come from the edge, before the service is reached: for example, when a request value resembles SQL injection or cross-site scripting. Those refusals carry no problem document. Write your client to handle an error response it can't parse, rather than assuming every failure has a JSON body.

**404**

Unknown `workflowId` or unknown `findingId`, including a `findingId` that belongs to a different workflow.

The body is an RFC 9457 problem document, served as `application/problem+json`. Two problem types tell the cases apart:

| `type` | Meaning |
|--------|---------|
| `https://pointservices.com/problems/not-found` | An id in the path isn't one this API knows. Check that you sent it exactly as the API returned it. |
| `about:blank` | The URL or method isn't one this API serves at all. Check the path. |

| Property | Description | Type |
|----------|-------------|------|
| type | An absolute URI identifying the problem type. This is the stable value to match on; compare it as a string. | string |
| title | A short summary, written for a person. | string |
| status | The HTTP status code, repeated in the body. | number |
| detail | An explanation of this occurrence, written for your logs. Never match on it. | string |
| instance | A URI identifying this occurrence, written as `urn:pps:request:<id>`. The id is the same value as the `x-pps-request-id` response header. Quote it when you report a problem to PPS. | string |

```json
{
  "type": "https://pointservices.com/problems/not-found",
  "title": "No such resource",
  "status": 404,
  "detail": "Finding[fndg_0000000000006051384] not found",
  "instance": "urn:pps:request:8d3a1f56-6c94-4e20-b7f8-0a5e9c2d4b73"
}
```

> **Warning:** Branch on `type`, never on the text of `title` or `detail`.

**409**

Another request changed the finding while yours was in flight, so **nothing was applied**. The body is an RFC 9457 problem document, served as `application/problem+json`, with the type `https://pointservices.com/problems/concurrent-modification`.

The service doesn't retry it for you, and the response carries no `Retry-After`. Read the finding again, decide whether your change still makes sense against what you now see, and resubmit only if it does. Don't retry blindly: you may overwrite a change someone else just made.

```json
{
  "type": "https://pointservices.com/problems/concurrent-modification",
  "title": "Concurrent modification",
  "status": 409,
  "detail": "The resource was modified by another request while this one was being processed. This request was not applied; re-read the resource before resubmitting.",
  "instance": "urn:pps:request:0000000000000000000000000000000000000000000000000000"
}
```
