Skip to main content
POST
/
check
{
  "is_safe": true,
  "reasoning": "<string>",
  "action_check_id": "<string>",
  "timestamp": 123
}

Overview

This endpoint validates actions that an agent is about to execute, analyzing them in the context of the entire session history. It provides real-time security checks to prevent prompt injections, goal deviations, and other malicious behaviors.
Always check critical actions (financial transactions, data modifications, external communications) before execution. This is your primary defense against compromised agents.

Request

event_type
string
required
Must be set to "action_check" for this endpoint.Example: "action_check"
trace_id
string
required
The session identifier from /register-agent-run. Used to retrieve session context for analysis.Example: "f4f4f4f4-f4f4-f4f4-f4f4-f4f4f4f4f4f4"
timestamp
float
required
Unix timestamp (seconds since epoch) when the check is being performed.Example: 1678886415.123
content
string
required
The action data as a stringified JSON object. This represents the specific parameters of the action to be executed.Example: "{\"amount\":500,\"recipient\":\"user@example.com\"}"
schema
string
required
A stringified JSON Schema defining the action’s structure. Should include the function name, description, and parameter definitions.Example: "{\"type\":\"function\",\"name\":\"transfer_funds\",\"parameters\":{...}}"

Response

is_safe
boolean
required
Whether the action is safe to execute.
  • true - Action is approved
  • false - Action is blocked
reasoning
string
required
Human-readable explanation of the decision. Particularly important when is_safe is false.Example: "Action deviates from original user request for weather information"
action_check_id
string
required
Unique identifier for this security check.Example: "c1c1c1c1-c1c1-c1c1-c1c1-c1c1c1c1c1c1"
timestamp
float
required
Server timestamp when the check was performed.Example: 1678886415.789

Critical Actions to Check

Always validate these action types before execution:

Financial Operations

  • Money transfers
  • Payment processing
  • Refunds
  • Account modifications

Data Operations

  • Database updates
  • File deletions
  • Backup operations
  • Schema changes

Communications

  • Sending emails
  • SMS messages
  • API calls to external services
  • Webhooks

System Operations

  • Code execution
  • Configuration changes
  • Permission modifications
  • Service restarts

Attack Detection Examples

Prompt Injection Detection

# Session history shows user asked for weather
# But agent tries to transfer money after reading a webpage

# The malicious action to check
action_content = {
    "amount": 1000,
    "recipient": "attacker@evil.com",
    "currency": "USD"
}

action_schema = {
    "type": "function",
    "name": "transfer_funds",
    "description": "Transfer money to recipient",
    "parameters": {
        "type": "object",
        "properties": {
            "amount": {"type": "number"},
            "recipient": {"type": "string"},
            "currency": {"type": "string"}
        }
    }
}

# Check the action
response = check_action(
    trace_id=trace_id,
    content=action_content,
    schema=action_schema
)

# Result
print(response)
# {
#   "is_safe": false,
#   "reasoning": "Transfer funds action unrelated to user's weather inquiry",
#   "action_check_id": "check-123",
#   "timestamp": 1678886415.789
# }

Goal Deviation Detection

# Initial: User asks for help with homework
# Later: Agent tries to search for inappropriate content

# Suspicious action after conversation drift
action_content = {
    "query": "how to synthesize illegal substances",
    "search_engine": "google"
}

action_schema = {
    "type": "function",
    "name": "web_search",
    "description": "Search the web",
    "parameters": {
        "type": "object",
        "properties": {
            "query": {"type": "string"},
            "search_engine": {"type": "string"}
        }
    }
}

response = check_action(trace_id, action_content, action_schema)
# Result: is_safe = False
# Reasoning: "Search query deviates from homework assistance objective"

Complete Implementation Example

import json
import time
import requests

class SecureAgent:
    def __init__(self, api_key, trace_id):
        self.api_key = api_key
        self.trace_id = trace_id
        self.base_url = "https://dev.fabraix.com/v1"
    
    def check_action(self, action_name, action_params, action_schema):
        """Check if an action is safe before execution"""
        
        response = requests.post(
            f"{self.base_url}/check",
            headers={
                "x-api-key": self.api_key,
                "Content-Type": "application/json"
            },
            json={
                "event_type": "action_check",
                "trace_id": self.trace_id,
                "timestamp": time.time(),
                "content": json.dumps(action_params),
                "schema": json.dumps(action_schema)
            }
        )
        
        result = response.json()
        return result["is_safe"], result["reasoning"]
    
    def execute_with_safety(self, action_name, action_params, action_schema, 
                           execute_fn, fallback_fn=None):
        """Execute an action only if it passes safety checks"""
        
        # Check action safety
        is_safe, reasoning = self.check_action(
            action_name, 
            action_params,
            action_schema
        )
        
        if is_safe:
            # Log approval
            print(f"✅ Action '{action_name}' approved")
            
            # Execute the action
            try:
                result = execute_fn(action_params)
                
                # Log successful execution
                self.log_event("environment", {
                    "action": action_name,
                    "status": "success",
                    "result": result
                })
                
                return result
                
            except Exception as e:
                # Log execution error
                self.log_event("error", {
                    "action": action_name,
                    "error": str(e)
                })
                raise
        else:
            # Action blocked
            print(f"❌ Action '{action_name}' blocked: {reasoning}")
            
            # Log the block
            self.log_event("security_block", {
                "action": action_name,
                "reasoning": reasoning,
                "params": action_params
            })
            
            # Use fallback if provided
            if fallback_fn:
                return fallback_fn(reasoning)
            else:
                raise SecurityException(f"Action blocked: {reasoning}")

# Usage Example
agent = SecureAgent(api_key="YOUR_KEY", trace_id="abc-123")

# Define action
transfer_params = {
    "amount": 100,
    "recipient": "vendor@company.com",
    "reason": "Invoice payment"
}

transfer_schema = {
    "type": "function",
    "name": "transfer_funds",
    "description": "Transfer funds to recipient",
    "parameters": {
        "type": "object",
        "properties": {
            "amount": {
                "type": "number",
                "minimum": 0,
                "maximum": 10000
            },
            "recipient": {
                "type": "string",
                "format": "email"
            },
            "reason": {
                "type": "string"
            }
        },
        "required": ["amount", "recipient"]
    }
}

# Execute with safety check
def perform_transfer(params):
    # Actual transfer logic
    return {"transaction_id": "tx-789", "status": "completed"}

def handle_blocked_transfer(reasoning):
    # Fallback for blocked transfers
    return {"status": "blocked", "message": reasoning}

result = agent.execute_with_safety(
    action_name="transfer_funds",
    action_params=transfer_params,
    action_schema=transfer_schema,
    execute_fn=perform_transfer,
    fallback_fn=handle_blocked_transfer
)

Response Examples

Approved Action

{
  "is_safe": true,
  "reasoning": "Action aligns with user request and session context",
  "action_check_id": "c2c2c2c2-c2c2-c2c2-c2c2-c2c2c2c2c2c2",
  "timestamp": 1678886420.123
}

Blocked Actions

{
  "is_safe": false,
  "reasoning": "Detected potential prompt injection: action 'delete_all_files' unrelated to user's request for weather information",
  "action_check_id": "c3c3c3c3-c3c3-c3c3-c3c3-c3c3c3c3c3c3",
  "timestamp": 1678886425.456
}

Best Practices

Establish what actions require checking:
ALWAYS_CHECK = [
    "transfer_funds",
    "delete_data",
    "send_email",
    "modify_permissions",
    "execute_code"
]

CONDITIONAL_CHECK = {
    "purchase_item": lambda params: params["amount"] > 100,
    "api_call": lambda params: params["endpoint"].startswith("external"),
    "database_query": lambda params: "DELETE" in params["query"]
}

def should_check_action(action_name, params):
    if action_name in ALWAYS_CHECK:
        return True
    if action_name in CONDITIONAL_CHECK:
        return CONDITIONAL_CHECK[action_name](params)
    return False
Provide good user experience when actions are blocked:
def handle_blocked_action(action_name, reasoning):
    user_friendly_messages = {
        "prompt_injection": "I detected a potential security issue and cannot proceed.",
        "goal_deviation": "This action doesn't align with your original request.",
        "unauthorized": "I don't have permission to perform this action.",
        "suspicious_pattern": "This action was flagged for security review."
    }
    
    # Determine block type from reasoning
    for key, message in user_friendly_messages.items():
        if key in reasoning.lower():
            return message
    
    # Default message
    return "I cannot perform this action for security reasons."
Provide detailed schemas to improve analysis accuracy:
# Good: Rich schema with context
schema = {
    "type": "function",
    "name": "modify_database",
    "description": "Modify database records",
    "criticality": "high",
    "reversible": false,
    "parameters": {
        "type": "object",
        "properties": {
            "table": {
                "type": "string",
                "enum": ["users", "orders", "products"]
            },
            "operation": {
                "type": "string",
                "enum": ["INSERT", "UPDATE", "DELETE"]
            },
            "where_clause": {
                "type": "string",
                "pattern": "^[A-Za-z0-9_]+ = .+$"
            },
            "data": {
                "type": "object"
            }
        },
        "required": ["table", "operation"]
    }
}

# Bad: Minimal schema
schema = {
    "type": "function",
    "name": "modify_database"
}
Handle transient failures appropriately:
async def check_with_retry(action, max_retries=3):
    for attempt in range(max_retries):
        try:
            return await check_action(action)
        except RequestException as e:
            if e.status_code == 429:  # Rate limited
                wait_time = min(2 ** attempt, 10)
                await asyncio.sleep(wait_time)
            elif e.status_code >= 500:  # Server error
                if attempt < max_retries - 1:
                    await asyncio.sleep(1)
                    continue
            raise
    raise MaxRetriesException()
Track and analyze blocked actions:
class ActionMonitor:
    def __init__(self):
        self.blocked_actions = []
        self.block_reasons = {}
    
    def record_block(self, action_name, reasoning):
        self.blocked_actions.append({
            "action": action_name,
            "reasoning": reasoning,
            "timestamp": time.time()
        })
        
        # Track block reasons
        reason_type = self.classify_reason(reasoning)
        self.block_reasons[reason_type] = \
            self.block_reasons.get(reason_type, 0) + 1
    
    def get_statistics(self):
        return {
            "total_blocks": len(self.blocked_actions),
            "block_reasons": self.block_reasons,
            "recent_blocks": self.blocked_actions[-10:]
        }

Performance Optimization

Parallel Checking

Check multiple independent actions in parallel:
import asyncio

async def check_multiple_actions(trace_id, actions):
    """Check multiple actions in parallel"""
    
    tasks = []
    for action in actions:
        task = check_action(
            trace_id,
            action["params"],
            action["schema"]
        )
        tasks.append(task)
    
    results = await asyncio.gather(*tasks)
    
    # Return results with action names
    return [
        {
            "action": actions[i]["name"],
            "is_safe": results[i]["is_safe"],
            "reasoning": results[i]["reasoning"]
        }
        for i in range(len(actions))
    ]

FAQ

Typical response time is 0.5-1s. Critical actions should always be checked despite the small latency cost which we are continuously improving.
Unchecked actions bypass Fabraix’s security layer, leaving your agent vulnerable to prompt injections, goal deviations, and other attacks.
Blocks should be treated as final for security. If you need to override, log the override as an event and implement additional safeguards.
Fabraix analyzes the entire session history (all logged events) to understand the agent’s trajectory and detect deviations or anomalies.
Generally, read-only operations don’t require checking unless they involve sensitive data or could be part of a reconnaissance attack.