diff --git a/app/en/operate/governance/contextual-access/_meta.tsx b/app/en/operate/governance/contextual-access/_meta.tsx
index 5fdf59830..e41331ffd 100644
--- a/app/en/operate/governance/contextual-access/_meta.tsx
+++ b/app/en/operate/governance/contextual-access/_meta.tsx
@@ -4,6 +4,9 @@ export const meta: MetaRecord = {
"how-hooks-work": {
title: "How Hooks Work",
},
+ "rate-limiting": {
+ title: "Rate Limiting",
+ },
examples: {
title: "Running an Extension",
},
diff --git a/app/en/operate/governance/contextual-access/build-your-own/page.mdx b/app/en/operate/governance/contextual-access/build-your-own/page.mdx
index 8dc5abaa1..4aa4d7531 100644
--- a/app/en/operate/governance/contextual-access/build-your-own/page.mdx
+++ b/app/en/operate/governance/contextual-access/build-your-own/page.mdx
@@ -215,6 +215,8 @@ Webhook servers can use tool metadata to make more granular decisions:
| `CHECK_FAILED` | Deny the operation; `error_message` is shown to the agent |
| `RATE_LIMIT_EXCEEDED` | Deny with rate-limit semantics |
+For per-tool call caps that need no webhook server, Arcade also enforces [built-in rate limits](/operate/governance/contextual-access/rate-limiting).
+
## Authentication
diff --git a/app/en/operate/governance/contextual-access/page.mdx b/app/en/operate/governance/contextual-access/page.mdx
index 71beecfbd..033ce4be9 100644
--- a/app/en/operate/governance/contextual-access/page.mdx
+++ b/app/en/operate/governance/contextual-access/page.mdx
@@ -39,6 +39,10 @@ You only implement the hooks you need. Configure everything through the Dashboar
title="How Hooks Work"
href="/operate/governance/contextual-access/how-hooks-work"
/>
+
+
+### Create a rate limit
+
+Navigate to **Contextual Access** in the Arcade Dashboard, click **Add Extension**, and choose the rate limit type.
+
+### Pick a scope
+
+Bind the rate limit to the organization to apply it across all projects, or to a single project.
+
+### Add rules
+
+Each rule row takes a tool matcher, a limit, and a time window. You can add up to 100 rules, and each matcher can appear only once.
+
+### Activate
+
+The **Active** toggle controls enforcement. Inactive rate limits are kept but not enforced, so you can stage rules before turning them on.
+
+
+
+## Configure via the API
+
+Create a rate limit with the plugins API. The example below caps `Slack.SendMessage` at 5 calls per minute, every other Slack tool at 100 calls per hour each, and everything else at 1000 calls per day each:
+
+```bash
+curl -s -X POST "https://api.arcade.dev/v1/orgs/{org_id}/projects/{project_id}/plugins" \
+ -H "Authorization: Bearer $ARCADE_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "plugin_type": "rate_limit",
+ "name": "Production tool limits",
+ "rate_limit_config": {
+ "rules": [
+ { "match": "Slack.SendMessage", "limit": 5, "time_unit": "m" },
+ { "match": "Slack.*", "limit": 100, "time_unit": "h" },
+ { "match": "*", "limit": 1000, "time_unit": "d" }
+ ]
+ }
+ }'
+```
+
+To bind a rate limit to the organization instead of a project, post to `/v1/orgs/{org_id}/plugins`. The [API reference](/references/api) documents the full plugins API, including listing, updating, and deleting.
+
+## When the platform cannot verify a limit
+
+If the Engine cannot reach its counting backend, a matched call's limit cannot be verified. By default the call is **rejected**: a degraded platform must not silently stop enforcing the caps you rely on.
+
+For rules that protect availability rather than enforce a hard cap, you can opt individual rules into allowing unverified calls by setting `allow_on_unavailable` on the rule:
+
+```json
+{ "match": "Slack.*", "limit": 100, "time_unit": "h", "allow_on_unavailable": true }
+```
+
+A rejected unverified call sees its own denial message:
+
+```text
+The rate limit for Slack.SendMessage could not be verified; the call was rejected.
+```
+
+
+ The unverified denial intentionally carries no detail about what failed, so
+ callers cannot tell which part of the platform is degraded.
+
+
+## Next steps
+
+- [How hooks work](/operate/governance/contextual-access/how-hooks-work) - Where rate limits fit in the hook pipeline
+- [Build your own](/operate/governance/contextual-access/build-your-own) - Enforce custom policies, including rate-limit responses, from your own webhook server
+- [API reference](/references/api) - Full plugins API documentation
diff --git a/public/llms.txt b/public/llms.txt
index 5b28d6ec4..f05fe3361 100644
--- a/public/llms.txt
+++ b/public/llms.txt
@@ -139,6 +139,7 @@ Arcade docs serve two audiences. Start with the path that matches your goal:
- [Operate Arcade](https://docs.arcade.dev/en/operate): Documentation page
- [Organize your MCP server and tools](https://docs.arcade.dev/en/build/create-tools/tool-basics/organize-mcp-tools): Documentation page
- [Providing useful tool errors](https://docs.arcade.dev/en/build/create-tools/error-handling/useful-tool-errors): Documentation page
+- [Rate Limiting](https://docs.arcade.dev/en/operate/governance/contextual-access/rate-limiting): Documentation page
- [Remote MCP servers](https://docs.arcade.dev/en/operate/governance/remote-mcp-servers): Documentation page
- [RetryableToolError in Arcade](https://docs.arcade.dev/en/build/create-tools/error-handling/retry-tools): Documentation page
- [Run evaluations](https://docs.arcade.dev/en/build/create-tools/evaluate-tools/run-evaluations): Documentation page