Tool rejection plan

Prevent unintended tool calls using conditions based on conversation state

Overview

A rejection plan lets you prevent a tool from executing when certain conditions are met. You attach it to any tool call and it evaluates the recent conversation state to decide whether to reject the call.

  • If all conditions match (AND logic), the tool call is rejected.
  • To express OR at the top level, use a single group condition with operator: "OR".
  • If conditions is empty or omitted, the tool always executes.

Use on any tool call, e.g., Assistant.hooks.do[type=tool].tool.rejectionPlan.

Schema

  • conditions: Array of condition objects. Defaults to [].
    • Types:
      • RegexCondition: Match message content with a regex
        • type: “regex”
        • regex: String pattern. RegExp.test-style substring matching. Escape backslashes in JSON (e.g., "\\bhello\\b"). Supports inline flags like (?i) for case-insensitive.
        • target (optional): Which message to inspect
          • role: user | assistant
          • position: Integer index in history (default -1 for the most recent). Negative counts from the end; 0 is the first message
        • negate (optional): When true, the condition matches if the regex does NOT match (default false)
      • LiquidCondition: Evaluate a Liquid template that must output exactly "true" or "false"
        • type: “liquid”
        • liquid: The template. You can access messages (recent chat messages), now, and assistant variables. Useful filters include last, where, and reverse
      • GroupCondition: Combine multiple conditions
        • type: “group”
        • operator: AND | OR
        • conditions: Nested list of conditions (can recursively nest groups)

Examples

1) Reject endCall unless the user says goodbye

1{
2 "conditions": [
3 {
4 "type": "regex",
5 "regex": "(?i)\\b(bye|goodbye|farewell|see you later|take care)\\b",
6 "target": { "position": -1, "role": "user" },
7 "negate": true
8 }
9 ]
10}

2) Reject transfer if the user is actually asking a question

1{
2 "conditions": [
3 {
4 "type": "regex",
5 "regex": "\\?",
6 "target": { "position": -1, "role": "user" }
7 }
8 ]
9}

3) Reject transfer if the user hasn’t mentioned transfer recently (Liquid)

Liquid template for readability:

1{% assign recentMessages = messages | last: 5 %}
2{% assign userMessages = recentMessages | where: 'role', 'user' %}
3{% assign mentioned = false %}
4{% for msg in userMessages %}
5 {% if msg.content contains 'transfer' or msg.content contains 'connect' or msg.content contains 'representative' %}
6 {% assign mentioned = true %}
7 {% endif %}
8{% endfor %}
9{% if mentioned %}false{% else %}true{% endif %}

Wired into a rejection plan:

1{
2 "conditions": [
3 {
4 "type": "liquid",
5 "liquid": "{% assign recentMessages = messages | last: 5 %}{% assign userMessages = recentMessages | where: 'role', 'user' %}{% assign mentioned = false %}{% for msg in userMessages %}{% if msg.content contains 'transfer' or msg.content contains 'connect' or msg.content contains 'representative' %}{% assign mentioned = true %}{% endif %}{% endfor %}{% if mentioned %}false{% else %}true{% endif %}"
6 }
7 ]
8}

4) Top-level OR using a group

1{
2 "conditions": [
3 {
4 "type": "group",
5 "operator": "OR",
6 "conditions": [
7 { "type": "regex", "regex": "(?i)\\bcancel\\b", "target": { "role": "user" } },
8 { "type": "regex", "regex": "(?i)\\bstop\\b", "target": { "role": "user" } }
9 ]
10 }
11 ]
12}

Normal tool call example

Attach rejectionPlan directly on a tool in your assistant configuration (model.tools):

1{
2 "model": {
3 "provider": "openai",
4 "model": "gpt-4o",
5 "messages": [
6 { "role": "system", "content": "Only end the call after the user says goodbye." }
7 ],
8 "tools": [
9 {
10 "type": "endCall",
11 "rejectionPlan": {
12 "conditions": [
13 {
14 "type": "regex",
15 "regex": "(?i)\\b(bye|goodbye|farewell|see you later|take care)\\b",
16 "target": { "position": -1, "role": "user" },
17 "negate": true
18 }
19 ]
20 }
21 }
22 ]
23 }
24}

Another example: transferCall with rejection

1{
2 "model": {
3 "provider": "openai",
4 "model": "gpt-4o",
5 "messages": [
6 { "role": "system", "content": "Transfer only if the user clearly asks to be connected." }
7 ],
8 "tools": [
9 {
10 "type": "transferCall",
11 "destinations": [
12 { "type": "number", "number": "+1234567890" }
13 ],
14 "rejectionPlan": {
15 "conditions": [
16 {
17 "type": "group",
18 "operator": "OR",
19 "conditions": [
20 { "type": "regex", "regex": "(?i)\\bconnect\\b", "target": { "role": "user" } },
21 { "type": "regex", "regex": "(?i)\\btransfer\\b", "target": { "role": "user" } }
22 ]
23 },
24 {
25 "type": "regex",
26 "regex": "\\?",
27 "target": { "position": -1, "role": "user" },
28 "negate": true
29 }
30 ]
31 }
32 }
33 ]
34 }
35}

Tips

  • Escape backslashes in regex patterns: write \\b in JSON to mean \b in the regex engine.
  • position: -1 targets the most recent message. Omit role to target regardless of role.
  • Prefer a group with operator: "OR" for disjunctive logic at the top level.