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

# Criando uma regra a partir de uma política

> Transforme uma frase de política escrita em uma regra do Tracer em produção: mapeie para campos, crie como rascunho, ensaie em uma conta de teste, ative-a e altere ou desative depois.

Um analista de compliance entrega uma frase: *"recuse compras com cartão não presente acima de R\$ 5.000 vindas de um dispositivo que nunca vimos antes."*

Este guia transforma essa frase em uma regra que avalia do jeito que a política diz. Você ensaia essa regra onde ela não alcança ninguém, depois a coloca em produção.

**O que muda na sua operação:** a política deixa de viver em um chamado e passa a viver em um endpoint. A pessoa que escreveu a frase consegue ler a regra de volta. O ensaio passa pela avaliação real, em vez de uma planilha. Cada alteração na regra deixa um evento de auditoria registrado.

> [!TIP]
>
> **Para quem é este guia?** Analistas de risco e fraude que criam regras, e os desenvolvedores que conectam os campos da política à requisição de validação. Os passos 1 e 2 são sobre a política. Os passos 3 a 8 são chamadas de API.

## Antes de começar

***

* [ ] O Tracer em execução e acessível, com uma chave de API. Veja [Primeiros passos](https://docs.lerian.studio/pt/products/tracer/getting-started.md)
* [ ] As variáveis CEL (Uma linguagem de expressão leve para escrever regras de negócio, por exemplo, 'if transaction amount > 10000 then REVIEW'. O Tracer usa CEL para regras de validação.) e o modelo de scope. Veja [Motor de regras](https://docs.lerian.studio/pt/products/tracer/rule-engine.md)
* [ ] Um id de conta de teste para o qual você pode enviar validações, que nenhum tráfego de cliente usa
* [ ] A frase da política, anotada, com quem a escreveu disponível para uma pergunta

Todas as chamadas abaixo enviam a chave de API como `X-API-Key`.

***

## Passo 1: Mapeie a frase para campos

***

Uma regra lê o que a requisição de validação carrega. Divida a frase cláusula por cláusula e coloque cada uma na coluna a que pertence.

| Cláusula na política | De onde vem o valor | Lida como |
| - | - | - |
| "compras com cartão" | `scheme`, o esquema de pagamento | `CARD` |
| "acima de R\$ 5.000" | `amount` e `asset` na requisição | `amount`, `asset` |
| "cartão não presente" | `subType`, texto livre que sua integração define | `subType` |
| "um dispositivo que nunca vimos antes" | metadados (Informações adicionais de chave-valor anexadas a entidades como contas ou transações, como IDs externos, números de referência ou códigos de departamento.), que sua integração define | `metadata.deviceFirstSeen` |

Os dois primeiros são campos que o Tracer define. Os dois últimos são campos que sua integração precisa enviar. O Tracer não tem opinião sobre o que significam "cartão não presente" ou "dispositivo novo". Essa é a pergunta para levar de volta a quem escreveu a política: *qual flag no nosso payload indica que o dispositivo é novo?*

> [!NOTE]
>
> `subType` chega às expressões em minúsculas, então `"card_not_present"` é a forma usada na comparação. Se sua integração já usa `subType` para outra coisa, carregue o modo de entrada em `metadata` e compare por ali. Os dois funcionam do mesmo jeito em uma expressão.

Para a lista completa de variáveis e os campos que cada mapa de contexto carrega, veja [Motor de regras](https://docs.lerian.studio/pt/products/tracer/rule-engine.md#expressions).

***

## Passo 2: Divida a regra entre scope e expressão

***

Duas coisas naquela tabela (o esquema de pagamento e a conta) são coisas que o Tracer pode filtrar antes de uma expressão ser executada. Elas pertencem ao `scopes` da regra. As comparações de valor pertencem à `expression`.

**Scope**, que decide *se a regra é considerada ou não*:

```json theme={null}
"scopes": [{ "scheme": "CARD" }]
```

**Expressão**, que decide *se a regra dispara*:

```cel theme={null}
subType == "card_not_present" && asset == "BRL" && amount > 5000 && metadata.deviceFirstSeen == true
```

Leia a expressão ao lado da frase. `amount > 5000` é estritamente maior, então uma transação de exatamente `5000.00` não a dispara. Confira isso com a política antes de continuar, porque "acima de" e "a partir de" são regras diferentes.

Uma regra que lê uma chave de metadados que a requisição não carrega não corresponde, e as outras regras continuam sendo executadas. Por isso, a expressão acima não precisa de um teste de presença para `deviceFirstSeen`. Veja [Motor de regras](https://docs.lerian.studio/pt/products/tracer/rule-engine.md#expressions).

> [!WARNING]
>
> Não repita condições de scope dentro da expressão. Ter `scheme == "CARD"` nos dois lugares não está errado. Isso deixa dois lugares para editar quando a política muda, e a expressão é a que precisa percorrer todo o ciclo de vida para ser editada.

***

## Passo 3: Crie a regra como rascunho

***

`POST /v1/rules` cria a regra em `DRAFT`. Um rascunho nunca chega à avaliação, então nada que você faz aqui alcança o tráfego.

```bash theme={null}
curl -X POST http://localhost:4020/v1/rules \
  -H "X-API-Key: your-secure-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Deny CNP card purchases above BRL 5,000 from a new device",
    "description": "Compliance policy 2026-14, approved 2026-07-20",
    "expression": "subType == \"card_not_present\" && asset == \"BRL\" && amount > 5000 && metadata.deviceFirstSeen == true",
    "action": "DENY",
    "scopes": [
      { "scheme": "CARD", "accountId": "8a1f2c34-5d6e-4a7b-8c9d-0e1f2a3b4c5d" }
    ]
  }'
```

O `accountId` nesse scope é sua conta de teste. É isso que mantém o passo 4 fora do tráfego de clientes. O passo 5 o remove.

Um `201` responde com a regra armazenada:

```json theme={null}
{
  "ruleId": "4d9c2e70-8b1a-4f36-9c07-5e2a1b3d4f68",
  "name": "Deny CNP card purchases above BRL 5,000 from a new device",
  "description": "Compliance policy 2026-14, approved 2026-07-20",
  "expression": "subType == \"card_not_present\" && asset == \"BRL\" && amount > 5000 && metadata.deviceFirstSeen == true",
  "action": "DENY",
  "scopes": [
    { "accountId": "8a1f2c34-5d6e-4a7b-8c9d-0e1f2a3b4c5d", "transactionType": "CARD", "scheme": "CARD" }
  ],
  "status": "DRAFT",
  "createdAt": "2026-07-31T11:04:12.318Z",
  "updatedAt": "2026-07-31T11:04:12.318Z"
}
```

O Tracer preserva a caixa e os espaços internos do nome. Ele remove os espaços do início e do fim antes de armazenar. A unicidade do nome da regra diferencia maiúsculas de minúsculas dentro do contexto derivado dos scopes da regra. Assim, `FraudRule` e `fraudrule` são nomes diferentes, e o mesmo nome pode coexistir em contextos diferentes.

Pegue o `ruleId` da resposta. Esse é o identificador usado por todas as chamadas abaixo. Veja [Criar uma regra](https://docs.lerian.studio/pt/reference/products/tracer/create-rule.md).

A expressão é compilada nessa chamada, então uma expressão que não pode ser executada nunca se torna um rascunho. Um erro de sintaxe responde `0340`, uma expressão que não retorna um booleano responde `0341`, e uma cujo custo estimado está acima do `CEL_COST_LIMIT` responde `0342`.

***

## Passo 4: Ensaie em uma conta que mais ninguém usa

***

O scope que você definiu é o que mantém o ensaio contido. A regra chega à avaliação apenas para transações naquela única conta de teste. A ativação a coloca na frente exatamente do tráfego que você enviar.

**1. Ative a regra com o scope definido**

```bash theme={null}
curl -X POST http://localhost:4020/v1/rules/4d9c2e70-8b1a-4f36-9c07-5e2a1b3d4f68/activate \
  -H "X-API-Key: your-secure-api-key"
```

A resposta retorna com `status: "ACTIVE"` e um `activatedAt`. Veja [Ativar uma regra](https://docs.lerian.studio/pt/reference/products/tracer/activate-rule.md).

**2. Envie uma transação que a política deve recusar**

```bash theme={null}
TS=$(date -u +%Y-%m-%dT%H:%M:%SZ)

curl -X POST http://localhost:4020/v1/validations \
  -H "X-API-Key: your-secure-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "requestId": "c1a7f402-6b93-4d5e-8f10-2a4c6e8b0d31",
    "scheme": "CARD",
    "subType": "card_not_present",
    "amount": "7500.00",
    "asset": "BRL",
    "transactionTimestamp": "'"$TS"'",
    "account": {
      "accountId": "8a1f2c34-5d6e-4a7b-8c9d-0e1f2a3b4c5d",
      "type": "checking",
      "status": "active"
    },
    "metadata": {
      "deviceFirstSeen": true
    }
  }'
```

Veja [Validar uma transação](https://docs.lerian.studio/pt/reference/products/tracer/validate-transaction.md).

**3. Leia a decisão**

```json theme={null}
{
  "validationId": "b7e3d190-4c25-4e8f-9a16-3d5f7b0c2e41",
  "requestId": "c1a7f402-6b93-4d5e-8f10-2a4c6e8b0d31",
  "decision": "DENY",
  "matchedRuleIds": ["4d9c2e70-8b1a-4f36-9c07-5e2a1b3d4f68"],
  "evaluatedRuleIds": ["4d9c2e70-8b1a-4f36-9c07-5e2a1b3d4f68"],
  "reason": "Rule matched with DENY action",
  "totalRulesLoaded": 1,
  "truncated": false,
  "limitUsageDetails": [],
  "processingTimeMs": 6.1,
  "evaluatedAt": "2026-07-31T11:09:44.207Z"
}
```

Seu `ruleId` em `matchedRuleIds` significa que o ensaio passou.

**4. Envie os casos que não devem disparar**

Altere um valor por vez e repita a chamada com um `requestId` novo: `"amount": "5000.00"` para o valor limite, `"deviceFirstSeen": false` para um dispositivo conhecido, `"subType": "purchase"` para uma venda com cartão presente. Cada uma deve voltar sem o seu `ruleId` em `matchedRuleIds`.

> [!WARNING]
>
> Envie um `requestId` novo a cada tentativa. O `requestId` é a chave de idempotência. Repita um e o Tracer responde `200` com a decisão que já registrou para essa chave. A alteração que você acabou de fazer então parece não ter feito nada.

> [!NOTE]
>
> Um ensaio é uma validação real. Ele armazena um registro de decisão e grava um evento de auditoria, e uma decisão `ALLOW` consome os limites de gastos que cobrem aquela conta. É por isso que a conta de teste importa.

Se o seu `ruleId` não estiver em `matchedRuleIds`, siga esta ordem. A regra está `ACTIVE` (`GET /v1/rules/{id}`)? A transação corresponde ao scope que você definiu? Os valores que você enviou satisfazem a expressão?

***

## Passo 5: Coloque em produção

***

Ir para produção significa uma única edição: remover a conta de teste do scope para que a regra se aplique à população que a política nomeia.

**1. Pare de avaliar a versão de ensaio**

Uma edição de scope não exige `INACTIVE`. Desativar primeiro faz a troca acontecer no momento que você controla e registra uma lacuna visível na trilha de auditoria. Cada instância serve regras a partir de uma cache que atualiza em um poll (a cada 10 segundos por padrão, `RULE_SYNC_POLL_INTERVAL_SECONDS`). Reserve essa janela para a desativação alcançar todas as instâncias. `GET /v1/rules` confirma o status armazenado, não que todas as instâncias já se atualizaram.

```bash theme={null}
curl -X POST http://localhost:4020/v1/rules/4d9c2e70-8b1a-4f36-9c07-5e2a1b3d4f68/deactivate \
  -H "X-API-Key: your-secure-api-key"
```

O status muda para `INACTIVE`. Veja [Desativar uma regra](https://docs.lerian.studio/pt/reference/products/tracer/deactivate-rule.md).

**2. Substitua o scope**

```bash theme={null}
curl -X PATCH http://localhost:4020/v1/rules/4d9c2e70-8b1a-4f36-9c07-5e2a1b3d4f68 \
  -H "X-API-Key: your-secure-api-key" \
  -H "Content-Type: application/json" \
  -d '{ "scopes": [{ "scheme": "CARD" }] }'
```

`scopes` substitui todo o array. Envie todos os objetos de scope que você quer que a regra mantenha. Veja [Atualizar uma regra](https://docs.lerian.studio/pt/reference/products/tracer/update-rule.md).

**3. Ative**

```bash theme={null}
curl -X POST http://localhost:4020/v1/rules/4d9c2e70-8b1a-4f36-9c07-5e2a1b3d4f68/activate \
  -H "X-API-Key: your-secure-api-key"
```

A ativação alcança a instância que atendeu essa chamada assim que ela é confirmada. Quando você roda várias instâncias atrás de um balanceador de carga, as outras pegam a mudança na próxima sincronização de regras (`RULE_SYNC_POLL_INTERVAL_SECONDS`, padrão `10`). A desativação percorre o mesmo caminho. Reserve essa mesma janela depois da ativação antes de considerar a regra em vigor em todas as instâncias.

**4. Confirme o que está em produção**

```http theme={null}
GET /v1/rules?status=ACTIVE&scheme=CARD&sort_by=updated_at
X-API-Key: {api_key}
```

A listagem responde "quais regras estão em vigor no tráfego de cartão agora". Veja [Listar regras](https://docs.lerian.studio/pt/reference/products/tracer/list-rules.md). Para uma única regra, `GET /v1/rules/{id}` retorna a expressão e os scopes como estão armazenados ([Recuperar uma regra](https://docs.lerian.studio/pt/reference/products/tracer/retrieve-rule.md)). As duas reportam o estado armazenado, não o que a cache de cada instância contém.

> [!NOTE]
>
> Uma validação carrega seu conjunto de regras uma vez, a partir da cache da instância que a atende, quando a chamada começa. Uma regra que é ativada enquanto uma validação está em andamento não faz parte dessa decisão. Decisões já registradas não mudam quando as regras mudam depois.

***

## Passo 6: Saiba onde sua regra fica entre as outras

***

Regras não carregam um campo de prioridade nem uma ordenação para configurar. Regras cujo scope corresponde a uma transação são avaliadas juntas. A decisão vem da ação mais restritiva que disparou: primeiro uma regra `DENY`, depois um limite de gastos excedido, depois `REVIEW`, depois `ALLOW`, depois o padrão configurado para quando nada corresponde. O array `matchedRuleIds` carrega toda regra que correspondeu, qualquer que seja a ação que ela tenha.

Duas consequências para a regra que você acabou de escrever:

* **Uma regra `ALLOW` não isenta ninguém de uma regra `DENY`.** Se a política tem uma exceção (clientes VIP, um lojista parceiro), coloque-a dentro da expressão `DENY`. Adicione-a como mais uma condição que torna a regra mais restrita:

  ```cel theme={null}
  subType == "card_not_present" && asset == "BRL" && amount > 5000 && metadata.deviceFirstSeen == true && metadata.customerTier != "vip"
  ```

  A regra mais restrita agora lê `metadata.customerTier`, e uma requisição que não carrega essa chave não corresponde a ela.

* **Sua regra entra no conjunto que toda transação correspondente avalia.** `MAX_RULES_PER_REQUEST` limita quantas regras uma validação avalia. Quando o conjunto é maior, a resposta reporta `truncated: true`. Veja [variáveis de ambiente](https://docs.lerian.studio/pt/products/tracer/tracer-environment-variables.md).

A tabela de precedência e o raciocínio por trás dela estão na página [Motor de regras](https://docs.lerian.studio/pt/products/tracer/rule-engine.md#evaluation-pattern).

***

## Passo 7: Altere a regra quando a política muda

***

O que você faz depende do campo, não do desempenho da regra.

| O que mudou | Como |
| - | - |
| Qualquer coisa dentro da expressão (o limite, o modo de entrada, o flag do dispositivo) | Desative, depois `POST /v1/rules/{id}/draft`, depois `PATCH` na expressão, depois ative |
| `DENY` se torna `REVIEW` | `PATCH /v1/rules/{id}` com a nova `action` |
| Nome, descrição ou scopes | `PATCH /v1/rules/{id}` |

> [!WARNING]
>
> A `expression` aceita uma edição apenas enquanto a regra está `DRAFT`. Enviar uma para uma regra em outro status responde `422` com o código de erro `0351`. Desativar não é suficiente por si só, porque `INACTIVE` não é `DRAFT`. Não pule `POST /v1/rules/{id}/draft`. Veja [Rascunhar uma regra](https://docs.lerian.studio/pt/reference/products/tracer/draft-rule.md).

Um `PATCH` é armazenado quando responde, e chega à avaliação na próxima sincronização de regras. Quando a mudança importa até o minuto, desative primeiro e ative de novo depois. Essa sequência também coloca uma lacuna visível na trilha de auditoria em que a regra não esteve em vigor. Um revisor vai procurar essa lacuna.

O ciclo de vida é um conjunto fechado de movimentos: `DRAFT` ativa ou é excluído. `ACTIVE` desativa. `INACTIVE` volta para `DRAFT`, volta para `ACTIVE`, ou é excluído. `DELETED` é o fim. Qualquer outra coisa responde `422` com o código de erro `0349`, incluindo uma solicitação para rascunhar uma regra que ainda está `ACTIVE`.

***

## Passo 8: Desative ou exclua a regra

***

**Para parar de aplicar sem perder nada**, desative. A regra mantém sua expressão, seus scopes e seu histórico. Ela não chega mais à avaliação, e `POST /v1/rules/{id}/activate` a traz de volta. Esse é o movimento para uma política suspensa, sazonal ou em revisão.

**Para removê-la**, exclua. Desative primeiro, porque você não pode excluir uma regra em `ACTIVE`:

```http theme={null}
DELETE /v1/rules/4d9c2e70-8b1a-4f36-9c07-5e2a1b3d4f68
X-API-Key: {api_key}
```

Um `204` responde em caso de sucesso. Veja [Excluir uma regra](https://docs.lerian.studio/pt/reference/products/tracer/delete-rule.md).

O que a exclusão remove:

* A regra para de responder em `GET /v1/rules/{id}`, que passa a retornar `404` com o código de erro `0347`.
* Ela não aparece mais em `GET /v1/rules`, e `DELETED` não é um valor que o filtro `status` aceita.
* `DELETED` é o fim do ciclo de vida. Nenhum endpoint move uma regra para fora dele. Uma regra excluída volta apenas como uma nova regra que você cria de novo.

O que a exclusão deixa para trás:

* A trilha de auditoria mantém o ciclo de vida da regra, e o evento `RULE_DELETED` carrega a definição (nome, descrição, expressão, ação, scopes) como ela estava no momento da exclusão. Veja [Auditoria e compliance](https://docs.lerian.studio/pt/products/tracer/audit-compliance.md).
* As decisões que a regra produziu mantêm seu `ruleId` em `matchedRuleIds`. Uma negação de seis meses atrás ainda a nomeia. Veja [Revisando uma transação negada](https://docs.lerian.studio/pt/products/tracer/reviewing-a-denied-transaction.md).
* O nome fica disponível de novo para uma nova regra no mesmo contexto.

> [!WARNING]
>
> Desative, depois leia a trilha de auditoria, depois exclua. Desativar é reversível em uma única chamada, e excluir não é reversível de forma alguma, então não há motivo para pular o estado intermediário.

***

## Armadilhas comuns

***

> [!WARNING]
>
> **O que costuma dar errado ao transformar uma política em regra:**
>
> * **"A regra está ACTIVE, mas não corresponde a nada."** Verifique o campo em que a política mais se apoia. Uma regra que lê `metadata.deviceFirstSeen` não corresponde a nada se sua integração nunca enviar essa chave. A regra está correta e o payload está incompleto.
> * **"Ela disparou em uma transação que a política isenta."** Uma regra `ALLOW` não sobrepõe uma `DENY`. Coloque a isenção dentro da expressão `DENY` (passo 6).
> * **"Meu segundo ensaio retornou a primeira decisão."** `requestId` é a chave de idempotência. Envie um novo UUID a cada tentativa.
> * **"O PATCH rejeitou minha expressão com 422."** A regra não estava em `DRAFT`. Coloque-a lá primeiro (passo 7).
> * **"O nome que enviei não é o nome que recebo de volta."** O Tracer remove apenas os espaços do início e do fim. Ele preserva a caixa e os espaços internos. Referencie a regra pelo `ruleId`.

### Códigos de erro

| Código | Status | O que alterar |
| - | - | - |
| `0340` | 400 | A expressão não é interpretada como CEL |
| `0341` | 400 | A expressão não retorna um booleano (`amount > 5000`, não `amount`) |
| `0342` | 422 | O custo estimado da expressão está acima do `CEL_COST_LIMIT` |
| `0347` | 404 | Nenhuma regra tem esse `ruleId`, ou ela foi excluída |
| `0349` | 422 | O ciclo de vida não permite esse movimento (por exemplo, excluir uma regra `ACTIVE`) |
| `0351` | 422 | Uma edição de expressão em uma regra que não está `DRAFT` |
| `0353` / `0355` / `0357` | 400 | `name`, `expression` ou uma `action` válida está faltando |
| `0354` / `0356` / `0359` | 400 | `name` acima de 255, `expression` acima de 5000, ou `description` acima de 1000 caracteres |
| `0358` | 400 | Um objeto de scope sem nenhum campo definido. Omita `scopes` inteiramente para uma regra global. Nunca envie `{}` |
| `0360` | 400 | Mais de 100 objetos de scope em uma regra |
| `0441` | 409 | Outra regra no mesmo contexto já usa esse nome |
| `0065` | 400 | O id no caminho não é um UUID |
| `0082` | 400 | Um filtro em `GET /v1/rules` carrega um valor que o endpoint não aceita |

A lista completa está em [Lista de erros do Tracer](https://docs.lerian.studio/pt/reference/products/tracer/tracer-error-list.md).

***

## Referência rápida

***

| Passo | Método | Endpoint |
| - | - | - |
| Criar como rascunho | POST | `/v1/rules` |
| Começar a avaliar | POST | `/v1/rules/{id}/activate` |
| Ensaiar ou verificar | POST | `/v1/validations` |
| Parar de avaliar | POST | `/v1/rules/{id}/deactivate` |
| Reabrir para edição | POST | `/v1/rules/{id}/draft` |
| Alterar campos | PATCH | `/v1/rules/{id}` |
| Ler uma regra | GET | `/v1/rules/{id}` |
| Ver o que está em produção | GET | `/v1/rules` |
| Remover | DELETE | `/v1/rules/{id}` |


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