# Get a Workflow's Findings by Category

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

**Beta**

The workflow's findings grouped into categories, in the order they were configured. Use this when you want findings grouped the way the workflow owner set up, rather than the flat list from [List a Workflow's Findings](https://docs.pitchpointsolutions.com/api/findings/get_workflow_findings_list). Each finding here is identical, field for field, to its counterpart in the flat list and in a direct single-finding GET.

If you only need counts and severity breakdowns per category, without fetching every finding, use [Get a Workflow's Category Counts](https://docs.pitchpointsolutions.com/api/findings/get_workflow_category_counts) instead.

Read-only and safe to poll.

### Sample

```bash
url="https://api.pointservices.com/riskinsight-services-ws/resources/v1/findings/workflows/wfpop_0000000000002082611/bycategories"
curl -X GET "${url}" \
  -H "Authorization: Bearer your_access_token_here"
```

#### Header Properties

| Property | Value | Required? |
|----------|-------|-----------|
| Authorization | Bearer *your token* | true |

#### Path Parameters

| Property | Description | Type |
|----------|-------------|------|
| workflowId | The workflow's id, exactly as this API returned it. | 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.

### Responses

**200**

| Property | Description | Type |
|----------|-------------|------|
| workflowId | The workflow's id. | string |
| categories | The workflow owner's configured categories, in configured order: present them in this order. Always an array, never null. | array |

##### categories entry

| Property | Description | Type |
|----------|-------------|------|
| category | The display name, exactly as configured. Not a closed set: two workflows can use different names, and names can be reworded or added over time. Treat it as a label to show a person, never a key to switch on; use a finding's `code` for that. | string |
| status | Rollup review state for this category: `IN_PROGRESS` when any matched finding still needs review, else `RESOLVED`. | string |
| findings | The findings in this category: see [List a Workflow's Findings](https://docs.pitchpointsolutions.com/api/findings/get_workflow_findings_list) for the `Finding` shape. `[]` when there are none, never null. | array |

```json
{
  "workflowId": "wfpop_0000000000002082611",
  "categories": [
    {
      "category": "Income",
      "status": "IN_PROGRESS",
      "findings": [
        {
          "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": false,
          "reviewRequired": true,
          "updatedAt": "2026-08-07T14:52:10Z",
          "hasAttachments": true,
          "hasHistory": true,
          "authorization": {
            "allow": [
              "riskinsight:JournalResolvedValueReview",
              "riskinsight:JournalResolvedValueUpdateNote",
              "riskinsight:JournalResolvedValueUpdateAttachment"
            ]
          }
        }
      ]
    }
  ]
}
```

> **Warning:** An **empty `categories` array doesn't mean "nothing to review"**: a workflow with no categories configured returns an empty list too.

A finding classified into more than one category appears once in each category it belongs to. Its own `category` field lists every category it carries, and its `findingId` is the same wherever it appears.

**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.

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. 

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`.

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": "Workflow[wfpop_0000000000002082611] not found",
  "instance": "urn:pps:request:8d3a1f56-6c94-4e20-b7f8-0a5e9c2d4b73"
}
```

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