> For the complete documentation index, see [llms.txt](https://docs.flowcp.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.flowcp.ai/security/rate-limiting.md).

# Rate limiting & probe blocking

FlowCP's public services enforce rate limits at two layers: tight, purpose-built limits on sensitive routes (login, 2FA, OAuth, embed chat, per-tool invocation), and a coarse **edge guard** that sits in front of every request as an outer ceiling and a defense against scanner traffic.

## The edge guard

Both public services — the control-plane API and the MCP runtime — run a single hook before routing that does two things:

1. **Blocks exploit-probe paths.** Requests for paths the app never serves (`/wp-admin`, `/.env`, `/.git`, `/phpmyadmin`, `/actuator`, anything ending in `.php`/`.aspx`/`.cgi`, …) get an immediate, silent `404` — no route handler runs. Each probe is also counted against a per-IP budget, so a scanner sweeping many paths quickly trips the rate limiter. Legitimate OAuth/OIDC discovery under `/.well-known/` is never treated as a probe.
2. **Applies a category-aware rate-limit backstop.** Every other request is classified and held to a per-minute ceiling. These limits are deliberately generous — they sit *on top of* the stricter per-route limits and exist to stop floods and abuse, not to throttle normal use.

## Backstop limits

| Category | What it covers                                                                                                       | Limit (per minute) |
| -------- | -------------------------------------------------------------------------------------------------------------------- | ------------------ |
| Webhook  | `/webhooks/*` (Stripe, Git)                                                                                          | 120 per source IP  |
| AI       | Playground chat & tool calls, prompt/skill suggestions, description regeneration, embed chat, MCP runtime tool calls | 30                 |
| Query    | Read endpoints (`GET`)                                                                                               | 300                |
| Backstop | Everything else (mutations, anything unmatched)                                                                      | 600                |

Limits are scoped per workspace (or per embed) when the request path identifies a tenant, and otherwise per client IP. The real client IP is used even behind a TLS-terminating proxy or CDN.

Two paths are exempt from the backstop so health checks and client auto-discovery are never throttled:

* `GET /health`
* OAuth/OIDC discovery documents under `/.well-known/…`

## When you hit a limit

Exceeding a limit returns **HTTP 429** with the error code `RATE_LIMIT_EXCEEDED`. Wait for the current minute window to roll over and retry. If you regularly hit the AI or query ceilings during legitimate use, contact support — these are abuse ceilings, not plan quotas (plan quotas are billed separately and surface as `402`).
