> For the complete documentation index, see [llms.txt](https://docs.maetra.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.maetra.io/secure-api/rules.md).

# Rules

**Secure rules** define what a [scan](/secure-api/scanning-content.md) looks for. Each rule has a type (what it inspects), an action (`block`, `flag`, or `log`), a severity, and a scope (all agents, or specific ones). You can list, create, and update rules over the API.

### List rules

`GET /v1/secure/rules` — requires scope `secure:rules:read`.

{% tabs %}
{% tab title="cURL" %}

```bash
curl "https://api.maetra.io/v1/secure/rules" \
  -H "Authorization: Bearer $MAETRA_API_KEY"
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const res = await fetch("https://api.maetra.io/v1/secure/rules", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.MAETRA_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Maetra API ${res.status}`);
const data = await res.json();
console.log(data);
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
const res = await fetch("https://api.maetra.io/v1/secure/rules", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.MAETRA_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Maetra API ${res.status}`);
const data = (await res.json());
```

{% endtab %}

{% tab title="Python" %}

```python
import os, requests

res = requests.get(
    "https://api.maetra.io/v1/secure/rules",
    headers={"Authorization": f"Bearer {os.environ['MAETRA_API_KEY']}"},
)
res.raise_for_status()
print(res.json())
```

{% endtab %}

{% tab title="Rust" %}

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let key = std::env::var("MAETRA_API_KEY")?;
    let client = reqwest::Client::new();
    let res = client
        .get("https://api.maetra.io/v1/secure/rules")
        .bearer_auth(&key)
        .send()
        .await?;
    let data: serde_json::Value = res.json().await?;
    println!("{data:#}");
    Ok(())
}
```

{% endtab %}

{% tab title="C++" %}

```cpp
#include <curl/curl.h>
#include <cstdlib>
#include <string>

int main() {
    CURL* curl = curl_easy_init();
    std::string auth = "Authorization: Bearer " + std::string(std::getenv("MAETRA_API_KEY"));
    struct curl_slist* headers = nullptr;
    headers = curl_slist_append(headers, auth.c_str());
    curl_easy_setopt(curl, CURLOPT_URL, "https://api.maetra.io/v1/secure/rules");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_perform(curl);   // response is written to stdout by default
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);
    return 0;
}
```

{% endtab %}

{% tab title="Java" %}

```java
import java.net.URI;
import java.net.http.*;

var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.maetra.io/v1/secure/rules"))
    .header("Authorization", "Bearer " + System.getenv("MAETRA_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
```

{% endtab %}
{% endtabs %}

```json
{
  "rules": [
    {
      "id": "rule_71c",
      "name": "Outbound PII",
      "description": "Flag customer PII leaving the system.",
      "type": "data_pattern",
      "action": "flag",
      "severity": "medium",
      "status": "active",
      "is_built_in": false,
      "applies_to_all": true,
      "trigger_count": 12,
      "last_triggered_at": "2026-07-06T22:04:00.000Z",
      "version": 3,
      "updated_at": "2026-07-01T09:12:00.000Z"
    }
  ]
}
```

See the [rule object](#rule-object) for every field.

### Create a rule

`POST /v1/secure/rules` — requires scope `secure:rules:write`. Returns `201`.

**Rule types**

| `type`           | Inspects                                         | Key fields                                                                                         |
| ---------------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------------- |
| `prompt_pattern` | Prompt text for injection / disallowed phrasing. | `custom_patterns`                                                                                  |
| `data_pattern`   | Content for sensitive-data signatures.           | `custom_patterns`, `data_categories`, `data_descriptions`, `data_direction`, `pattern_library_ids` |
| `tool_call`      | Tool/function calls.                             | `tool_names`                                                                                       |
| `policy_dsl`     | Conditions in Maetra's rule DSL.                 | `dsl_statements`                                                                                   |

**Request body**

| Field                 | Type           | Required | Default  | Description                                                  |
| --------------------- | -------------- | -------- | -------- | ------------------------------------------------------------ |
| `name`                | string         | ✓        |          | Rule name.                                                   |
| `type`                | enum           | ✓        |          | `data_pattern`, `policy_dsl`, `prompt_pattern`, `tool_call`. |
| `description`         | string \| null |          |          | Human-facing description.                                    |
| `action`              | enum           |          | `flag`   | `block`, `flag`, `log`.                                      |
| `severity`            | enum           |          | `medium` | `critical`, `high`, `medium`, `low`.                         |
| `status`              | enum           |          | `draft`  | `active`, `archived`, `draft`.                               |
| `applies_to_all`      | boolean        |          | `true`   | Apply to every agent.                                        |
| `agent_ids`           | string\[]      |          |          | Limit to specific agents (when not `applies_to_all`).        |
| `data_direction`      | enum \| null   |          |          | `inbound`, `outbound`, `both`.                               |
| `custom_patterns`     | string\[]      |          |          | Literal/regex patterns to match.                             |
| `tool_names`          | string\[]      |          |          | Tools this rule governs (for `tool_call`).                   |
| `data_categories`     | string\[]      |          |          | Named data categories to detect.                             |
| `data_descriptions`   | string\[]      |          |          | Natural-language descriptions of data to detect.             |
| `dsl_statements`      | string\[]      |          |          | Rule DSL statements (for `policy_dsl`).                      |
| `pattern_library_ids` | string\[]      |          |          | Reference built-in pattern libraries.                        |

{% hint style="info" %}
New rules default to `status: draft` — they don't affect scans until `active`. Create as `draft`, test, then promote (with [Update a rule](#update-a-rule)).
{% endhint %}

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X POST "https://api.maetra.io/v1/secure/rules" \
  -H "Authorization: Bearer $MAETRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Block secrets in tool calls",
  "type": "data_pattern",
  "action": "block",
  "severity": "high",
  "status": "active",
  "data_direction": "outbound",
  "custom_patterns": [
    "AKIA[0-9A-Z]{16}",
    "-----BEGIN (RSA )?PRIVATE KEY-----"
  ]
}'
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const res = await fetch("https://api.maetra.io/v1/secure/rules", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.MAETRA_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
      "name": "Block secrets in tool calls",
      "type": "data_pattern",
      "action": "block",
      "severity": "high",
      "status": "active",
      "data_direction": "outbound",
      "custom_patterns": [
          "AKIA[0-9A-Z]{16}",
          "-----BEGIN (RSA )?PRIVATE KEY-----"
      ]
  }),
});
if (!res.ok) throw new Error(`Maetra API ${res.status}`);
const data = await res.json();
console.log(data);
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
const res = await fetch("https://api.maetra.io/v1/secure/rules", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.MAETRA_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
      "name": "Block secrets in tool calls",
      "type": "data_pattern",
      "action": "block",
      "severity": "high",
      "status": "active",
      "data_direction": "outbound",
      "custom_patterns": [
          "AKIA[0-9A-Z]{16}",
          "-----BEGIN (RSA )?PRIVATE KEY-----"
      ]
  }),
});
if (!res.ok) throw new Error(`Maetra API ${res.status}`);
const data = (await res.json());
```

{% endtab %}

{% tab title="Python" %}

```python
import os, requests

res = requests.post(
    "https://api.maetra.io/v1/secure/rules",
    headers={"Authorization": f"Bearer {os.environ['MAETRA_API_KEY']}"},
    json={
        "name": "Block secrets in tool calls",
        "type": "data_pattern",
        "action": "block",
        "severity": "high",
        "status": "active",
        "data_direction": "outbound",
        "custom_patterns": ["AKIA[0-9A-Z]{16}", "-----BEGIN (RSA )?PRIVATE KEY-----"]
    },
)
res.raise_for_status()
print(res.json())
```

{% endtab %}

{% tab title="Rust" %}

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let key = std::env::var("MAETRA_API_KEY")?;
    let client = reqwest::Client::new();
    let res = client
        .post("https://api.maetra.io/v1/secure/rules")
        .bearer_auth(&key)
        .json(&json!({
            "name": "Block secrets in tool calls",
            "type": "data_pattern",
            "action": "block",
            "severity": "high",
            "status": "active",
            "data_direction": "outbound",
            "custom_patterns": [
                "AKIA[0-9A-Z]{16}",
                "-----BEGIN (RSA )?PRIVATE KEY-----"
            ]
        }))
        .send()
        .await?;
    let data: serde_json::Value = res.json().await?;
    println!("{data:#}");
    Ok(())
}
```

{% endtab %}

{% tab title="C++" %}

```cpp
#include <curl/curl.h>
#include <cstdlib>
#include <string>

int main() {
    CURL* curl = curl_easy_init();
    std::string auth = "Authorization: Bearer " + std::string(std::getenv("MAETRA_API_KEY"));
    struct curl_slist* headers = nullptr;
    headers = curl_slist_append(headers, auth.c_str());
    headers = curl_slist_append(headers, "Content-Type: application/json");
    curl_easy_setopt(curl, CURLOPT_URL, "https://api.maetra.io/v1/secure/rules");
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, R"({  "name": "Block secrets in tool calls",  "type": "data_pattern",  "action": "block",  "severity": "high",  "status": "active",  "data_direction": "outbound",  "custom_patterns": [    "AKIA[0-9A-Z]{16}",    "-----BEGIN (RSA )?PRIVATE KEY-----"  ]})");
    curl_easy_perform(curl);   // response is written to stdout by default
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);
    return 0;
}
```

{% endtab %}

{% tab title="Java" %}

```java
import java.net.URI;
import java.net.http.*;

var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.maetra.io/v1/secure/rules"))
    .header("Authorization", "Bearer " + System.getenv("MAETRA_API_KEY"))
    .header("Content-Type", "application/json")
    .method("POST", HttpRequest.BodyPublishers.ofString("""
{
  "name": "Block secrets in tool calls",
  "type": "data_pattern",
  "action": "block",
  "severity": "high",
  "status": "active",
  "data_direction": "outbound",
  "custom_patterns": [
    "AKIA[0-9A-Z]{16}",
    "-----BEGIN (RSA )?PRIVATE KEY-----"
  ]
}"""))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
```

{% endtab %}
{% endtabs %}

```json
{
  "rule": {
    "id": "rule_9f2",
    "name": "Block secrets in tool calls",
    "type": "data_pattern",
    "action": "block",
    "severity": "high",
    "status": "active",
    "is_built_in": false,
    "applies_to_all": true,
    "trigger_count": 0,
    "last_triggered_at": null,
    "version": 1,
    "updated_at": "2026-07-07T11:40:00.000Z"
  }
}
```

### Update a rule

`PATCH /v1/secure/rules/{id}` — requires scope `secure:rules:write`.

Only the fields you send are changed; omit the rest to leave them untouched. Accepts the **same fields as create** (all optional here). Built-in rules can't be edited. A common use is flipping a `draft` rule to `active`, or dialing an action from `block` down to `flag`:

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X PATCH "https://api.maetra.io/v1/secure/rules/rule_9f2" \
  -H "Authorization: Bearer $MAETRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "active",
  "action": "flag"
}'
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const res = await fetch("https://api.maetra.io/v1/secure/rules/rule_9f2", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.MAETRA_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
      "status": "active",
      "action": "flag"
  }),
});
if (!res.ok) throw new Error(`Maetra API ${res.status}`);
const data = await res.json();
console.log(data);
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
const res = await fetch("https://api.maetra.io/v1/secure/rules/rule_9f2", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.MAETRA_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
      "status": "active",
      "action": "flag"
  }),
});
if (!res.ok) throw new Error(`Maetra API ${res.status}`);
const data = (await res.json());
```

{% endtab %}

{% tab title="Python" %}

```python
import os, requests

res = requests.patch(
    "https://api.maetra.io/v1/secure/rules/rule_9f2",
    headers={"Authorization": f"Bearer {os.environ['MAETRA_API_KEY']}"},
    json={
        "status": "active",
        "action": "flag"
    },
)
res.raise_for_status()
print(res.json())
```

{% endtab %}

{% tab title="Rust" %}

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let key = std::env::var("MAETRA_API_KEY")?;
    let client = reqwest::Client::new();
    let res = client
        .patch("https://api.maetra.io/v1/secure/rules/rule_9f2")
        .bearer_auth(&key)
        .json(&json!({
            "status": "active",
            "action": "flag"
        }))
        .send()
        .await?;
    let data: serde_json::Value = res.json().await?;
    println!("{data:#}");
    Ok(())
}
```

{% endtab %}

{% tab title="C++" %}

```cpp
#include <curl/curl.h>
#include <cstdlib>
#include <string>

int main() {
    CURL* curl = curl_easy_init();
    std::string auth = "Authorization: Bearer " + std::string(std::getenv("MAETRA_API_KEY"));
    struct curl_slist* headers = nullptr;
    headers = curl_slist_append(headers, auth.c_str());
    headers = curl_slist_append(headers, "Content-Type: application/json");
    curl_easy_setopt(curl, CURLOPT_URL, "https://api.maetra.io/v1/secure/rules/rule_9f2");
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, R"({  "status": "active",  "action": "flag"})");
    curl_easy_perform(curl);   // response is written to stdout by default
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);
    return 0;
}
```

{% endtab %}

{% tab title="Java" %}

```java
import java.net.URI;
import java.net.http.*;

var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.maetra.io/v1/secure/rules/rule_9f2"))
    .header("Authorization", "Bearer " + System.getenv("MAETRA_API_KEY"))
    .header("Content-Type", "application/json")
    .method("PATCH", HttpRequest.BodyPublishers.ofString("""
{
  "status": "active",
  "action": "flag"
}"""))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
```

{% endtab %}
{% endtabs %}

```json
{
  "rule": {
    "id": "rule_9f2",
    "name": "Block secrets in tool calls",
    "action": "flag",
    "status": "active",
    "version": 2,
    "updated_at": "2026-07-07T12:02:00.000Z"
  }
}
```

{% hint style="info" %}
List-type fields (`custom_patterns`, `tool_names`, `agent_ids`, …) are **replaced** when you send them — send the full desired list, not a delta. Omit a list entirely to leave it unchanged.
{% endhint %}

### Rule object

| Field                  | Description                                                  |
| ---------------------- | ------------------------------------------------------------ |
| `id`                   | Rule identifier.                                             |
| `name` / `description` | Human-facing labels.                                         |
| `type`                 | `data_pattern`, `policy_dsl`, `prompt_pattern`, `tool_call`. |
| `action`               | `block`, `flag`, `log`.                                      |
| `severity`             | `critical`, `high`, `medium`, `low`.                         |
| `status`               | `active`, `archived`, `draft`.                               |
| `is_built_in`          | Whether Maetra ships this rule by default.                   |
| `applies_to_all`       | Whether it applies to every agent.                           |
| `trigger_count`        | How many times it has matched.                               |
| `last_triggered_at`    | ISO 8601 of the last match, or `null`.                       |
| `version`              | Increments on every edit.                                    |
| `updated_at`           | ISO 8601 of the last change.                                 |
