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

# Detection Rules

> Create, edit, activate, and pause custom detection rules

## Overview

Custom rules allow your institution to define institution-specific detection logic beyond the 9 mandatory CBN rules. Rules are evaluated during transaction screening and can trigger `REVIEW`, `ESCALATE`, or `BLOCK` outcomes.

## Common Workflows

**New rule rollout:** A compliance officer drafts a rule → Tests it against historical transactions → Activates it → Monitors false positive rate → Tunes thresholds.

**Emergency pause:** A rule starts generating too many false positives during a holiday period → A `BANK_ADMIN` immediately pauses the rule → Investigates and updates thresholds → Re-activates.

## Permissions

| Action                  | Who Can Do It                      |
| ----------------------- | ---------------------------------- |
| Create, update rules    | `BANK_ADMIN`, `COMPLIANCE_OFFICER` |
| Activate, pause, delete | `BANK_ADMIN`                       |
| View rules              | All authenticated users            |

## Rule Statuses

| Status     | Meaning                           |
| ---------- | --------------------------------- |
| `DRAFT`    | Created but not yet active        |
| `ACTIVE`   | Currently evaluating transactions |
| `PAUSED`   | Temporarily disabled              |
| `ARCHIVED` | Permanently retired               |

## Endpoints

| Method   | Endpoint                     | Description           |
| -------- | ---------------------------- | --------------------- |
| `POST`   | `/api/v1/rules`              | Create a custom rule  |
| `GET`    | `/api/v1/rules`              | List all rules        |
| `GET`    | `/api/v1/rules/:id`          | Retrieve rule details |
| `PATCH`  | `/api/v1/rules/:id`          | Update a rule         |
| `PATCH`  | `/api/v1/rules/:id/activate` | Activate a rule       |
| `PATCH`  | `/api/v1/rules/:id/pause`    | Pause a rule          |
| `DELETE` | `/api/v1/rules/:id`          | Delete a custom rule  |

> **Note:** CBN mandatory rules (`ruleType: "CBN_BUILTIN"`) cannot be edited, paused, or deleted. They appear in listings for reference.

***

### Create Rule

Define a new custom detection rule with conditions, logic, and actions.

**Request Body**

| Field           | Type     | Required | Description                              |
| --------------- | -------- | -------- | ---------------------------------------- |
| `name`          | `string` | ✅        | Human-readable rule name                 |
| `description`   | `string` | ✅        | What the rule detects                    |
| `ruleType`      | `string` | ✅        | Always `"CUSTOM"` for user-created rules |
| `configuration` | `object` | ✅        | Rule logic (see below)                   |
| `scoreModifier` | `number` | ✅        | Risk score to add (0–100)                |

**Configuration Object**

| Field            | Type       | Required | Description                                |
| ---------------- | ---------- | -------- | ------------------------------------------ |
| `conditions`     | `array`    | ✅        | List of condition objects                  |
| `conditionLogic` | `string`   | ✅        | `"AND"` or `"OR"`                          |
| `outcome`        | `string`   | ✅        | `"REVIEW"`, `"ESCALATE"`, or `"BLOCK"`     |
| `riskScore`      | `number`   | ✅        | Base risk score                            |
| `actions`        | `string[]` |          | Array of actions like `["NOTIFY_OFFICER"]` |

**Condition Object**

| Field      | Type     | Description                                                            |
| ---------- | -------- | ---------------------------------------------------------------------- |
| `field`    | `string` | `amount`, `channel`, `type`, `narration`, `senderName`, `receiverName` |
| `operator` | `string` | `GREATER_THAN`, `LESS_THAN`, `EQUALS`, `CONTAINS`, `REGEX_MATCH`       |
| `value`    | `any`    | Value to compare against                                               |

**Example Request**

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST /v1/rules \
    -H "Authorization: Bearer <access_token>" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "High-Value ATM Withdrawal",
      "description": "Flag ATM withdrawals over ₦500,000",
      "ruleType": "CUSTOM",
      "configuration": {
        "conditions": [
          { "field": "amount", "operator": "GREATER_THAN", "value": 500000 },
          { "field": "channel", "operator": "EQUALS", "value": "ATM" }
        ],
        "conditionLogic": "AND",
        "outcome": "REVIEW",
        "riskScore": 45,
        "actions": ["NOTIFY_OFFICER"]
      },
      "scoreModifier": 45
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch("/v1/rules", {
    method: "POST",
    headers: {
      Authorization: "Bearer <access_token>",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name: "High-Value ATM Withdrawal",
      description: "Flag ATM withdrawals over ₦500,000",
      ruleType: "CUSTOM",
      configuration: {
        conditions: [
          { field: "amount", operator: "GREATER_THAN", value: 500000 },
          { field: "channel", operator: "EQUALS", value: "ATM" },
        ],
        conditionLogic: "AND",
        outcome: "REVIEW",
        riskScore: 45,
        actions: ["NOTIFY_OFFICER"],
      },
      scoreModifier: 45,
    }),
  });
  const result = await response.json();
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "/v1/rules",
      headers={
          "Authorization": "Bearer <access_token>",
          "Content-Type": "application/json"
      },
      json={
          "name": "High-Value ATM Withdrawal",
          "description": "Flag ATM withdrawals over ₦500,000",
          "ruleType": "CUSTOM",
          "configuration": {
              "conditions": [
                  {"field": "amount", "operator": "GREATER_THAN", "value": 500000},
                  {"field": "channel", "operator": "EQUALS", "value": "ATM"}
              ],
              "conditionLogic": "AND",
              "outcome": "REVIEW",
              "riskScore": 45,
              "actions": ["NOTIFY_OFFICER"]
          },
          "scoreModifier": 45
      }
  )
  result = response.json()
  ```
</CodeGroup>

**Example Response -201 Created**

```json theme={null}
{
  "success": true,
  "data": {
    "id": "rule_789xyz",
    "tenantId": "660e8400-e29b-41d4-a716-446655440001",
    "name": "High-Value ATM Withdrawal",
    "description": "Flag ATM withdrawals over ₦500,000",
    "ruleType": "CUSTOM",
    "status": "DRAFT",
    "marbleScenarioId": null,
    "configuration": {
      "conditions": [
        { "field": "amount", "operator": "GREATER_THAN", "value": 500000 },
        { "field": "channel", "operator": "EQUALS", "value": "ATM" }
      ],
      "conditionLogic": "AND",
      "outcome": "REVIEW",
      "riskScore": 45,
      "actions": ["NOTIFY_OFFICER"]
    },
    "scoreModifier": 45,
    "version": 1,
    "createdBy": "550e8400-e29b-41d4-a716-446655440000",
    "activatedAt": null,
    "createdAt": "2026-05-16T10:00:00Z",
    "updatedAt": "2026-05-16T10:00:00Z"
  }
}
```

***

### List Rules

Retrieve all rules including CBN built-ins and custom rules.

**Query Parameters**

| Parameter  | Type      | Default | Description                             |
| ---------- | --------- | ------- | --------------------------------------- |
| `status`   | `string`  | -       | `DRAFT`, `ACTIVE`, `PAUSED`, `ARCHIVED` |
| `ruleType` | `string`  | -       | `CUSTOM`, `CBN_BUILTIN`                 |
| `page`     | `integer` | `1`     | Page number                             |
| `limit`    | `integer` | `20`    | Items per page                          |

**Example Request**

```bash theme={null}
curl -X GET "/v1/rules?status=ACTIVE&page=1&limit=20" \
  -H "Authorization: Bearer <access_token>"
```

**Example Response -200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "rule_789xyz",
        "name": "High-Value ATM Withdrawal",
        "ruleType": "CUSTOM",
        "status": "ACTIVE",
        "version": 1,
        "createdAt": "2026-05-16T10:00:00Z"
      },
      {
        "id": "CBN-001",
        "name": "Cash Threshold",
        "ruleType": "CBN_BUILTIN",
        "status": "ACTIVE",
        "version": 1,
        "createdAt": "2024-01-01T00:00:00Z"
      }
    ],
    "total": 8,
    "page": 1,
    "limit": 20,
    "totalPages": 1
  }
}
```

***

### Get Rule Detail

Retrieve the full configuration of a single rule.

**Path Parameters**

| Parameter | Type     | Description           |
| --------- | -------- | --------------------- |
| `id`      | `string` | Rule UUID or CBN code |

**Example Request**

```bash theme={null}
curl -X GET /v1/rules/rule_789xyz \
  -H "Authorization: Bearer <access_token>"
```

**Example Response -200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "id": "rule_789xyz",
    "tenantId": "660e8400-e29b-41d4-a716-446655440001",
    "name": "High-Value ATM Withdrawal",
    "description": "Flag ATM withdrawals over ₦500,000",
    "ruleType": "CUSTOM",
    "status": "ACTIVE",
    "marbleScenarioId": null,
    "configuration": {
      "conditions": [
        { "field": "amount", "operator": "GREATER_THAN", "value": 500000 },
        { "field": "channel", "operator": "EQUALS", "value": "ATM" }
      ],
      "conditionLogic": "AND",
      "outcome": "REVIEW",
      "riskScore": 45,
      "actions": ["NOTIFY_OFFICER"]
    },
    "scoreModifier": 45,
    "version": 1,
    "createdBy": "550e8400-e29b-41d4-a716-446655440000",
    "activatedAt": "2026-05-16T11:00:00Z",
    "createdAt": "2026-05-16T10:00:00Z",
    "updatedAt": "2026-05-16T10:00:00Z"
  }
}
```

***

### Update Rule

Modify an existing custom rule. CBN built-ins cannot be updated.

**Path Parameters**

| Parameter | Type     | Description |
| --------- | -------- | ----------- |
| `id`      | `string` | Rule UUID   |

**Request Body**

All fields optional. Same structure as Create Rule.

**Example Request**

```bash theme={null}
curl -X PATCH /v1/rules/rule_789xyz \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "High-Value ATM Withdrawal (Updated)",
    "configuration": {
      "conditions": [
        { "field": "amount", "operator": "GREATER_THAN", "value": 750000 }
      ],
      "conditionLogic": "AND",
      "outcome": "REVIEW",
      "riskScore": 50,
      "actions": ["NOTIFY_OFFICER"]
    },
    "scoreModifier": 50
  }'
```

**Example Response -200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "id": "rule_789xyz",
    "name": "High-Value ATM Withdrawal (Updated)",
    "configuration": {
      "conditions": [
        { "field": "amount", "operator": "GREATER_THAN", "value": 750000 }
      ],
      "conditionLogic": "AND",
      "outcome": "REVIEW",
      "riskScore": 50,
      "actions": ["NOTIFY_OFFICER"]
    },
    "scoreModifier": 50,
    "version": 2,
    "updatedAt": "2026-05-16T11:00:00Z"
  }
}
```

<Warning>
  Updating an active rule does not automatically pause it. Consider pausing
  first if the change significantly alters detection logic.
</Warning>

***

### Activate Rule

Enable a drafted or paused custom rule.

**Path Parameters**

| Parameter | Type     | Description |
| --------- | -------- | ----------- |
| `id`      | `string` | Rule UUID   |

**Example Request**

```bash theme={null}
curl -X PATCH /v1/rules/rule_789xyz/activate \
  -H "Authorization: Bearer <access_token>"
```

**Example Response -200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "id": "rule_789xyz",
    "status": "ACTIVE",
    "activatedAt": "2026-05-16T12:00:00Z",
    "updatedAt": "2026-05-16T12:00:00Z"
  }
}
```

***

### Pause Rule

Temporarily disable an active rule without deleting it.

**Path Parameters**

| Parameter | Type     | Description |
| --------- | -------- | ----------- |
| `id`      | `string` | Rule UUID   |

**Example Request**

```bash theme={null}
curl -X PATCH /v1/rules/rule_789xyz/pause \
  -H "Authorization: Bearer <access_token>"
```

**Example Response -200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "id": "rule_789xyz",
    "status": "PAUSED",
    "updatedAt": "2026-05-16T12:05:00Z"
  }
}
```

***

### Delete Rule

Permanently remove a custom rule. This action cannot be undone.

**Path Parameters**

| Parameter | Type     | Description |
| --------- | -------- | ----------- |
| `id`      | `string` | Rule UUID   |

**Example Request**

```bash theme={null}
curl -X DELETE /v1/rules/rule_789xyz \
  -H "Authorization: Bearer <access_token>"
```

**Example Response -200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "id": "rule_789xyz",
    "deleted": true
  }
}
```

<Warning>
  Deleting a rule removes it from all future screenings. Historical transactions
  screened by this rule retain their original verdicts.
</Warning>
