# AI agents

> How AI crawlers and agents use a zone, by category, bot and operator, plus traps, signals, MCP Shield and the bot catalog.

Reports of AI Crawl Control. The edge classifies automated traffic (AI training, AI search, user-triggered AI agents, search engines, unverified automation, spoofed claims) and verifies bots against the catalog. Every endpoint accepts any role, zone tokens included. Configure the behaviour in the `ai_agents` block of the [edge settings](https://developers.openwaf.dev/api/edge-settings/#update-zone-edge). See [AI Crawl Control](https://developers.openwaf.dev/ai-crawl-control/).

| Operation | Endpoint |
|---|---|
| [Get AI agents report](https://developers.openwaf.dev/api/ai-agents/#get-ai-agents-report) | `GET /v1/orgs/{org}/zones/{zone}/ai-agents/report` |
| [Get AI Crawl Control overview](https://developers.openwaf.dev/api/ai-agents/#get-ai-agents-overview) | `GET /v1/orgs/{org}/zones/{zone}/ai-agents/overview` |
| [Get AI agents summary](https://developers.openwaf.dev/api/ai-agents/#get-ai-agents-summary) | `GET /v1/orgs/{org}/zones/{zone}/ai-agents/summary` |
| [Get AI agents time series](https://developers.openwaf.dev/api/ai-agents/#get-ai-agents-timeseries) | `GET /v1/orgs/{org}/zones/{zone}/ai-agents/timeseries` |
| [List seen bots](https://developers.openwaf.dev/api/ai-agents/#get-ai-agents-agents) | `GET /v1/orgs/{org}/zones/{zone}/ai-agents/agents` |
| [Get top automated sources](https://developers.openwaf.dev/api/ai-agents/#get-ai-agents-top) | `GET /v1/orgs/{org}/zones/{zone}/ai-agents/top` |
| [List notable AI events](https://developers.openwaf.dev/api/ai-agents/#get-ai-agents-events) | `GET /v1/orgs/{org}/zones/{zone}/ai-agents/events` |
| [Get AI agents metrics](https://developers.openwaf.dev/api/ai-agents/#get-ai-agents-metrics) | `GET /v1/orgs/{org}/zones/{zone}/ai-agents/metrics` |
| [List crawlers](https://developers.openwaf.dev/api/ai-agents/#get-ai-agents-crawlers) | `GET /v1/orgs/{org}/zones/{zone}/ai-agents/crawlers` |
| [List spoofers of a bot](https://developers.openwaf.dev/api/ai-agents/#get-ai-agents-spoofers) | `GET /v1/orgs/{org}/zones/{zone}/ai-agents/spoofers` |
| [Get AI content optimization](https://developers.openwaf.dev/api/ai-agents/#get-ai-agents-optimization) | `GET /v1/orgs/{org}/zones/{zone}/ai-agents/optimization` |
| [Get robots.txt signals](https://developers.openwaf.dev/api/ai-agents/#get-ai-agents-signals) | `GET /v1/orgs/{org}/zones/{zone}/ai-agents/signals` |
| [Get trap activity](https://developers.openwaf.dev/api/ai-agents/#get-ai-agents-traps) | `GET /v1/orgs/{org}/zones/{zone}/ai-agents/traps` |
| [List agent bursts](https://developers.openwaf.dev/api/ai-agents/#get-ai-agents-bursts) | `GET /v1/orgs/{org}/zones/{zone}/ai-agents/bursts` |
| [Get AI endpoints activity](https://developers.openwaf.dev/api/ai-agents/#get-ai-agents-endpoints) | `GET /v1/orgs/{org}/zones/{zone}/ai-agents/endpoints` |
| [Get MCP Shield activity](https://developers.openwaf.dev/api/ai-agents/#get-ai-agents-mcp) | `GET /v1/orgs/{org}/zones/{zone}/ai-agents/mcp` |
| [Get the AI bot catalog](https://developers.openwaf.dev/api/ai-agents/#get-ai-bot-catalog) | `GET /v1/orgs/{org}/ai-agents/catalog` |

## Get AI agents report

```http
GET /v1/orgs/{org}/zones/{zone}/ai-agents/report
```

Returns in one call the summary, time series, bots, top sources and `ai_agents` settings of the zone (or of one name with `host`).

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `range` | query | string | No | Period up to now: 24h (default), 7d or 30d; not with since. One of `24h`, `7d`, `30d`. |
| `since` | query | string (date-time) | No | RFC 3339. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `category` | query | string | No | Only that traffic category. One of `ai_training`, `ai_search`, `ai_user_agent`, `search_engine`, `unverified_automation`, `spoofed`, `human`. |
| `bot` | query | string | No | Id of a catalog bot (gptbot). At most 253 characters. |
| `operator` | query | string | No | Operator (OpenAI). At most 64 characters. |

#### Response

- `200`: Returns [AiReport](https://developers.openwaf.dev/api/schemas/#ai-report).

| Field | Type | Description |
|---|---|---|
| `summary` | [AiSummary](https://developers.openwaf.dev/api/schemas/#ai-summary) |  |
| `timeseries` | [AiTimeseries](https://developers.openwaf.dev/api/schemas/#ai-timeseries) |  |
| `agents` | [AiAgents](https://developers.openwaf.dev/api/schemas/#ai-agents) |  |
| `top` | [AiTop](https://developers.openwaf.dev/api/schemas/#ai-top) |  |
| `settings` | [ZoneEdgeAIAgents](https://developers.openwaf.dev/api/schemas/#zone-edge-ai-agents) |  |
| `catalog_version` | string |  |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/ai-agents/report" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getAIAgentsReport({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_ai_agents_report(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "summary": {
    "since": "2026-10-27T09:30:00Z",
    "until": "2026-10-27T09:30:00Z",
    "prev_since": "2026-10-27T09:30:00Z",
    "prev_until": "2026-10-27T09:30:00Z",
    "requests": 128,
    "prev_requests": 128,
    "categories": [
      {
        "category": "ai_training",
        "requests": 128,
        "prev_requests": 128
      }
    ],
    "decisions": [
      {
        "decision": "example",
        "enforced": 0,
        "would": 0
      }
    ],
    "trap": {
      "injected": 0,
      "hits": 128,
      "prev_injected": 0,
      "prev_hits": 128
    }
  },
  "timeseries": {
    "step": 0,
    "points": [
      {}
    ]
  },
  "agents": {
    "rows": [
      {
        "bot_id": "948186e3-4508-48d6-a001-c683a3ec9626",
        "name": "example.com",
        "operator": "OpenAI",
        "category": "ai_training",
        "requests": 128,
        "verified": 0,
        "unverified": 0,
        "spoofed": 0,
        "methods": [
          "POST"
        ],
        "last_seen": "2026-10-27T09:30:00Z"
      }
    ]
  },
  "top": {
    "ip": [
      {
        "value": "4f1c2e9a7b3d",
        "country_code": "ES",
        "asn": 0,
        "category": "ai_training",
        "requests": 128
      }
    ],
    "fingerprint": [
      {
        "value": "4f1c2e9a7b3d",
        "country_code": "ES",
        "asn": 0,
        "category": "ai_training",
        "requests": 128
      }
    ],
    "asn": [
      {
        "value": "4f1c2e9a7b3d",
        "country_code": "ES",
        "asn": 0,
        "category": "ai_training",
        "requests": 128
      }
    ]
  },
  "settings": {
    "enabled": false,
    "mode": "log",
    "spoofed_challenge": "log",
    "template": "search_ok_block_training",
    "policy": {
      "ai_training": "allow",
      "ai_search": "allow",
      "ai_user_agent": "allow",
      "search_engine": "allow",
      "unverified_automation": "allow",
      "spoofed": "allow",
      "unverified_bot": "allow",
      "trap_hit": "allow",
      "secret_probe": "allow",
      "robots_violation": "allow",
      "agent_burst": "allow"
    },
    "bot_overrides": [
      {
        "bot_id": "948186e3-4508-48d6-a001-c683a3ec9626",
        "action": "block"
      }
    ],
    "trap": {
      "enabled": false
    },
    "tarpit": {
      "enabled": false
    },
    "robots": {
      "enabled": false,
      "content_signals": {
        "search": "yes",
        "ai_input": "yes",
        "ai_train": "yes"
      }
    },
    "markdown": {
      "enabled": false
    },
    "block_response": {
      "status": 403,
      "message": ""
    },
    "endpoints": [
      {
        "path_prefix": "/api/",
        "methods": [
          "POST"
        ],
        "max_body_bytes": 0,
        "rpm_per_ip": 0,
        "rpm_per_session": 0,
        "session_header": "X-Session-Id",
        "session_cookie": "session",
        "injection_action": "log",
        "limit_action": "log",
        "kind": "mcp",
        "mcp": {}
      }
    ]
  },
  "catalog_version": "example"
}
```

## Get AI Crawl Control overview

```http
GET /v1/orgs/{org}/zones/{zone}/ai-agents/overview
```

Returns the AI Crawl Control overview of the dashboard, highlights, cards with their series and operators.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `range` | query | string | No | Period up to now: 24h (default), 7d or 30d; not with since. One of `24h`, `7d`, `30d`. |
| `since` | query | string (date-time) | No | RFC 3339. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `category` | query | string | No | Only that traffic category. One of `ai_training`, `ai_search`, `ai_user_agent`, `search_engine`, `unverified_automation`, `spoofed`, `human`. |
| `bot` | query | string | No | Id of a catalog bot (gptbot). At most 253 characters. |
| `operator` | query | string | No | Operator (OpenAI). At most 64 characters. |

#### Response

- `200`: Returns [AiOverview](https://developers.openwaf.dev/api/schemas/#ai-overview).

| Field | Type | Description |
|---|---|---|
| `since` | string (date-time) |  |
| `until` | string (date-time) |  |
| `step` | integer |  |
| `ts` | array of string (date-time) | Time of the request. |
| `facts` | object |  |
| `facts.requests` | integer | Requests. |
| `facts.prev_requests` | integer |  |
| `facts.status_4xx` | integer |  |
| `facts.top_operator_html` | object or null |  |
| `facts.top_path` | object or null |  |
| `facts.top_crawler` | object or null |  |
| `cards` | object |  |
| `cards.total` | [AiCard](https://developers.openwaf.dev/api/schemas/#ai-card) |  |
| `cards.allowed` | [AiCard](https://developers.openwaf.dev/api/schemas/#ai-card) |  |
| `cards.failed` | [AiCard](https://developers.openwaf.dev/api/schemas/#ai-card) |  |
| `operators` | array of object |  |
| `operators[].operator` | string | Company that operates the bot. |
| `operators[].crawlers` | array of object |  |
| `operators[].allowed` | integer |  |
| `operators[].prev_allowed` | integer |  |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/ai-agents/overview" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getAIAgentsOverview({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_ai_agents_overview(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "since": "2026-10-27T09:30:00Z",
  "until": "2026-10-27T09:30:00Z",
  "step": 0,
  "ts": [
    "2026-10-27T09:30:00Z"
  ],
  "facts": {
    "requests": 128,
    "prev_requests": 128,
    "status_4xx": 0,
    "top_operator_html": {},
    "top_path": {},
    "top_crawler": {}
  },
  "cards": {
    "total": {
      "value": 0,
      "prev": 0,
      "series": [
        0
      ]
    },
    "allowed": {
      "value": 0,
      "prev": 0,
      "series": [
        0
      ]
    },
    "failed": {
      "value": 0,
      "prev": 0,
      "series": [
        0
      ]
    }
  },
  "operators": [
    {
      "operator": "OpenAI",
      "crawlers": [
        {}
      ],
      "allowed": 0,
      "prev_allowed": 0
    }
  ]
}
```

## Get AI agents summary

```http
GET /v1/orgs/{org}/zones/{zone}/ai-agents/summary
```

Counts the zone's requests per traffic category and decision, plus the trap, against the previous period.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `range` | query | string | No | Period up to now: 24h (default), 7d or 30d; not with since. One of `24h`, `7d`, `30d`. |
| `since` | query | string (date-time) | No | RFC 3339. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `category` | query | string | No | Only that traffic category. One of `ai_training`, `ai_search`, `ai_user_agent`, `search_engine`, `unverified_automation`, `spoofed`, `human`. |
| `bot` | query | string | No | Id of a catalog bot (gptbot). At most 253 characters. |
| `operator` | query | string | No | Operator (OpenAI). At most 64 characters. |

#### Response

- `200`: Returns [AiSummary](https://developers.openwaf.dev/api/schemas/#ai-summary).

| Field | Type | Description |
|---|---|---|
| `since` | string (date-time) |  |
| `until` | string (date-time) |  |
| `prev_since` | string (date-time) |  |
| `prev_until` | string (date-time) |  |
| `requests` | integer | Requests. |
| `prev_requests` | integer |  |
| `categories` | array of [AiCount](https://developers.openwaf.dev/api/schemas/#ai-count) |  |
| `decisions` | array of object |  |
| `decisions[].decision` | string |  |
| `decisions[].enforced` | integer |  |
| `decisions[].would` | integer | What would be applied in enforce mode. |
| `trap` | [AiTrapSummary](https://developers.openwaf.dev/api/schemas/#ai-trap-summary) |  |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/ai-agents/summary" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getAIAgentsSummary({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_ai_agents_summary(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "since": "2026-10-27T09:30:00Z",
  "until": "2026-10-27T09:30:00Z",
  "prev_since": "2026-10-27T09:30:00Z",
  "prev_until": "2026-10-27T09:30:00Z",
  "requests": 128,
  "prev_requests": 128,
  "categories": [
    {
      "category": "ai_training",
      "requests": 128,
      "prev_requests": 128
    }
  ],
  "decisions": [
    {
      "decision": "example",
      "enforced": 0,
      "would": 0
    }
  ],
  "trap": {
    "injected": 0,
    "hits": 128,
    "prev_injected": 0,
    "prev_hits": 128
  }
}
```

## Get AI agents time series

```http
GET /v1/orgs/{org}/zones/{zone}/ai-agents/timeseries
```

Returns the requests per category over time.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `range` | query | string | No | Period up to now: 24h (default), 7d or 30d; not with since. One of `24h`, `7d`, `30d`. |
| `since` | query | string (date-time) | No | RFC 3339. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `category` | query | string | No | Only that traffic category. One of `ai_training`, `ai_search`, `ai_user_agent`, `search_engine`, `unverified_automation`, `spoofed`, `human`. |
| `bot` | query | string | No | Id of a catalog bot (gptbot). At most 253 characters. |
| `operator` | query | string | No | Operator (OpenAI). At most 64 characters. |
| `step` | query | integer | No | Seconds per point (0 = automatic). Minimum 0. |

#### Response

- `200`: Returns [AiTimeseries](https://developers.openwaf.dev/api/schemas/#ai-timeseries).

| Field | Type | Description |
|---|---|---|
| `step` | integer |  |
| `points` | array of object |  |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/ai-agents/timeseries" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getAIAgentsTimeseries({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_ai_agents_timeseries(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "step": 0,
  "points": [
    {}
  ]
}
```

## List seen bots

```http
GET /v1/orgs/{org}/zones/{zone}/ai-agents/agents
```

Lists the bots seen on the zone, verified, unverified and spoofed.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `range` | query | string | No | Period up to now: 24h (default), 7d or 30d; not with since. One of `24h`, `7d`, `30d`. |
| `since` | query | string (date-time) | No | RFC 3339. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `category` | query | string | No | Only that traffic category. One of `ai_training`, `ai_search`, `ai_user_agent`, `search_engine`, `unverified_automation`, `spoofed`, `human`. |
| `bot` | query | string | No | Id of a catalog bot (gptbot). At most 253 characters. |
| `operator` | query | string | No | Operator (OpenAI). At most 64 characters. |

#### Response

- `200`: Returns [AiAgents](https://developers.openwaf.dev/api/schemas/#ai-agents).

| Field | Type | Description |
|---|---|---|
| `rows` | array of [AiAgentRow](https://developers.openwaf.dev/api/schemas/#ai-agent-row) |  |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/ai-agents/agents" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getAIAgentsAgents({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_ai_agents_agents(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "rows": [
    {
      "bot_id": "948186e3-4508-48d6-a001-c683a3ec9626",
      "name": "example.com",
      "operator": "OpenAI",
      "category": "ai_training",
      "requests": 128,
      "verified": 0,
      "unverified": 0,
      "spoofed": 0,
      "methods": [
        "POST"
      ],
      "last_seen": "2026-10-27T09:30:00Z"
    }
  ]
}
```

## Get top automated sources

```http
GET /v1/orgs/{org}/zones/{zone}/ai-agents/top
```

Returns the top IPs, TLS fingerprints and ASNs of the automated traffic.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `range` | query | string | No | Period up to now: 24h (default), 7d or 30d; not with since. One of `24h`, `7d`, `30d`. |
| `since` | query | string (date-time) | No | RFC 3339. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `category` | query | string | No | Only that traffic category. One of `ai_training`, `ai_search`, `ai_user_agent`, `search_engine`, `unverified_automation`, `spoofed`, `human`. |
| `bot` | query | string | No | Id of a catalog bot (gptbot). At most 253 characters. |
| `operator` | query | string | No | Operator (OpenAI). At most 64 characters. |
| `limit` | query | integer | No | 10 by default. Minimum 0. |

#### Response

- `200`: Returns [AiTop](https://developers.openwaf.dev/api/schemas/#ai-top).

| Field | Type | Description |
|---|---|---|
| `ip` | array of [AiTopItem](https://developers.openwaf.dev/api/schemas/#ai-top-item) |  |
| `fingerprint` | array of [AiTopItem](https://developers.openwaf.dev/api/schemas/#ai-top-item) |  |
| `asn` | array of [AiTopItem](https://developers.openwaf.dev/api/schemas/#ai-top-item) | Autonomous system number. |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/ai-agents/top" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getAIAgentsTop({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_ai_agents_top(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "ip": [
    {
      "value": "4f1c2e9a7b3d",
      "country_code": "ES",
      "asn": 0,
      "category": "ai_training",
      "requests": 128
    }
  ],
  "fingerprint": [
    {
      "value": "4f1c2e9a7b3d",
      "country_code": "ES",
      "asn": 0,
      "category": "ai_training",
      "requests": 128
    }
  ],
  "asn": [
    {
      "value": "4f1c2e9a7b3d",
      "country_code": "ES",
      "asn": 0,
      "category": "ai_training",
      "requests": 128
    }
  ]
}
```

## List notable AI events

```http
GET /v1/orgs/{org}/zones/{zone}/ai-agents/events
```

Lists the notable events (spoofing, trap hits, injections, MCP Shield detections…), newest first.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `range` | query | string | No | Period up to now: 24h (default), 7d or 30d; not with since. One of `24h`, `7d`, `30d`. |
| `since` | query | string (date-time) | No | RFC 3339. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `category` | query | string | No | Only that traffic category. One of `ai_training`, `ai_search`, `ai_user_agent`, `search_engine`, `unverified_automation`, `spoofed`, `human`. |
| `bot` | query | string | No | Id of a catalog bot (gptbot). At most 253 characters. |
| `operator` | query | string | No | Operator (OpenAI). At most 64 characters. |
| `kind` | query | string | No | Comma-separated kinds (spoofed, trap_hit, injection, cost_limit, automation, wba_invalid, tarpit, blocked, secret_probe, robots_violation, agent_burst; MCP Shield: mcp_blocked_method, mcp_blocked_tool, mcp_injection_request, mcp_injection_response, mcp_dlp, mcp_rate_limited, mcp_invalid). |
| `cursor` | query | string | No | Cursor of the next page (`next_cursor` of the previous answer). |
| `limit` | query | integer | No | 50 by default, at most 500. Minimum 0. |

#### Response

- `200`: Returns [AiEventsPage](https://developers.openwaf.dev/api/schemas/#ai-events-page).

| Field | Type | Description |
|---|---|---|
| `items` | array of [AiEvent](https://developers.openwaf.dev/api/schemas/#ai-event) |  |
| `next_cursor` | string |  |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/ai-agents/events?kind=spoofed,trap_hit" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getAIAgentsEvents({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
  kind: "spoofed,trap_hit"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_ai_agents_events(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d", kind="spoofed,trap_hit")
```



```json
{
  "items": [
    {
      "event_id": "57c7c826-7db4-42c8-af1d-3ecdfe37182f",
      "ts": "2026-10-27T09:30:00Z",
      "site_id": 0,
      "node_id": "edge-node-1",
      "host": "www.example.com",
      "kind": "spoofed",
      "category": "ai_training",
      "bot_id": "948186e3-4508-48d6-a001-c683a3ec9626",
      "verified": false,
      "verify": "",
      "decision": "example",
      "enforced": false,
      "ip": "203.0.113.10",
      "country_code": "ES",
      "asn": 0,
      "user_agent": "Mozilla/5.0",
      "tls_fingerprint": "",
      "method": "GET",
      "path": "/checkout",
      "status": 0,
      "signals": [
        "example"
      ],
      "detections": [
        {}
      ],
      "token_age_ms": 0,
      "mcp": {
        "method": "GET",
        "tool": "search_docs",
        "token_fp": "example",
        "blocked": false
      }
    }
  ],
  "next_cursor": ""
}
```

## Get AI agents metrics

```http
GET /v1/orgs/{org}/zones/{zone}/ai-agents/metrics
```

Returns metrics per group (crawler, category, operator, host or content type) with status classes and most requested pages. `format=csv` downloads CSV instead of JSON.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `range` | query | string | No | Period up to now: 24h (default), 7d or 30d; not with since. One of `24h`, `7d`, `30d`. |
| `since` | query | string (date-time) | No | RFC 3339. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `category` | query | string | No | Only that traffic category. One of `ai_training`, `ai_search`, `ai_user_agent`, `search_engine`, `unverified_automation`, `spoofed`, `human`. |
| `bot` | query | string | No | Id of a catalog bot (gptbot). At most 253 characters. |
| `operator` | query | string | No | Operator (OpenAI). At most 64 characters. |
| `status` | query | string | No | Only responses with this status class. One of `all`, `1xx`, `2xx`, `3xx`, `4xx`, `5xx`. |
| `group_by` | query | string | No | Dimension to group by. One of `crawler`, `category`, `operator`, `host`, `content_type`. |
| `offset` | query | integer | No | Number of rows to skip (pagination). Minimum 0. |
| `limit` | query | integer | No | 10 by default, at most 100. Minimum 0. |
| `format` | query | string | No | csv: download CSV instead of JSON. One of `csv`. |

#### Response

- `200`: Returns [AiMetrics](https://developers.openwaf.dev/api/schemas/#ai-metrics).

| Field | Type | Description |
|---|---|---|
| `step` | integer |  |
| `ts` | array of string (date-time) | Time of the request. |
| `status` | string |  |
| `group_by` | string |  |
| `cards` | object |  |
| `requests` | array of [AiSeries](https://developers.openwaf.dev/api/schemas/#ai-series) | Requests. |
| `bytes` | array of [AiSeries](https://developers.openwaf.dev/api/schemas/#ai-series) |  |
| `status_classes` | array of object |  |
| `content_types` | array of object |  |
| `top_paths` | [AiPathPage](https://developers.openwaf.dev/api/schemas/#ai-path-page) |  |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/ai-agents/metrics?group_by=operator" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getAIAgentsMetrics({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
  group_by: "operator"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_ai_agents_metrics(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d", group_by="operator")
```



```json
{
  "step": 0,
  "ts": [
    "2026-10-27T09:30:00Z"
  ],
  "status": "example",
  "group_by": "example",
  "cards": {},
  "requests": [
    {
      "key": "/pricing",
      "label": "AI training",
      "total": 128,
      "series": [
        0
      ]
    }
  ],
  "bytes": [
    {
      "key": "/pricing",
      "label": "AI training",
      "total": 128,
      "series": [
        0
      ]
    }
  ],
  "status_classes": [
    {}
  ],
  "content_types": [
    {}
  ],
  "top_paths": {
    "total": 128,
    "items": [
      {
        "host": "www.example.com",
        "path": "/checkout",
        "bytes": 0,
        "requests": 128,
        "secret_probe": false
      }
    ]
  }
}
```

## List crawlers

```http
GET /v1/orgs/{org}/zones/{zone}/ai-agents/crawlers
```

Lists the catalog crawlers with their requests, verified and spoofed requests, bursts and effective action.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `range` | query | string | No | Period up to now: 24h (default), 7d or 30d; not with since. One of `24h`, `7d`, `30d`. |
| `since` | query | string (date-time) | No | RFC 3339. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `category` | query | string | No | Only that traffic category. One of `ai_training`, `ai_search`, `ai_user_agent`, `search_engine`, `unverified_automation`, `spoofed`, `human`. |
| `bot` | query | string | No | Id of a catalog bot (gptbot). At most 253 characters. |
| `operator` | query | string | No | Operator (OpenAI). At most 64 characters. |
| `inactive` | query | string | No | 1: include the catalog bots without traffic. One of `1`. |

#### Response

- `200`: Returns [AiCrawlers](https://developers.openwaf.dev/api/schemas/#ai-crawlers).

| Field | Type | Description |
|---|---|---|
| `step` | integer |  |
| `ts` | array of string (date-time) | Time of the request. |
| `rows` | array of object |  |
| `rows[].bot_id` | string |  |
| `rows[].name` | string | Name. |
| `rows[].operator` | string | Company that operates the bot. |
| `rows[].category` | string |  |
| `rows[].methods` | array of string |  |
| `rows[].bytes` | integer |  |
| `rows[].requests` | integer | Requests. |
| `rows[].allowed` | integer |  |
| `rows[].unsuccessful` | integer |  |
| `rows[].series` | array of integer |  |
| `rows[].verified` | integer |  |
| `rows[].spoofed` | integer |  |
| `rows[].unverified` | integer |  |
| `rows[].bursts` | integer | agent_burst events of the bot. |
| `rows[].action` | string |  |
| `rows[].action_source` | string | override, category or unverified_bot. |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/ai-agents/crawlers" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getAIAgentsCrawlers({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_ai_agents_crawlers(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "step": 0,
  "ts": [
    "2026-10-27T09:30:00Z"
  ],
  "rows": [
    {
      "bot_id": "948186e3-4508-48d6-a001-c683a3ec9626",
      "name": "example.com",
      "operator": "OpenAI",
      "category": "ai_training",
      "methods": [
        "POST"
      ],
      "bytes": 0,
      "requests": 128,
      "allowed": 0,
      "unsuccessful": 0,
      "series": [
        0
      ],
      "verified": 0,
      "spoofed": 0,
      "unverified": 0,
      "bursts": 0,
      "action": "block",
      "action_source": "example"
    }
  ]
}
```

## List spoofers of a bot

```http
GET /v1/orgs/{org}/zones/{zone}/ai-agents/spoofers
```

Lists the IPs and ASNs that claimed to be a bot without passing its verification. `bot` is required.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `range` | query | string | No | Period up to now: 24h (default), 7d or 30d; not with since. One of `24h`, `7d`, `30d`. |
| `since` | query | string (date-time) | No | RFC 3339. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `category` | query | string | No | Only that traffic category. One of `ai_training`, `ai_search`, `ai_user_agent`, `search_engine`, `unverified_automation`, `spoofed`, `human`. |
| `bot` | query | string | No | Id of a catalog bot (gptbot). At most 253 characters. |
| `operator` | query | string | No | Operator (OpenAI). At most 64 characters. |

#### Response

- `200`: Returns [AiSpoofers](https://developers.openwaf.dev/api/schemas/#ai-spoofers).

| Field | Type | Description |
|---|---|---|
| `bot_id` | string |  |
| `ips` | array of object |  |
| `ips[].value` | string |  |
| `ips[].country_code` | string | ISO 3166-1 alpha-2 country code. |
| `ips[].asn` | integer | Autonomous system number. |
| `ips[].requests` | integer | Requests. |
| `ips[].last_seen` | string (date-time) |  |
| `asns` | array of object |  |
| `asns[].asn` | integer | Autonomous system number. |
| `asns[].country_code` | string | ISO 3166-1 alpha-2 country code. |
| `asns[].requests` | integer | Requests. |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/ai-agents/spoofers?bot=gptbot" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getAIAgentsSpoofers({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
  bot: "gptbot"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_ai_agents_spoofers(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d", bot="gptbot")
```



```json
{
  "bot_id": "948186e3-4508-48d6-a001-c683a3ec9626",
  "ips": [
    {
      "value": "4f1c2e9a7b3d",
      "country_code": "ES",
      "asn": 0,
      "requests": 128,
      "last_seen": "2026-10-27T09:30:00Z"
    }
  ],
  "asns": [
    {
      "asn": 0,
      "country_code": "ES",
      "requests": 128
    }
  ]
}
```

## Get AI content optimization

```http
GET /v1/orgs/{org}/zones/{zone}/ai-agents/optimization
```

Returns the pages AI fetches and the demand for pages that do not exist. `format=csv` downloads CSV instead of JSON.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `range` | query | string | No | Period up to now: 24h (default), 7d or 30d; not with since. One of `24h`, `7d`, `30d`. |
| `since` | query | string (date-time) | No | RFC 3339. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `category` | query | string | No | Only that traffic category. One of `ai_training`, `ai_search`, `ai_user_agent`, `search_engine`, `unverified_automation`, `spoofed`, `human`. |
| `bot` | query | string | No | Id of a catalog bot (gptbot). At most 253 characters. |
| `operator` | query | string | No | Operator (OpenAI). At most 64 characters. |
| `offset` | query | integer | No | Number of rows to skip (pagination). Minimum 0. |
| `limit` | query | integer | No | 10 by default, at most 100. Minimum 0. |
| `format` | query | string | No | csv: download CSV instead of JSON. One of `csv`. |

#### Response

- `200`: Returns [AiOptimization](https://developers.openwaf.dev/api/schemas/#ai-optimization).

| Field | Type | Description |
|---|---|---|
| `cards` | object |  |
| `cards.retrievals` | object |  |
| `cards.demand` | object |  |
| `top_pages` | [AiPathPage](https://developers.openwaf.dev/api/schemas/#ai-path-page) |  |
| `demand` | [AiPathPage](https://developers.openwaf.dev/api/schemas/#ai-path-page) |  |
| `operators` | array of object |  |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/ai-agents/optimization" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getAIAgentsOptimization({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_ai_agents_optimization(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "cards": {
    "retrievals": {
      "value": 0,
      "prev": 0
    },
    "demand": {
      "value": 0,
      "prev": 0
    }
  },
  "top_pages": {
    "total": 128,
    "items": [
      {
        "host": "www.example.com",
        "path": "/checkout",
        "bytes": 0,
        "requests": 128,
        "secret_probe": false
      }
    ]
  },
  "demand": {
    "total": 128,
    "items": [
      {
        "host": "www.example.com",
        "path": "/checkout",
        "bytes": 0,
        "requests": 128,
        "secret_probe": false
      }
    ]
  },
  "operators": [
    {}
  ]
}
```

## Get robots.txt signals

```http
GET /v1/orgs/{org}/zones/{zone}/ai-agents/signals
```

Returns the robots.txt requests, the Content-Signal values served and the violations of its rules.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `range` | query | string | No | Period up to now: 24h (default), 7d or 30d; not with since. One of `24h`, `7d`, `30d`. |
| `since` | query | string (date-time) | No | RFC 3339. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `category` | query | string | No | Only that traffic category. One of `ai_training`, `ai_search`, `ai_user_agent`, `search_engine`, `unverified_automation`, `spoofed`, `human`. |
| `bot` | query | string | No | Id of a catalog bot (gptbot). At most 253 characters. |
| `operator` | query | string | No | Operator (OpenAI). At most 64 characters. |

#### Response

- `200`: Returns [AiSignals](https://developers.openwaf.dev/api/schemas/#ai-signals).

| Field | Type | Description |
|---|---|---|
| `robots` | array of object |  |
| `robots[].host` | string |  |
| `robots[].requests` | integer | Requests. |
| `robots[].ok` | integer |  |
| `robots[].failed` | integer |  |
| `robots[].last_status` | integer |  |
| `robots[].content_signals` | boolean |  |
| `violations` | array of object |  |
| `violations[].bot_id` | string |  |
| `violations[].name` | string | Name. |
| `violations[].operator` | string | Company that operates the bot. |
| `violations[].category` | string |  |
| `violations[].host` | string |  |
| `violations[].path` | string |  |
| `violations[].requests` | integer | Requests. |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/ai-agents/signals" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getAIAgentsSignals({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_ai_agents_signals(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "robots": [
    {
      "host": "www.example.com",
      "requests": 128,
      "ok": 0,
      "failed": 0,
      "last_status": 0,
      "content_signals": false
    }
  ],
  "violations": [
    {
      "bot_id": "948186e3-4508-48d6-a001-c683a3ec9626",
      "name": "example.com",
      "operator": "OpenAI",
      "category": "ai_training",
      "host": "www.example.com",
      "path": "/checkout",
      "requests": 128
    }
  ]
}
```

## Get trap activity

```http
GET /v1/orgs/{org}/zones/{zone}/ai-agents/traps
```

Returns the trap insertions, who followed the trap and the agent bursts.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `range` | query | string | No | Period up to now: 24h (default), 7d or 30d; not with since. One of `24h`, `7d`, `30d`. |
| `since` | query | string (date-time) | No | RFC 3339. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `category` | query | string | No | Only that traffic category. One of `ai_training`, `ai_search`, `ai_user_agent`, `search_engine`, `unverified_automation`, `spoofed`, `human`. |
| `bot` | query | string | No | Id of a catalog bot (gptbot). At most 253 characters. |
| `operator` | query | string | No | Operator (OpenAI). At most 64 characters. |

#### Response

- `200`: Returns [AiTraps](https://developers.openwaf.dev/api/schemas/#ai-traps).

| Field | Type | Description |
|---|---|---|
| `injected` | integer |  |
| `hits` | integer |  |
| `prev_injected` | integer |  |
| `prev_hits` | integer |  |
| `bursts` | integer |  |
| `prev_bursts` | integer |  |
| `step` | integer |  |
| `ts` | array of string (date-time) | Time of the request. |
| `series` | object |  |
| `series.injected` | array of integer |  |
| `series.hits` | array of integer |  |
| `hitters` | array of object |  |
| `hitters[].ip` | string |  |
| `hitters[].country_code` | string | ISO 3166-1 alpha-2 country code. |
| `hitters[].asn` | integer | Autonomous system number. |
| `hitters[].tls_fingerprint` | string | TLS fingerprint of the client. |
| `hitters[].bot_id` | string |  |
| `hitters[].hits` | integer |  |
| `hitters[].last_seen` | string (date-time) |  |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/ai-agents/traps" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getAIAgentsTraps({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_ai_agents_traps(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "injected": 0,
  "hits": 128,
  "prev_injected": 0,
  "prev_hits": 128,
  "bursts": 0,
  "prev_bursts": 0,
  "step": 0,
  "ts": [
    "2026-10-27T09:30:00Z"
  ],
  "series": {
    "injected": [
      0
    ],
    "hits": [
      0
    ]
  },
  "hitters": [
    {
      "ip": "203.0.113.10",
      "country_code": "ES",
      "asn": 0,
      "tls_fingerprint": "",
      "bot_id": "948186e3-4508-48d6-a001-c683a3ec9626",
      "hits": 128,
      "last_seen": "2026-10-27T09:30:00Z"
    }
  ]
}
```

## List agent bursts

```http
GET /v1/orgs/{org}/zones/{zone}/ai-agents/bursts
```

Lists the composite bursts of agents in code mode (`agent_burst`), the clients with an explanation, the bots and the detections.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `range` | query | string | No | Period up to now: 24h (default), 7d or 30d; not with since. One of `24h`, `7d`, `30d`. |
| `since` | query | string (date-time) | No | RFC 3339. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `category` | query | string | No | Only that traffic category. One of `ai_training`, `ai_search`, `ai_user_agent`, `search_engine`, `unverified_automation`, `spoofed`, `human`. |
| `bot` | query | string | No | Id of a catalog bot (gptbot). At most 253 characters. |
| `operator` | query | string | No | Operator (OpenAI). At most 64 characters. |

#### Response

- `200`: Returns [AiBursts](https://developers.openwaf.dev/api/schemas/#ai-bursts).

| Field | Type | Description |
|---|---|---|
| `bursts` | integer |  |
| `prev_bursts` | integer |  |
| `clients` | array of object | At most 50 items. |
| `clients[].ip` | string |  |
| `clients[].country_code` | string | ISO 3166-1 alpha-2 country code. |
| `clients[].asn` | integer | Autonomous system number. |
| `clients[].tls_fingerprint` | string | TLS fingerprint of the client. |
| `clients[].bot_id` | string |  |
| `clients[].bursts` | integer |  |
| `clients[].last_seen` | string (date-time) |  |
| `clients[].explanations` | array of string | Detection labels (distinct_endpoints=23/10s, id_walk=…, pagination=…). At most 3 items. |
| `crawlers` | array of object |  |
| `crawlers[].bot_id` | string |  |
| `crawlers[].name` | string | Name. |
| `crawlers[].operator` | string | Company that operates the bot. |
| `crawlers[].bursts` | integer |  |
| `detections` | array of object |  |
| `detections[].id` | string | Id. |
| `detections[].label` | string |  |
| `detections[].count` | integer |  |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/ai-agents/bursts" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getAIAgentsBursts({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_ai_agents_bursts(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "bursts": 0,
  "prev_bursts": 0,
  "clients": [
    {
      "ip": "203.0.113.10",
      "country_code": "ES",
      "asn": 0,
      "tls_fingerprint": "",
      "bot_id": "948186e3-4508-48d6-a001-c683a3ec9626",
      "bursts": 0,
      "last_seen": "2026-10-27T09:30:00Z",
      "explanations": []
    }
  ],
  "crawlers": [
    {
      "bot_id": "948186e3-4508-48d6-a001-c683a3ec9626",
      "name": "example.com",
      "operator": "OpenAI",
      "bursts": 0
    }
  ],
  "detections": [
    {
      "id": "example",
      "label": "AI training",
      "count": 128
    }
  ]
}
```

## Get AI endpoints activity

```http
GET /v1/orgs/{org}/zones/{zone}/ai-agents/endpoints
```

Returns the activity of the site's AI endpoints, injections, limits and blocks.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `range` | query | string | No | Period up to now: 24h (default), 7d or 30d; not with since. One of `24h`, `7d`, `30d`. |
| `since` | query | string (date-time) | No | RFC 3339. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `category` | query | string | No | Only that traffic category. One of `ai_training`, `ai_search`, `ai_user_agent`, `search_engine`, `unverified_automation`, `spoofed`, `human`. |
| `bot` | query | string | No | Id of a catalog bot (gptbot). At most 253 characters. |
| `operator` | query | string | No | Operator (OpenAI). At most 64 characters. |

#### Response

- `200`: Returns [AiEndpoints](https://developers.openwaf.dev/api/schemas/#ai-endpoints).

| Field | Type | Description |
|---|---|---|
| `rows` | array of object |  |
| `rows[].path_prefix` | string |  |
| `rows[].requests` | integer | Requests. |
| `rows[].injections` | integer |  |
| `rows[].limited` | integer |  |
| `rows[].blocked` | integer |  |
| `rows[].would_block` | integer |  |
| `detections` | array of object |  |
| `detections[].id` | string | Id. |
| `detections[].label` | string |  |
| `detections[].count` | integer |  |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/ai-agents/endpoints" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getAIAgentsEndpoints({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_ai_agents_endpoints(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "rows": [
    {
      "path_prefix": "/api/",
      "requests": 128,
      "injections": 0,
      "limited": 0,
      "blocked": 0,
      "would_block": 0
    }
  ],
  "detections": [
    {
      "id": "example",
      "label": "AI training",
      "count": 128
    }
  ]
}
```

## Get MCP Shield activity

```http
GET /v1/orgs/{org}/zones/{zone}/ai-agents/mcp
```

Returns the MCP Shield activity of the site's MCP endpoints, JSON-RPC messages, blocks, detections and tools. See [MCP Shield](https://developers.openwaf.dev/mcp-shield/).

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `range` | query | string | No | Period up to now: 24h (default), 7d or 30d; not with since. One of `24h`, `7d`, `30d`. |
| `since` | query | string (date-time) | No | RFC 3339. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |

#### Response

- `200`: Returns [AiMcp](https://developers.openwaf.dev/api/schemas/#ai-mcp).

| Field | Type | Description |
|---|---|---|
| `endpoints` | array of object |  |
| `endpoints[].path_prefix` | string |  |
| `endpoints[].requests` | integer | Requests. |
| `endpoints[].messages` | integer |  |
| `endpoints[].blocked` | integer |  |
| `endpoints[].detections` | integer |  |
| `endpoints[].uninspected` | integer |  |
| `tools` | array of object |  |
| `tools[].path_prefix` | string |  |
| `tools[].tool` | string |  |
| `tools[].calls` | integer |  |
| `tools[].blocked` | integer |  |
| `tools[].detections` | integer |  |
| `kinds` | array of object |  |
| `kinds[].kind` | string |  |
| `kinds[].count` | integer |  |
| `detections` | array of object |  |
| `detections[].id` | string | Id. |
| `detections[].label` | string |  |
| `detections[].count` | integer |  |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/ai-agents/mcp" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getAIAgentsMCP({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_ai_agents_mcp(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "endpoints": [
    {
      "path_prefix": "/api/",
      "requests": 128,
      "messages": 0,
      "blocked": 0,
      "detections": 0,
      "uninspected": 0
    }
  ],
  "tools": [
    {
      "path_prefix": "/api/",
      "tool": "search_docs",
      "calls": 0,
      "blocked": 0,
      "detections": 0
    }
  ],
  "kinds": [
    {
      "kind": "example",
      "count": 128
    }
  ],
  "detections": [
    {
      "id": "example",
      "label": "AI training",
      "count": 128
    }
  ]
}
```

## Get the AI bot catalog

```http
GET /v1/orgs/{org}/ai-agents/catalog
```

Returns the AI bot catalog the edge verifies against, each bot's operator, category and verification methods (published IP ranges, reverse DNS, Web Bot Auth signatures), and the state of each official source. The ranges and keys themselves are not included.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |

#### Response

- `200`: Returns [AiCatalog](https://developers.openwaf.dev/api/schemas/#ai-catalog).

| Field | Type | Description |
|---|---|---|
| `version` | string | Version number. |
| `bots` | array of object |  |
| `bots[].id` | string | Id. |
| `bots[].name` | string | Name. |
| `bots[].operator` | string | Company that operates the bot. |
| `bots[].category` | string | One of `ai_training`, `ai_search`, `ai_user_agent`, `search_engine`. |
| `bots[].methods` | array of string | Items: one of `ip`, `rdns`, `web_bot_auth`. |
| `bots[].ua` | array of string |  |
| `bots[].rdns` | array of string |  |
| `bots[].cidr_count` | integer |  |
| `bots[].signature_agents` | array of string |  |
| `sources` | array of object |  |
| `sources[].id` | string | Id. |
| `sources[].url` | string |  |
| `sources[].fetched_at` | string (date-time) or null |  |
| `sources[].ok` | boolean |  |
| `sources[].error` | string |  |
| `sources[].items` | integer |  |
| `sources[].baseline` | boolean | No stored download: the built-in data is used. |

#### Errors

| Status | Description |
|---|---|
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/ai-agents/catalog" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getAIBotCatalog();
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_ai_bot_catalog()
```



```json
{
  "version": "example",
  "bots": [
    {
      "id": "example",
      "name": "example.com",
      "operator": "OpenAI",
      "category": "ai_training",
      "methods": [
        "POST"
      ],
      "ua": [
        "example"
      ],
      "rdns": [
        "example"
      ],
      "cidr_count": 128,
      "signature_agents": [
        "example"
      ]
    }
  ],
  "sources": [
    {
      "id": "example",
      "url": "https://www.example.com/",
      "fetched_at": "2026-10-27T09:30:00Z",
      "ok": false,
      "error": "",
      "items": 0,
      "baseline": false
    }
  ]
}
```
