Beta
Edit a Finding
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.
Read the finding first (Get One 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:
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 |
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. 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 returns. See List a Workflow’s Findings for the full Finding field table.
{
"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.
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 |
{
"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" |
{
"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"
}
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. |
{
"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 |
{
"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"
}
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.
{
"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"
}