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

# TED Lerian antifraud partner check

> How TED Lerian asks your institution's antifraud partner to screen sent TEDs, internal transfers and received TEDs, how you turn each check on, and what the partner must implement.

TED Lerian can ask your institution's own antifraud partner to screen a transfer before money moves. The partner answers `ALLOW`, `REVIEW` or `DENY`. Each check is off by default. You turn it on for your institution.

## The four checks

***

Each check is a flow. A flow has its own settings, route and credentials.

| Flow | What the partner screens |
| - | - |
| `out` | A TED that your institution sends. |
| `intra_out` | An internal transfer, from the payer side. |
| `intra_in` | An internal transfer, from the payee side. |
| `in` | A TED that your institution receives. |

TED Lerian reads the settings when it creates a transfer. A transfer created with every flow off is not screened, even if you turn a flow on later. Its `fraudCheck` is `null`.

## Sent TEDs and internal transfers

***

TED Lerian asks the partner for at most the `syncBudgetMs` of the route, which is 4000 ms or less. A TED is screened after TED Lerian holds the amount and before it sends the TED to the STR. For an internal transfer with both flows on, TED Lerian asks about the payer side first, then about the payee side. It stops at the first refusal.

| Answer | Result |
| - | - |
| `ALLOW` | A TED goes on to the STR. An internal transfer settles once every side is allowed. |
| `DENY` | The transfer is `REJECTED` with `TED-0050` and `rejection.source` `ANTIFRAUD`. TED Lerian releases the hold, if there is one, and the usage limit. |
| `REVIEW` | The transfer waits in `FRAUD_ANALYSIS`. A TED keeps its hold. For an internal transfer, TED Lerian holds the payer amount right after, so a refusal by the ledger can still reject it. |

The create response can return `REJECTED` or `FRAUD_ANALYSIS`. Every transfer read carries `fraudCheck`. It holds `decideBy` and one check for each flow that was on when TED Lerian created the transfer. Each check holds the `decision`, its `origin` (`PROVIDER` or `FALLBACK`), the `cause`, the `analysisId`, the `score`, the `reasons`, the `resolution` and `resolvedAt`. The `score` and the `reasons` are the partner's, as it sent them. The text of a reason can name a third party.

`decideBy` is the creation time plus the review window of the flow, less 2 minutes. The review window is the `review_window_minutes` setting, 10 to 30 minutes. When both internal flows are on, the shorter window applies. For a TED, the close of the operating window can set `decideBy` earlier: it is then the close less 2 minutes. The close of the operating window does not apply to an internal transfer.

With the `out` flow on, TED Lerian refuses a TED with `422 TED-0020` when the operating window closes in 2 minutes and 4 seconds or less. TED Lerian holds nothing then. If TED Lerian cannot read the antifraud settings, it refuses the send with a `503` before it holds anything.

A transfer in `FRAUD_ANALYSIS` ends in one of these ways:

1. **The partner decides.** The partner calls the [resolve operation](https://docs.lerian.studio/en/reference/interfaces/ted-lerian/apply-a-fraud-analysis-decision-to-a-transfer.md). On `DENY`, TED Lerian rejects the transfer with `TED-0050`. On `ALLOW`, it sends a TED to the STR, or settles an internal transfer once every side allows.
2. **You cancel it.** `POST /v1/transfers/{transferId}/cancel` answers `200`, and TED Lerian releases the hold and the usage limit. While a call to Midaz or Lerian SPB for the transfer is in progress, the answer is `409 TED-0061`.
3. **`decideBy` passes.** TED Lerian rejects the transfer with `TED-0052`, with the timeline origin `WORKER`, and releases the hold and the usage limit.

After `decideBy`, TED Lerian still accepts a `DENY` for a transfer in `FRAUD_ANALYSIS`. An `ALLOW` that arrives at or after `decideBy` gets `409 TED-0060`. An `ALLOW` for a TED that arrives in time, with the operating window closed, rejects the TED with `TED-0020` or `TED-0021`. If TED Lerian cannot read the window, it answers `503 TED-0022` and makes no change.

`TED-0050`, `TED-0051` and `TED-0052` arrive in `rejection.code` of a `REJECTED` transfer. They are not error responses.

## When the partner cannot answer

***

A partner can time out, refuse a call or send an invalid answer. For `out`, `intra_out` and `intra_in`, the `posture` of the route then decides:

* `CLOSED` rejects the transfer with `TED-0051`.
* `OPEN` lets the transfer go on without screening. The check shows `origin` `FALLBACK` and the `cause`.

`posture.default` sets the posture. Optional `bands` set it by amount: the first band whose `upTo` is at or above the amount decides.

TED Lerian rejects the transfer with `TED-0051`, whatever the posture, in these cases:

* The route is not set.
* The credential bundle is missing or cannot be read.
* `ANTIFRAUD_SECRETS_BACKEND` is empty.
* The address is one that TED Lerian does not call, or its TLS certificate does not verify.

If the secret manager does not answer, the posture decides.

The `in` flow takes no posture. For a received TED, an answer that does not come from the partner does not credit or return the TED. TED Lerian sends it to manual review.

## Received TEDs

***

With the `in` flow on, TED Lerian does not credit a received TED before the partner answers.

| Answer | Result |
| - | - |
| `ALLOW` | TED Lerian credits the customer. |
| `REVIEW` | The TED waits in `MANUAL_REVIEW` with `reviewReason` `FRAUD_REVIEW`. Nothing is credited. |
| `DENY` | TED Lerian returns the TED to the sending institution with the official reason `9` (fraud). The event `incoming_return.requested` carries `fraudCheck`. |
| No answer from the partner | The TED waits in `MANUAL_REVIEW` with `reviewReason` `FRAUD_CHECK_UNAVAILABLE`. An operator decides. |

A TED in `FRAUD_REVIEW` ends in one of these ways:

1. **The partner decides.** The partner calls the [resolve operation](https://docs.lerian.studio/en/reference/interfaces/ted-lerian/apply-a-fraud-analysis-decision-to-an-incoming-ted.md). On `ALLOW`, TED Lerian credits the TED. On `DENY`, it returns the TED with reason `9`.
2. **An operator resolves the review.** The operator uses `resolve-review`, as for any other manual review.
3. **`decideBy` passes.** `decideBy` is 15 minutes before the 60 minute regulatory deadline, that is 45 minutes after interbank settlement. TED Lerian marks the open review `EXPIRED`. With `antifraud.in.auto_return_enabled` on, it returns the TED with reason `9`. With it off, the TED stays with the operator.

The resolve operation answers `409 TED-0060` in these cases:

* The TED does not wait for this analysis.
* The `analysisId` differs.
* Another decision is on record.
* `decideBy` has passed.
* A `DENY` arrives after an operator's credit of the TED may have reached the ledger.

Do not repeat a `409`. A `503` means TED Lerian did not finish. Repeat it with the same `X-Idempotency`. `TED-0053` means the decision arrived before TED Lerian recorded the analysis, or the same key is still in progress. With `TED-1000` or `TED-0080`, the decision stays on record, and the repeat finishes it.

## Turn a check on

***

Write the settings in the `plugin-br-ted-lerian.policy` namespace through the [systemplane](https://docs.lerian.studio/en/reference/platform/systemplane/overview.md) admin API of TED Lerian, on the `/v1/system` plane. The `editor` role writes them. For each flow:

| Key | Meaning |
| - | - |
| `antifraud.<flow>.enabled` | Turns the check on. The default is off. |
| `antifraud.<flow>.route` | The partner route, as one JSON object. `{}` means not set. TED Lerian checks the route when you write it. |
| `antifraud.<flow>.review_window_minutes` | How long the partner has to decide a `REVIEW`, in minutes, from 10 to 30. The default is 30. The `in` flow has none. |
| `antifraud.in.auto_return_enabled` | Returns an expired review of a received TED with reason `9`. The default is off. |

The route holds these fields:

| Field | Meaning |
| - | - |
| `endpoint` | The address that receives the evaluation. |
| `feedbackEndpoint` | Optional. The address that receives the outcome of each screened sent TED and internal transfer. |
| `auth` | `mode` (`hmac-v1`, `lerian-m2m` or `oauth2-client-credentials`). The two token modes also need `tokenUrl` and `clientId`. `oauth2-client-credentials` accepts `scopes`. Optional `staticHeaders` apply to every mode. |
| `syncBudgetMs` | The longest wait for one evaluation, retries included. From 100 to 4000. |
| `attemptTimeoutMs` | The time limit of one attempt. It cannot be more than `syncBudgetMs`. |
| `maxAttempts` | The number of attempts, 1 or more. |
| `posture` | `default` (`OPEN` or `CLOSED`) is required, and `bands` are optional. The `in` flow refuses a `posture`. |

```json theme={null}
{
  "endpoint": "https://antifraud.example.com/evaluate",
  "feedbackEndpoint": "https://antifraud.example.com/feedback",
  "auth": { "mode": "hmac-v1" },
  "syncBudgetMs": 3000,
  "attemptTimeoutMs": 1500,
  "maxAttempts": 2,
  "posture": { "default": "CLOSED" }
}
```

TED Lerian calls an `endpoint`, `feedbackEndpoint` or `tokenUrl` only when it is `https` on a public address, unless its host is in `ANTIFRAUD_ALLOWED_PRIVATE_HOSTS`. When you write a route that does not meet this rule, TED Lerian refuses it. See the [environment variables](https://docs.lerian.studio/en/interfaces/ted-lerian/ted-lerian-environment-variables.md#antifraud-partner).

In BYOC, TED Lerian reads a credential bundle from the secret manager that `ANTIFRAUD_SECRETS_BACKEND` names. The path is `tenants/{ENVIRONMENT_NAME}/single-tenant/plugin-br-ted-lerian/external/antifraud-<flow>/credentials`. `<flow>` is `out`, `intra-out`, `intra-in` or `in`. With `vault`, the path sits under the KV v2 mount `secret`. The bundle accepts these keys:

* `secret`: the client secret, or the HMAC secret for `hmac-v1`. It is required for every auth mode.
* `certPem` and `keyPem`: an optional client certificate pair. Send both or neither.
* `header.<Name>`: an optional header that TED Lerian sends on each call.

## What the partner receives

***

With a flow on, TED Lerian sends the partner these data of both parties:

* The full name.
* The CPF or CNPJ.
* The ISPB, the branch, the account and the account type.

It also sends the amount and the purpose code.

* **Sent TEDs and internal transfers:** the description and the `metadata` of the transfer. The payer side also gets `clientReference`.
* **Received TEDs:** the description that the sending institution wrote, the `nuop`, the STR control number and the settlement time.

## What the partner implements

***

TED Lerian sends each evaluation as a `POST` of a JSON body to the `endpoint` of the route. The headers are:

| Header | Value |
| - | - |
| `Idempotency-Key` | The key of the evaluation. The same key returns the same analysis. |
| `X-Lerian-Decide-By` | The deadline of the decision, in RFC 3339 UTC. |
| `X-Lerian-Payload-Schema` | `ted-out.v1` for the payer side of a TED or an internal transfer. `ted-in.v1` for the payee side and for a received TED. |

Each body has `transferId`, `flow` (`EXTERNAL` or `INTRA`), `amount`, `currency` (`BRL`), `payer`, `payee` and `createdAt`. It also has `purposeCode`, without leading zeros, and `description` when they exist. `ted-out.v1` adds `clientReference` and `metadata`. `ted-in.v1` adds `metadata` on an internal transfer, and `messageCode`, `numCtrlStr`, `settledAt` and `nuop` on a received TED. Each party has `name`, `taxId`, `ispb`, `branch`, `account` and `accountType`. Sent TEDs and internal transfers also carry `personType`. The side with an account at your institution carries `accountId`.

TED Lerian signs each call with the `auth.mode` of the route:

* `hmac-v1`: `X-Webhook-Timestamp` has the Unix time in seconds. `X-Webhook-Signature` is `v1,sha256=` followed by the hex HMAC-SHA256 of `v1:<timestamp>.<raw body>` with the `secret`.
* `lerian-m2m` and `oauth2-client-credentials`: TED Lerian gets a token from `auth.tokenUrl` with the `clientId` and the `secret`. It sends the token as a bearer token.

The partner answers `200` with a JSON body:

| Field | Meaning |
| - | - |
| `decision` | `ALLOW`, `REVIEW` or `DENY`. |
| `analysisId` | 1 to 64 printable ASCII characters, with no space. Required with `REVIEW`. |
| `score`, `reasons` | Optional. TED Lerian does not decide by them. |

A `202` counts as `REVIEW`, and it needs an `analysisId`, unless the body says `DENY`. TED Lerian repeats a `408`, `429` or `5xx` with the same `Idempotency-Key`, within `syncBudgetMs` and `maxAttempts`. It does not repeat a `403`. In the token modes, when `maxAttempts` is 2 or more, it repeats a `401` once, after it gets a new token. Any status other than `200` or `202`, a body of more than 64 KiB, or a `REVIEW` with no valid `analysisId` is not a decision. TED Lerian handles it as an answer that did not come from the partner (see [When the partner cannot answer](#when-the-partner-cannot-answer)).

The partner sends the final decision of a `REVIEW` with these operations. Both need the `editor` role. An application with the `operator` role cannot call them.

| Operation | Permission |
| - | - |
| [`POST /v1/transfers/{transferId}/fraud-analysis/resolve`](https://docs.lerian.studio/en/reference/interfaces/ted-lerian/apply-a-fraud-analysis-decision-to-a-transfer) | `transfers:resolve-fraud-analysis` |
| [`POST /v1/incoming-transfers/{incomingTransferId}/resolve`](https://docs.lerian.studio/en/reference/interfaces/ted-lerian/apply-a-fraud-analysis-decision-to-an-incoming-ted) | `incoming-transfers:resolve-fraud-check` |

The body is `analysisId`, `decision` (`ALLOW` or `DENY`) and optional `reasons`. `X-Idempotency` is required, and its value is `<analysisId>:<decision>`. The `reasons` of a resolution replace the reasons of the evaluation on the check. The `score` stays.

When the route of the `out`, `intra_out` or `intra_in` flow has a `feedbackEndpoint`, TED Lerian reports the outcome of each screened sent TED and internal transfer. It sends no feedback for received TEDs. The body has `evaluationKey`, `analysisId`, `decisionOrigin` (`PROVIDER` or `FALLBACK`), `outcome` and `occurredAt`. `outcome` is `SETTLED`, `REJECTED`, `CANCELLED`, `EXPIRED` or `RETURNED`. The `Idempotency-Key` is the evaluation key, a colon and the outcome. The feedback needs `OUTBOX_ENABLED` set to `true`. Without it, the screening works and TED Lerian sends no feedback.


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