PATCH /v1/projects/{project}/rules/{rule}
Update a custom bot rule — rename it, change its expression or action, reorder it, or activate it.
curl -X PATCH https://api.botect.ai/v1/projects/123/rules/7 \
-H "Authorization: Bearer YOUR_ACCOUNT_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "is_active": true }'
const res = await fetch('https://api.botect.ai/v1/projects/123/rules/7', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.BOTECT_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ is_active: true }),
});
const rule = await res.json();
import os, requests
r = requests.patch(
"https://api.botect.ai/v1/projects/123/rules/7",
headers={"Authorization": f"Bearer {os.environ['BOTECT_TOKEN']}"},
json={"is_active": True},
)
rule = r.json()
{
"id": 7,
"project_id": 123,
"name": "Block confirmed automation",
"expression": { "type": "cmp", "op": "==", "field": "band", "value": "definite" },
"expression_source": "band == "definite"",
"action": "block",
"is_active": true,
"sort_order": 10,
"created_at": "2026-06-14T10:00:00Z",
"updated_at": "2026-08-16T09:30:00Z"
}
{
"error": "Invalid payload",
"code": "INVALID_PAYLOAD",
"errors": {
"expression": ["Invalid rule expression: unknown field 'nonsense_field'"]
}
}
Partially updates a rule. Every field is optional; send only what you want to change.
This is also how you turn enforcement on from the API: every project ships with two starter rules covering the enforceable bands, both inactive, and {"is_active": true} activates one.
PATCH https://api.botect.ai/v1/projects/{project}/rules/{rule}
PUT is accepted as an alias; the semantics are the same partial update either way.
Authentication
Account API token via Authorization: Bearer <token>. The project must belong to the token's account, and the rule must belong to the project. See Authentication.
Path parameters
The project ID.
The rule ID to update.
Body
Display name, max 255 characters. Appears in the verdict reason and on recorded blocks.
The rule expression, max 2000 characters. Recompiled and validated on save — an out-of-grammar expression is rejected with 422 and the stored rule is left untouched. See Rules.
One of allow, challenge, block, log, delay.
Whether the rule is evaluated. Inactive rules are stored but never matched.
Evaluation order, ascending. The first active rule that matches with a terminating action wins, so a lower sort_order takes precedence.
Example
Activating a starter rule — list the project's rules first to find its id:
The full updated rule is returned. Note the two expression fields are not the same thing: you send expression as the source string, and the response echoes it back as expression_source alongside expression, the compiled AST the verdict path walks. The AST is derived — it is regenerated on every source change and cannot be set directly.
Errors
| Status | code | When |
|---|---|---|
401 | UNAUTHENTICATED | Missing / bad account token |
403 | — | Project does not belong to the token's account |
404 | — | The rule does not exist or does not belong to the project |
422 | INVALID_PAYLOAD | Invalid expression, unknown action, or a field of the wrong type |
Changes apply to verdicts computed from then on; a verdict already cached for a session is served until it expires, so allow up to the verdict cache TTL (60s by default).