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

# Criar uma regra a partir de uma política

> Transforme uma frase de política escrita em uma regra ativa do Tracer: mapeie para campos, crie como rascunho, ensaie em uma conta de teste, ative e mude ou aposente depois.

export const GMetadata = ({children}) => <Tooltip headline="Metadata" tip="Additional key-value information attached to entities like accounts or transactions — such as external IDs, reference numbers, or department codes." cta="See glossary" href="/en/glossary">
    {children}
  </Tooltip>;

export const GCEL = ({children}) => <Tooltip headline="CEL (Common Expression Language)" tip="A lightweight expression language for writing business rules — for example, 'if transaction amount > 10000 then REVIEW'. Tracer uses CEL for validation rules." cta="See glossary" href="/en/glossary">
    {children}
  </Tooltip>;

Um responsável por compliance entrega uma frase: *"negue compras com cartão sem presença física acima de BRL 5.000 a partir de um dispositivo que não vimos antes."* Este guia transforma essa frase em uma regra que avalia do jeito que a política lê, ensaia essa regra onde ela não alcança ninguém e a coloca no ar.

**O que muda na sua operação:** a política deixa de morar em um ticket e passa a morar em um endpoint. A pessoa que escreveu a frase consegue ler a regra de volta, o ensaio roda por avaliação real em vez de uma planilha, e cada mudança na regra deixa um evento de auditoria atrás de si.

<Tip>
  **Para quem é este guia?** Analistas de risco e fraude que escrevem regras, e os desenvolvedores que ligam os campos da política à requisição de validação. Os passos 1 e 2 tratam da política; os passos 3 a 8 são chamadas de API.
</Tip>

## Antes de começar

***

* [ ] Tracer em execução e acessível, com uma API key — veja [Primeiros passos](./getting-started.mdx)
* [ ] As variáveis <GCEL>CEL</GCEL> e o modelo de escopo — veja o [Motor de regras](./rule-engine.mdx)
* [ ] Um id de conta de teste para o qual você possa enviar validações, que nenhum tráfego de clientes use
* [ ] A frase da política, por escrito, com quem a escreveu disponível para uma pergunta

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

***

## Passo 1: Mapeie a frase sobre os campos

***

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

| Cláusula na política                 | De onde vem o valor                                        | Lê como                    |
| ------------------------------------ | ---------------------------------------------------------- | -------------------------- |
| "compras ... com cartão"             | `transactionType`, um enum do Tracer                       | `CARD`                     |
| "acima de BRL 5.000"                 | `amount` e `currency` na requisição                        | `amount`, `currency`       |
| "sem presença física"                | `subType`, texto livre que sua integração define           | `subType`                  |
| "um dispositivo que não vimos antes" | <GMetadata>metadata</GMetadata>, 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 "sem presença física" ou "dispositivo novo" significam. Essa é a pergunta para levar de volta a quem escreveu a política: *qual sinalizador do nosso payload diz que o dispositivo é novo?*

<Note>
  `subType` chega às expressões em minúsculas, então `"card_not_present"` é a forma contra a qual comparar. Se sua integração já usa `subType` para outra coisa, leve o modo de entrada em `metadata` e compare contra isso — os dois funcionam do mesmo jeito em uma expressão.
</Note>

Para a lista completa de variáveis e os campos que cada mapa de contexto carrega, veja o [Motor de regras](./rule-engine.mdx#expressões).

***

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

***

Duas coisas daquela tabela — o tipo de transação e a conta — são coisas que o Tracer consegue filtrar antes de uma expressão rodar. Elas pertencem aos `scopes` da regra. As comparações de valor pertencem à `expression`.

**Escopo**, que decide *se a regra é sequer considerada*:

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

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

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

Leia a expressão contra a frase: modo de entrada, limiar e o sinalizador do dispositivo. `amount > 5000` é estritamente maior, então uma transação de exatamente `5000.00` não dispara a regra — confira isso contra a política antes de seguir, porque "acima de" e "a partir de" são regras diferentes.

Uma regra que lê uma chave de metadata que a requisição não carrega não corresponde, e as outras regras continuam rodando. Por isso a expressão acima não precisa de um teste de presença para `deviceFirstSeen`; veja o [Motor de regras](./rule-engine.mdx#expressões).

<Warning>
  Não repita as condições de escopo dentro da expressão. `transactionType == "CARD"` nos dois lugares não está errado, mas deixa dois lugares para editar quando a política mudar — e a expressão é a que precisa de uma volta completa pelo ciclo de vida para ser editada.
</Warning>

***

## Passo 3: Crie a regra como rascunho

***

`POST /v1/rules` cria a regra em `DRAFT`. Um rascunho não é avaliado, então nada do que você fizer 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\" && amount > 5000 && metadata.deviceFirstSeen == true",
    "action": "DENY",
    "scopes": [
      { "transactionType": "CARD", "accountId": "8a1f2c34-5d6e-4a7b-8c9d-0e1f2a3b4c5d" }
    ]
  }'
```

O `accountId` nesse escopo é a sua conta de teste. É o 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\" && amount > 5000 && metadata.deviceFirstSeen == true",
  "action": "DENY",
  "scopes": [
    { "transactionType": "CARD", "accountId": "8a1f2c34-5d6e-4a7b-8c9d-0e1f2a3b4c5d" }
  ],
  "status": "DRAFT",
  "createdAt": "2026-07-31T11:04:12.318Z",
  "updatedAt": "2026-07-31T11:04:12.318Z"
}
```

O Tracer armazena o nome da regra em uma forma normalizada, então o `name` que ele devolve pode diferir da string que você enviou. Pegue o `ruleId` da resposta — esse é o identificador que toda chamada abaixo usa. Veja [Criar uma regra](/pt/reference/tracer/create-rule).

A expressão é compilada nesta chamada, então uma expressão que não consegue rodar nunca vira 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 de `CEL_COST_LIMIT` responde `0342`.

***

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

***

O escopo que você definiu é o que mantém o ensaio contido: a regra é considerada apenas para transações daquela única conta de teste, então ativá-la a coloca na frente de exatamente o tráfego que você enviar.

<Steps>
  <Step title="Ative a regra com escopo">
    ```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 volta com `status: "ACTIVE"` e um `activatedAt`. Veja [Ativar uma regra](/pt/reference/tracer/activate-rule).
  </Step>

  <Step title="Envie uma transação que a política deveria negar">
    ```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",
        "transactionType": "CARD",
        "subType": "card_not_present",
        "amount": "7500.00",
        "currency": "BRL",
        "transactionTimestamp": "'"$TS"'",
        "account": {
          "accountId": "8a1f2c34-5d6e-4a7b-8c9d-0e1f2a3b4c5d",
          "type": "checking",
          "status": "active"
        },
        "metadata": {
          "deviceFirstSeen": true
        }
      }'
    ```

    Veja [Validar uma transação](/pt/reference/tracer/validate-transaction).
  </Step>

  <Step title="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"
    }
    ```

    O seu `ruleId` em `matchedRuleIds` é o ensaio passando.
  </Step>

  <Step title="Envie os casos que não deveriam disparar">
    Mude um valor por vez e repita a chamada com um `requestId` novo: `"amount": "5000.00"` para o limite exato, `"deviceFirstSeen": false` para um dispositivo conhecido, `"subType": "purchase"` para uma venda com presença física. Cada uma deve voltar sem o seu `ruleId` em `matchedRuleIds`.
  </Step>
</Steps>

<Warning>
  Envie um `requestId` novo a cada tentativa. `requestId` é a chave de idempotência: repita um e o Tracer responde `200` com a decisão que ele já registrou para aquela chave, então a mudança que você acabou de fazer vai parecer não ter feito nada.
</Warning>

<Note>
  Um ensaio é uma validação real. Ele armazena um registro de decisão e escreve um evento de auditoria, e uma decisão `ALLOW` consome os limites de gasto que cobrem aquela conta. É por isso que a conta de teste importa.
</Note>

Se o seu `ruleId` não estiver em `matchedRuleIds`, percorra nesta ordem: a regra está `ACTIVE` (`GET /v1/rules/{id}`)?, a transação corresponde ao escopo que você definiu?, os valores que você enviou satisfazem a expressão?

***

## Passo 5: Coloque no ar

***

Entrar em produção significa uma edição: tire a conta de teste do escopo para que a regra se aplique à população que a política nomeia.

<Steps>
  <Step title="Pare de avaliar a versão de ensaio">
    Editar o escopo não exige `INACTIVE`. Desativar primeiro faz a troca surtir efeito em um momento que você controla e deixa uma lacuna visível no rastro de auditoria. Cada instância serve as regras a partir de um cache que ela atualiza por sondagem (a cada 10 segundos por padrão, `RULE_SYNC_POLL_INTERVAL_SECONDS`), então aguarde essa janela para a desativação alcançar todas as instâncias; `GET /v1/rules` confirma o status armazenado, não que cada instância já se atualizou.

    ```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 vai para `INACTIVE`. Veja [Desativar uma regra](/pt/reference/tracer/deactivate-rule).
  </Step>

  <Step title="Substitua o escopo">
    ```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": [{ "transactionType": "CARD" }] }'
    ```

    `scopes` substitui o array inteiro — envie cada objeto de escopo que você quer que a regra mantenha. Veja [Atualizar uma regra](/pt/reference/tracer/update-rule).
  </Step>

  <Step title="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 esta chamada assim que ela é confirmada. Quando você roda várias instâncias atrás de um balanceador, as outras pegam a mudança na próxima sincronização de regras (`RULE_SYNC_POLL_INTERVAL_SECONDS`, padrão `10`). A desativação viaja do mesmo jeito. Aguarde essa mesma janela depois de ativar antes de considerar a regra valendo em todas as instâncias.
  </Step>

  <Step title="Confirme o que está no ar">
    ```http theme={null}
    GET /v1/rules?status=ACTIVE&transaction_type=CARD&sort_by=updated_at
    X-API-Key: {api_key}
    ```

    A listagem responde "o que está valendo no tráfego de cartão agora" — veja [Listar regras](/pt/reference/tracer/list-rules). Para uma regra só, `GET /v1/rules/{id}` devolve a expressão e os escopos como estão armazenados ([Recuperar uma regra](/pt/reference/tracer/retrieve-rule)). Os dois retornam o estado armazenado, não o que o cache de cada instância guarda.
  </Step>
</Steps>

<Note>
  Uma validação carrega o conjunto de regras dela uma vez, do cache da instância que atende a chamada, quando a chamada começa. Uma regra que é ativada enquanto uma validação está em voo não faz parte daquela decisão, e decisões já registradas não mudam quando as regras mudam depois.
</Note>

***

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

***

As regras não têm campo de prioridade nem ordem para configurar. Regras cujo escopo corresponde a uma transação são avaliadas juntas, e a decisão vem da ação mais estrita que disparou: primeiro uma regra `DENY`, depois um limite de gasto excedido, depois `REVIEW`, depois `ALLOW`, depois o padrão configurado para quando não há correspondência. `matchedRuleIds` carrega cada regra que correspondeu, seja qual for a ação de cada uma.

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 — a exceção pertence dentro da expressão `DENY`, como mais uma condição que a torna mais estreita:

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

  Repare no que isso custa: a regra estreitada agora lê `metadata.customerTier`, e uma requisição que não carrega essa chave não corresponde a ela.

* **A sua regra entra no conjunto que cada 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](./tracer-environment-variables.mdx).

A tabela de precedência e o raciocínio por trás dela estão na página do [Motor de regras](./rule-engine.mdx#padrão-de-avaliação).

***

## Passo 7: Mude a regra quando a política mudar

***

O que você faz depende do campo, não de como a regra está indo.

| O que mudou                                                                                    | Como                                                                                    |
| ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| O limiar, o modo de entrada, o sinalizador do dispositivo — qualquer coisa dentro da expressão | Desative, depois `POST /v1/rules/{id}/draft`, depois `PATCH` na expressão, depois ative |
| `DENY` vira `REVIEW`                                                                           | `PATCH /v1/rules/{id}` com a nova `action`                                              |
| Nome, descrição ou escopos                                                                     | `PATCH /v1/rules/{id}`                                                                  |

<Warning>
  A `expression` aceita uma edição apenas enquanto a regra está em `DRAFT`. Enviar uma para uma regra em outro status responde `422` com o código de erro `0351` — desativar não basta sozinho, porque `INACTIVE` não é `DRAFT`. `POST /v1/rules/{id}/draft` é o passo que as pessoas esquecem; veja [Voltar uma regra para rascunho](/pt/reference/tracer/draft-rule).
</Warning>

Um `PATCH` fica armazenado quando responde, e chega à avaliação na próxima sincronização de regras. Quando a mudança importa ao minuto, desative primeiro e ative de novo depois — essa sequência ainda deixa uma lacuna visível no rastro de auditoria onde a regra não estava valendo, que é o que quem revisa vai procurar.

O ciclo de vida é um conjunto fechado de movimentos: `DRAFT` é ativado ou excluído; `ACTIVE` é desativado; `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` — inclusive um pedido para voltar para rascunho uma regra que ainda está `ACTIVE`.

***

## Passo 8: Aposente ou exclua a regra

***

**Para parar de valer sem perder nada**, desative. A regra mantém a expressão, os escopos e a história dela, para de ser avaliada, 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 — e só depois de desativar, porque uma regra em `ACTIVE` não pode ser excluída:

```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](/pt/reference/tracer/delete-rule).

O que a exclusão remove:

* A regra para de responder em `GET /v1/rules/{id}`, que devolve `404` com o código de erro `0347` a partir de então.
* 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 tira uma regra de lá — uma regra excluída volta apenas como uma regra nova que você crie de novo.

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

* O rastro 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, escopos — como ela estava na exclusão. Veja [Auditoria e compliance](./audit-compliance.mdx).
* As decisões que a regra produziu mantêm o `ruleId` dela em `matchedRuleIds`. Uma negativa de seis meses atrás ainda a nomeia — veja [Revisar uma transação negada](./reviewing-a-denied-transaction.mdx).
* O nome fica disponível de novo para uma regra nova no mesmo contexto.

<Warning>
  Desative, depois leia o rastro de auditoria, depois exclua. Desativar é reversível em uma chamada e excluir não é reversível de jeito nenhum, então não há razão para pular o estado intermediário.
</Warning>

***

## Erros comuns

***

<Warning>
  **O que costuma dar errado ao transformar uma política em regra:**

  * **"A regra está ACTIVE mas nada corresponde."** Verifique o campo em que a política mais se apoia. Uma regra que lê `metadata.deviceFirstSeen` não corresponde a nada se a sua integração nunca envia 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 anula uma `DENY`. Coloque a isenção dentro da expressão `DENY` (passo 6).
  * **"Meu segundo ensaio devolveu a primeira decisão."** `requestId` é a chave de idempotência. Envie um UUID novo a cada tentativa.
  * **"O PATCH rejeitou minha expressão com 422."** A regra não estava em `DRAFT`. Mova-a para lá primeiro (passo 7).
  * **"O nome que enviei não é o nome que recebo."** O Tracer armazena os nomes em uma forma normalizada. Referencie a regra pelo `ruleId`.
</Warning>

### Códigos de erro

| Código                   | Status | O que mudar                                                                                                         |
| ------------------------ | ------ | ------------------------------------------------------------------------------------------------------------------- |
| `0340`                   | 400    | A expressão não é analisada 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 de `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á em `DRAFT`                                                        |
| `0353` / `0355` / `0357` | 400    | Falta `name`, `expression` ou uma `action` válida                                                                   |
| `0354` / `0356` / `0359` | 400    | `name` acima de 255, `expression` acima de 5000, ou `description` acima de 1000 caracteres                          |
| `0358`                   | 400    | Um objeto de escopo sem nenhum campo definido — omita `scopes` por completo para uma regra global, nunca envie `{}` |
| `0360`                   | 400    | Mais de 100 objetos de escopo em uma regra                                                                          |
| `0441`                   | 409    | Outra regra no mesmo contexto já tem esse nome                                                                      |
| `0065`                   | 400    | O id no caminho não é um UUID                                                                                       |
| `0082`                   | 400    | Um filtro do `GET /v1/rules` leva um valor que o endpoint não aceita                                                |

A lista completa está na [Lista de erros do Tracer](/pt/reference/tracer/tracer-error-list).

***

## 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`      |
| Mudar campos         | PATCH  | `/v1/rules/{id}`            |
| Ler uma regra        | GET    | `/v1/rules/{id}`            |
| Ver o que está no ar | GET    | `/v1/rules`                 |
| Remover              | DELETE | `/v1/rules/{id}`            |
