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

# How Plugin SISBAJUD runs orders

> How Plugin SISBAJUD receives judicial orders, runs blocks, unblocks, transfers, and information requests on Midaz, and reports each result.

**Plugin SISBAJUD** runs judicial orders on the client's Midaz ledger. The orders come from the Mensageria or, on the direct channel, from the institution's own system. This page describes how the plugin receives an order, what each operation writes, and how the plugin reports the result. [Operating Plugin SISBAJUD](https://docs.lerian.studio/en/interfaces/plugin-sisbajud/plugin-sisbajud-operations.md) describes the setup. [How Lerian SISBAJUD works](https://docs.lerian.studio/en/rails/sisbajud/how-sisbajud-works.md) shows how an order moves from the Mensageria to Plugin SISBAJUD.

## Order lifecycle

***

1. **Notice.** The Streaming Hub posts an execution notice to the webhook listener of the plugin. The notice carries the order ID and the order revision, not the order.
2. **Verification.** The plugin verifies the HMAC signature of the delivery. It stores each signed notice before it answers.
3. **Fetch.** The plugin gets the order with `GET /v1/sisbajud/execution/orders/{orderId}` on the execution API of the Mensageria.
4. **Owner check.** Before its first write, the plugin checks that it owns the institution of the order.
5. **Execution.** On its next cycle, the plugin runs the operation on Midaz.
6. **Result.** The plugin posts the result with `POST /v1/sisbajud/execution/orders/{orderId}/execution-results`.

### Webhook answers

| Delivery | Answer |
| - | - |
| The body is larger than 1 MiB. | `413`. The plugin stores nothing. |
| The HMAC signature is missing or invalid. | `401`. The plugin does not store the delivery. |
| The signature verifies, but the body is neither a usable notice nor a verification ping. | `200`. The plugin parks the delivery and raises an alarm. |
| The Mensageria answers the fetch with a `4xx` status other than `401`, `404`, `410`, or `429`. | `200`. The plugin parks the delivery and raises an alarm. |
| The Mensageria or a database fails. | `503`. The Streaming Hub retries the delivery. |
| The plugin accepts the notice. | `200`. The plugin runs the order on its next cycle. |

The plugin accepts a delivery with a timestamp outside `EXECUTOR_HUB_WEBHOOK_MAX_SKEW_SEC`, and logs a warning.

### Fetch answers

| Answer of the Mensageria | What the plugin does |
| - | - |
| `200` | Runs the order. |
| `404` or `410` | Treats the order as withdrawn. It writes nothing on the ledger. |
| `401`, `429`, a `5xx` status, or a network error | Retries the request. |
| Any other `4xx` status | Parks the delivery and raises an alarm. |
| `200`, with a block order whose minimum amount is above its amount | Writes nothing on the ledger and raises an alarm. |

### Owner check

The plugin writes on the ledger only for the institutions that it owns. When it creates an institution, it records its own M2M subject (`EXECUTOR_M2M_SUBJECT`) as the owner.

* If another identity owns the institution, the plugin writes nothing and raises an alarm.
* If the institution has no configuration on this plugin, the order goes to a human decision.

### Execution cycle

The plugin runs one cycle every `EXECUTOR_TICK_EVERY`. The default is `30s`. Each cycle runs three steps in this order:

1. **Ingest.** Runs the accepted notices.
2. **Orphan check.** Looks for operations on the block accounts that the plugin did not write.
3. **Monitor.** Sends a heartbeat for each write that has no known outcome.

A failed step does not stop the other steps. The plugin drains the results that wait in the outbox in a separate loop for each channel, so a slow channel does not hold the cycle or another channel.

### Results

The plugin stores each result in an outbox before it posts it. Each post carries the `X-Idempotency` header. When a post fails with a transient error, the plugin posts the same result again after a wait, up to `EXECUTOR_RESULT_MAX_ATTEMPTS` attempts. The default is 12.

After the last attempt, the plugin parks the result, records a human decision, and raises an alarm. It does not post a parked result again, and the subsequent versions of the same order wait behind it. A platform administrator re-arms the result. [Operating Plugin SISBAJUD](https://docs.lerian.studio/en/interfaces/plugin-sisbajud/plugin-sisbajud-operations.md#orders-that-need-attention) describes the route.

| Field | Values |
| - | - |
| `executionState` | `ACCEPTED`, `EXECUTING`, `MONITORING`, `RESOLVED`, `INDETERMINATE`, `AWAITING_RECONCILIATION` |
| `effect` | `NONE`, `FULL`, `PARTIAL`, `INDETERMINATE` |

The `reasonCode` of each operation is in the sections below.

## Operations

***

The block and the unblock write on Midaz. The information request only reads.

### Block (BLOQUEIO)

1. The plugin reads the active accounts of the subject. A subject with no active account gets `NONE` with `NO_CLIENT`.
2. It applies the branch and account restriction of the order. [Branch and account restriction](#branch-account-restriction) gives the rules.
3. It blocks a salary account only when the order allows salary accounts.
4. It blocks own funds only. An account in overdraft counts as 0.
5. It sends one write to Midaz. The write has one leg for each source account, and all legs go into the block account of the order.

| Outcome | `effect` | `reasonCode` |
| - | - | - |
| The block covers the amount still open on the order. | `FULL` | `BLOCKED_TOTAL` |
| The block covers less than that amount. | `PARTIAL` | `BLOCKED_PARTIAL` |
| No funds are available. | `NONE` | `NO_BALANCE` |

A block order can carry a minimum amount. When the total that the plugin can block is below that minimum, it writes nothing and hands the order to a human decision.

#### Branch and account restriction

The plugin reads the branch and the account number of each account from the CRM instruments of the subject. With `crmMode` `embedded`, the branch is `bankingDetails.branch` and the account number is `bankingDetails.account`. With `crmMode` `legacy`, the CRM gives neither.

* An order that names a branch goes to a human decision when an account that can be the named one has an unknown or ambiguous branch.
* An order that names an account number blocks the accounts whose number matches. It also blocks an account whose number is unknown or ambiguous, up to the order amount, because that account can be the one that the court named. The accounts whose number matches come first. With `crmMode` `legacy`, no number is known, so every account that passes the branch rule can be blocked.
* An order with an account number and no branch goes to a human decision when two or more accounts match.
* With an account number, the result is `NONE` when every account number is known and none matches.

### Unblock (DESBLOQUEIO)

The unblock releases funds of a parent block order, identified by `parentOrderId`. The plugin uses the parent block's recorded origin account as the release destination. If it cannot safely determine one destination, it requires a human decision rather than guessing.

* Without a parent, the result is `NONE` with `PARENT_UNRESOLVED`, and the plugin writes nothing.
* When the parent has no known effect yet, the plugin waits. At the order deadline minus `RESPONSE_DEADLINE_MARGIN`, the order goes to a human decision. If the order has no deadline, it goes to a human decision at once.
* The plugin reads the block account of the parent when it runs the order. It releases the smallest of three amounts: the amount that the order asks, the remaining balance of the parent, and the balance of the block account.
* When the release amount is 0, the result is `NOTHING_TO_RELEASE`.
* When a subject account is a salary account, the order goes to a human decision.

### Information request (REQUISICAO\_INFORMACAO)

The plugin reads the balances of the subject and writes nothing on the ledger. If the subject is a client but does not own the specified account, the plugin requires a human decision rather than reporting that the subject is not a client.

* A subject with at least one active account gets `FULL` with `INFO_FULFILLED`.
* A subject with no active account gets `NONE` with `INFO_NON_CLIENT`.
* An order that names a branch goes to a human decision.

When the order asks for the balance (`searchBalance`), the result carries two totals: the blockable total and the blockable total of salary accounts. A subject with no active account gets 0.00 in both.

## Direct channel

***

On the direct channel, the institution's own system sends judicial orders to the plugin, and the Mensageria is not involved. The plugin stores each order, runs it on Midaz, and keeps the results. The institution reads the results and answers the court itself.

The plugin serves the direct channel only when it runs in executor mode, with one tenant, and `PLUGIN_AUTH_ENABLED` on. Otherwise the five direct routes answer `501`.

A platform administrator switches an institution to the direct channel with `PUT /v1/institutions/{institutionId}/channel`. The switch needs all of these:

* The plugin owns the institution.
* The stored institution code is the 8-character CNPJ root of the responsible grouping.
* The institution has no open order, and no block of the institution still holds money.

The same route switches the institution back to the Mensageria. An institution that is not on the direct channel gets `422` `SBJ-0025` on a direct order.

| Route | Purpose |
| - | - |
| `POST /v1/institutions/{institutionId}/executor/orders` | Submits an order or a revision. It answers `202`. |
| `GET /v1/institutions/{institutionId}/executor/orders` | Searches the orders by `externalReference`. |
| `GET /v1/institutions/{institutionId}/executor/orders/{orderId}` | Reads one order. |
| `GET /v1/institutions/{institutionId}/executor/orders/{orderId}/results` | Lists the result versions of one order. |
| `GET /v1/institutions/{institutionId}/executor/results` | Reads the results of the institution as a cursor feed. |

The results feed delivers each result at least once. Deduplicate by `orderId`, `orderRevision`, and `executionVersion`. Submit an unblock or a transfer only after the `202` of its parent block.

The direct channel takes `BLOCK`, `UNBLOCK`, `TRANSFER`, `INFO`, and `INTERRUPT`. A transfer moves funds from the block account of its parent to the judicial deposit account of the institution. An interruption stops a permanent block and keeps what the block already holds. `DIRECT_PERMANENT_BLOCK_ENABLED` decides whether the channel takes a new permanent block. With the default, it refuses one with `422` `SBJ-0027`.

A permanent block keeps blocking new credits of the subject, up to the order amount. The plugin reads those credits from the Midaz ledger events. It lasts one year from receipt, or until the court date when the order gives one, and never more than one year. When the term ends, the block stops taking new credits and keeps what it already holds. An interruption, a transfer or an unblock of the remainder, or a cancellation closes it. When a later block of a permanent block keeps failing, the plugin parks the attempt, raises an alarm, and stops taking new credits for that block until a platform administrator resolves the attempt. A direct order that fails on every attempt, or reaches its cut while the ledger fails, is parked too. Its read shows `parked` `true`.

The machine roles `order-submitter` and `result-reader` call these routes. [Operating Plugin SISBAJUD](https://docs.lerian.studio/en/interfaces/plugin-sisbajud/plugin-sisbajud-operations.md#roles) describes them.

### Erase a direct-channel order

A platform administrator erases one concluded direct-channel order with `POST /v1/admin/direct/orders/{orderId}/erase`, for an LGPD request from the data subject or at the end of retention. `POST /v1/admin/direct/purge` lists the orders that can be erased now and changes nothing. Both operations need a user token with no tenant, and they answer only on a single-tenant installation.

After the erase, nobody can read the content submitted with the order again: the order body and the document number of the person or company that the order targets. The erase cannot be undone. Your `clientOrderId` and `externalReference` become tokens. A lookup by the original `externalReference` still finds the erased order, with its content hidden.

The erase does not remove:

* the court protocol and the sequence numbers of the order;
* the plugin's ledger journal, with its amounts, and the Midaz transactions;
* the results already in the results feed, and what your system already read from it;
* the audit entry of the erase;
* backups taken before the erase. After you restore one, erase the order again.

The plugin never erases an order by itself. The erase refuses an order that is still open, still holds money, or has an open child order. It also refuses a block order less than one year after its first ledger posting, and a permanent block before its end date. When an order stays eligible longer than `EXECUTOR_DIRECT_PURGE_ELIGIBLE_ALARM_AFTER` (default `168h`), the plugin logs an error so that an administrator reviews it.

## Ledger semantics

***

### Block account

The alias of the block account has the prefix `@block_sisbajud_` and then the 32 lowercase hexadecimal digits of the block order ID. An unblock finds the account through its `parentOrderId`.

The account type of a block account is `sisbajud_block`. If your ledger validates account types, register `sisbajud_block` as an account type.

### Directions

* A block decreases the subject accounts and increases the block account.
* A transfer decreases the block account of its parent and increases the judicial deposit account.
* An unblock decreases the block account. Its write has one source leg: the block account. It releases funds to the recorded origin account, or requires a human decision when one safe destination cannot be determined.

A block moves the amount from the available balance of each subject account to the available balance of the block account.

### Idempotency

Each write carries the `X-Idempotency` header and `X-TTL: 604800`, which is seven days. Midaz keeps the idempotency key of a write for that time. The key is `orderId:orderRevision`, so a redelivery of the same order revision sends the same key.

## Uncertain writes

***

When Midaz answers a write without proof of its effect, the outcome is `INDETERMINATE`. The plugin reads the ledger again before each heartbeat. It sends a heartbeat result to the Mensageria every `EXECUTOR_HEARTBEAT_EVERY`, until the Mensageria answers that the order revision is superseded.

The order goes to a human decision at the first of two limits:

* `EXECUTOR_HUMAN_DECISION_AFTER_BEATS` heartbeats without a change. The default is 3.
* The order deadline minus `RESPONSE_DEADLINE_MARGIN`.

The plugin then records a human decision. In the human-decisions list it has `humanDisposition` `HELD_MONEY_MOVED`. The heartbeats continue, with `executionState` and `effect` both `INDETERMINATE`.

### Ledger down at the cut

The cut of an order is its deadline minus `RESPONSE_DEADLINE_MARGIN`. When Midaz fails for an order of the Mensageria at or after the cut, and no court result of that revision waits in the outbox, the plugin stops its retries. It records a human decision and sends no result. Check the ledger and answer the court yourself before the deadline. Otherwise the Mensageria records a non-response.

### After a person answers

Take a revision that was handed to a person at its cut, or that a person resolved as effect-acknowledged or proven-no-effect. When its write lands on Midaz afterwards, the plugin sends no `FULL` or `PARTIAL` result for it. It records the effect and raises an alarm. The court gets only the answer of the person, so reconcile a late effect on the ledger yourself.

### Operations that the plugin did not write

The orphan check reads the operations of each block account over `EXECUTOR_ORPHAN_RECONCILE_WINDOW`. The default is `48h`. The plugin reports an operation as an orphan when its transaction has no `metadata.code`, or has a code that the plugin did not record. This step only detects. It changes nothing on the ledger.

## Accounting routes

***

Accounting routes are optional, per organization. The plugin reads them from the tenant documents `routing.organizations` and `routing.ledger_bindings` in the Systemplane. With `SYSTEMPLANE_ENABLED` off, the plugin writes without routes.

With `routing.organizations` at `schemaVersion` 2, each organization carries its own routes for each flow and the list of ledgers that post with them. The plugin then does not read `routing.ledger_bindings`. A write to a ledger that is not on the list of its organization goes out without routes.

When a write cannot go out because of its configuration, the plugin defers it. Midaz has applied nothing, so the order waits, and a later delivery sends the same write with the same idempotency key. These conditions defer a write:

* The plugin cannot resolve the accounting routes of the write.
* Midaz refuses the accounting routes of the write: `422` with code `0113`, `0114`, `0116`, `0117`, `0118`, `0119`, `0152`, or `0157`, or `404` with code `0105`.

Fix the configuration before the order deadline. `GET /v1/admin/ledger-routing/preflight` checks the configuration before you turn on routes.

## Midaz calls

***

The plugin calls the Midaz ledger at the `baseUrl` of each institution, with the token of that institution. Give the credentials of the institution access to these operations:

| Purpose | Midaz operation |
| - | - |
| Block | `POST /v1/organizations/{organizationId}/ledgers/{ledgerId}/transactions/block` |
| Unblock | `POST /v1/organizations/{organizationId}/ledgers/{ledgerId}/transactions/unblock` |
| Transfer to the judicial deposit account | `POST /v1/organizations/{organizationId}/ledgers/{ledgerId}/transactions/json` |
| Create a block account | `POST /v1/organizations/{organizationId}/ledgers/{ledgerId}/accounts` |
| Find an account by alias | `GET /v1/organizations/{organizationId}/ledgers/{ledgerId}/accounts?alias=` |
| Read an account | `GET /v1/organizations/{organizationId}/ledgers/{ledgerId}/accounts/{accountId}` |
| Read the balances of an alias | `GET /v1/organizations/{organizationId}/ledgers/{ledgerId}/accounts/alias/{alias}/balances` |
| Read the operations of a block account | `GET /v1/organizations/{organizationId}/ledgers/{ledgerId}/accounts/{accountId}/operations` |
| Read a transaction | `GET /v1/organizations/{organizationId}/ledgers/{ledgerId}/transactions/{transactionId}` |
| Find the transactions of a write | `GET /v1/organizations/{organizationId}/ledgers/{ledgerId}/transactions?metadata.code=` |

The plugin finds the accounts of a subject through the Midaz CRM. With `crmMode` `legacy`, it calls `GET /v1/aliases?document=` at `crmBaseUrl`. With `crmMode` `embedded`, it calls `GET /v2/organizations/{organizationId}/instruments?document=` at `baseUrl`.


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