> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Manual review approvals

> How two people decide a Lerian SPI operation under manual review through the Cabine API.

A Lerian SPI MED operation can be in manual review. Two people resolve it through the Cabine API. One person proposes the resolution. A different person approves or rejects it. After an approval, the Cabine API sends the decision to [Lerian SPI](https://docs.lerian.studio/en/rails/spi/what-is-lerian-spi.md).

Cabine has no screen for these approvals: call the Cabine API.

## Permissions

Grant `br-spi-operations:update` on Lerian SPI only to the Cabine API application. An application that holds this permission can resolve a manual review directly, without a proposal and a separate decision.

The Cabine API accepts a proposal or a decision only from a person's sign-in, never from an application credential. The person who decides must be different from the person who proposed.

Members of `admin-group` (the Access Manager administrator group), `br-desk-admin-group` and `br-desk-operator-group` can propose and decide. Members of `br-desk-viewer-group` can list and read approvals. See [Access and roles](https://docs.lerian.studio/en/rails/cabine/cabine-access-and-roles.md).

## Propose a resolution

[Propose an operation approval resolution](https://docs.lerian.studio/en/reference/rails/cabine/propose-an-operation-approval-resolution-maker.md) creates the approval. The request carries these fields.

| Field | Description |
| - | - |
| `operationId` | The UUID of the Lerian SPI operation in manual review. |
| `action` | `ATTACH_BACEN_RESOURCE` links the BACEN resource that you found to the operation. `CONFIRM_NOT_APPLIED` states that BACEN never received the attempt. |
| `bacenResourceId` | The BACEN resource UUID. Send it with `ATTACH_BACEN_RESOURCE`. Do not send it with `CONFIRM_NOT_APPLIED`. |
| `digest` | The `requestFingerprint` of the operation: 64 lowercase hexadecimal characters. Read it with [Get DICT MED operation status](https://docs.lerian.studio/en/reference/rails/spi/dict/get-regulatory-operation.md). |
| `note` | An optional reason, up to 500 characters. |

```bash theme={null}
curl -X POST "$CABINE_API_URL/v1/operation-approvals" \
  -H "Authorization: Bearer $CABINE_BEARER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "operationId": "01930000-0000-7000-8000-000000000000",
    "action": "CONFIRM_NOT_APPLIED",
    "digest": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
    "note": "BACEN has no record of this attempt."
  }'
```

The response carries the `approvalId` and the `decisionDeadline`. The state is `requested`. While an earlier proposal for the same operation waits for a decision inside its window, a new proposal returns `409` with the code `DESK-0005`.

## Decide

[Approve or reject an operation approval workflow](https://docs.lerian.studio/en/reference/rails/cabine/approve-or-reject-an-operation-approval-workflow-checker.md) records the decision. Send `decision` as `approved` or `rejected`, with an optional `note` of up to 500 characters.

```bash theme={null}
curl -X POST "$CABINE_API_URL/v1/operation-approvals/$CABINE_APPROVAL_ID/decision" \
  -H "Authorization: Bearer $CABINE_BEARER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"decision": "approved"}'
```

A decision on an approval that is already decided returns `409` with the code `DESK-0005`. A rejection is not sent to Lerian SPI. The operation stays in manual review.

To find approvals, use [List operation approval workflows](https://docs.lerian.studio/en/reference/rails/cabine/list-operation-approval-workflows.md). Filter with `status` set to `requested`, `approved`, `rejected` or `expired`. To read one approval, use [Get one operation approval workflow](https://docs.lerian.studio/en/reference/rails/cabine/get-one-operation-approval-workflow.md).

## Decision window

The person who decides has 24 hours from the proposal. An administrator can change this period with `DESK_OPERATION_APPROVAL_DECISION_WINDOW`. See [Cabine API environment variables](https://docs.lerian.studio/en/rails/cabine/cabine-environment-variables.md).

A decision after the window returns `410` with the code `DESK-0006`. Propose the operation again. After the window, the Cabine API sets the state to `expired` at its next check.

## Delivery to Lerian SPI

An approval is sent to [Resolve regulatory operation](https://docs.lerian.studio/en/reference/rails/spi/dict/resolve-regulatory-operation.md) on Lerian SPI. The Cabine API makes the first attempt when the decision is recorded. The decision response does not carry the delivery. Read the approval to see the `delivery` field. It appears after the first attempt. It has `outcome`, `attemptCount`, `lastStatusCode` and `lastAttemptAt`.

| Outcome | Meaning | What you do |
| - | - | - |
| `pending` | Lerian SPI has not accepted the decision. The Cabine API tries again. | Read the approval again later. |
| `succeeded` | Lerian SPI accepted the decision. | Nothing. |
| `refused` | Lerian SPI answered `422`: the decision is not valid for the operation, for example the `digest` does not match. The Cabine API does not try again. | Propose again with correct data. |
| `exhausted` | All attempts failed. The Cabine API does not try again. | Read the operation in Lerian SPI. If it is still in manual review, check the Cabine API settings for Lerian SPI, then propose again. |

By default, a retry waits at least 30 seconds after the first failed attempt. The wait doubles after each failure, up to 30 minutes. The Cabine API makes at most 20 attempts. Administrators set these values with the `DESK_APPROVAL_DELIVERY_*` variables.

## Errors

The Cabine API returns the codes in the [Cabine API error list](https://docs.lerian.studio/en/reference/rails/cabine/cabine-error-list.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.