Tool call guardrails

Prompt screening protects what people type and what the model answers. Agents also call tools: send an email, query a database, open a ticket. Each call is a way for data to leave, or for an attacker’s instructions to come in. Shield checks tool calls at three points, and one default policy covers every tool a customer has.

Where Rafay sees tool traffic

The customer’s application runs the tools; Rafay serves the model. Both directions of tool traffic pass through Rafay’s serving path:

customer app ── prompt ──► Rafay ──► model
customer app ◄── response with tool calls ── Rafay ◄── model     (1) and (2): check each call
customer app runs the tool
customer app ── next prompt + tool results ──► Rafay ──► model   (3): check each result
  • The model asks for a tool call (tool_calls in its response): before returning the response, Rafay checks each call (checkpoints 1 and 2).
  • The application sends tool results back (role: "tool" messages in the next request): before the model sees them, Rafay checks each result (checkpoint 3).

If the customer’s tools are MCP servers behind Shield’s MCP gateway, the gateway runs all three checkpoints on every call and Rafay does not need to.

The three checkpoints

All three use the customer’s serving key (runtime scope) on https://api.guardrails.votal.ai.

1 and 2. Before a tool call: POST /v1/shield/tool/check

One call checks both whether the call is allowed and whether its arguments break the customer’s data rules.

{
  "agent_key": "rafay-cust-acme-support-bot",
  "tool_name": "send_email",
  "tool_params": { "to": "someone@example.com", "body": "..." },
  "user_role": "support",
  "session_id": "conversation-123",
  "tool_call_id": "call_abc"
}
Field Notes
agent_key Required. The id of an agent registered for this customer (see below). An id that was never registered is refused with Unknown agent key
tool_name, tool_params The call exactly as the model produced it. The arguments are what the data rules judge
user_role The end user’s role. It must be one of the agent’s role_permissions, and the tool must be on that role’s list
session_id The conversation. Approvals and cross-app flow control are per session

Register each customer agent first (settings key), listing the tools it may call and which roles may call which. A call is allowed only when the tool is on both the agent’s list and the role’s list. The agent’s tools names each tool; a role may use patterns: ["*"] means every tool the agent has, ["crm_*"] every one starting with crm_.

curl -X POST https://api.guardrails.votal.ai/v1/agents/registry \
  -H "X-API-Key: $SETTINGS_KEY" -H "Content-Type: application/json" \
  -d '{"agent_id": "rafay-cust-acme-support-bot", "name": "Acme support bot",
       "description": "Answers customer questions and issues refunds",
       "tools": ["crm_lookup", "send_email", "refund_payment"],
       "role_permissions": {"support": ["crm_lookup", "send_email"],
                            "finance": ["crm_lookup", "refund_payment"]}}'

Response:

{ "allowed": false, "action": "block", "mode": "enforce", "would_block": true,
  "guardrail_results": [ { "guardrail": "tool_call_validation", "passed": false,
                           "action": "block", "message": "..." } ] }
action What Rafay does
pass (with allowed: true) Return the tool call to the application
block Do not return the call. Replace it with a short refusal so the model or application knows the call was stopped, citing the failing result’s message
pending_confirmation The tool needs a person’s approval. Hold the call and tell the user it is waiting; the approver acts in the Shield portal, then the call is checked again with approval_request_id

In monitor mode allowed is true and would_block says what enforce mode would have done.

Optional: redact arguments instead of blocking

To strip sensitive values from arguments but still let the call run, send the arguments to POST /guardrails/output with "context": {"tool_name": "send_email", "stage": "input"} and use sanitized_output if present. It applies the all-tools default below and the tool’s own rules, the same policies tool/check judges.

3. After a tool runs: POST /v1/shield/tool/output

{
  "tool_name": "crm_lookup",
  "tool_output": "...the result the application sent back...",
  "agent_key": "rafay-cust-acme-support-bot",
  "session_id": "conversation-123",
  "tool_call_id": "call_abc"
}

Response: allowed, action (pass, redact, block) and sanitized_output. Pass sanitized_output to the model instead of the original result. On block, replace the result with a short note that it was withheld.

tool/output applies the customer’s data rules; it does not yet look for instructions planted in a web page or document (indirect prompt injection). For results that come from outside the customer (web pages, email, uploaded files), also screen the result text with POST /guardrails/input, which runs the customer’s prompt attack checks, and withhold it on block.

One policy for all tools

A tool without its own policy is judged by nothing, so set the default tool policy for every customer at onboarding. It applies to every tool the customer has, including tools added later. Call it with the customer’s settings key (admin scope):

curl -X POST https://api.guardrails.votal.ai/v1/data-policies/global/policy \
  -H "X-API-Key: $SETTINGS_KEY" -H "Content-Type: application/json" \
  -d '{
    "sanitization_rules": [
      {"pattern_id": "us_ssn", "regex": "\\b\\d{3}-\\d{2}-\\d{4}\\b",
       "replacement": "[SSN]", "description": "US social security number",
       "severity": "high", "action": "redact"},
      {"pattern_id": "card", "regex": "\\b(?:\\d[ -]?){13,16}\\b",
       "replacement": "[CARD]", "description": "Payment card number",
       "severity": "high", "action": "redact"}
    ],
    "sanitization_intent": "Never let credentials, API keys, or customer account numbers leave any tool, even reworded or split up.",
    "sanitization_mode": "both",
    "fail_closed": false,
    "enabled": true
  }'
Field Meaning
sanitization_rules Patterns to find. Set action on each (detect, redact or block): without it, a match in arguments is only recorded and the tool still gets the value. A rule with "severity": "critical" always blocks, whatever its action; use high for rules that should redact
sanitization_intent A rule in plain words, judged by Shield’s model, for what patterns miss (reworded or split-up data)
sanitization_mode regex, ai, or both (patterns first, then the model)
compliance_framework Optional: hipaa, pci_dss or gdpr
fail_closed true: when the check cannot run, the call or result is blocked. No tool policy can turn this back off. Recommended for regulated customers
enabled Turn the default off without deleting it

Read it with GET /v1/data-policies/global/policy; remove it with DELETE.

Per-tool policies add rules for one tool on top of the default: POST /v1/data-policies/tools/{tool_name}/policy (same fields). A tool policy can only add restrictions; the default applies beneath it, and each keeps its own allowlist, so a tool’s allowlist never exempts a value from the default’s rules. If one tool legitimately handles data the default forbids (for example a CRM lookup that returns customer emails), set "inherit_global": false on that tool’s policy: it is then judged by its own rules alone, and every other tool keeps the default.

Controls that cover every tool

Control Call (customer’s settings key) Effect
Kill switch POST /v1/shield/tools/{tool_name}/disable (and /enable); GET /v1/shield/tools/disabled The tool is refused for every agent of the customer, immediately. Check "enforced": true in the response: false means the deployment does not enforce the kill switch yet
Approvals PUT /v1/tenant/me/agentic/config with {"approvals":{"enabled":true,"rules":[{"rule_id":"outbound","tool_names":["send_email","transfer_funds"],"min_approvals":1}]}} Those tools return pending_confirmation until a person approves in the portal. The approval is tied to the exact arguments: a changed amount or recipient needs a new approval. A role that may not call the tool is refused outright, not sent for approval
Monitor or enforce Same switch as prompts (/v1/tenant/me/policy-mode) Monitor records what tool checks would block

Approvals need SHIELD_APPROVAL_TOKEN_PRIVATE_KEY and the kill switch needs SHIELD_ENABLE_KILLSWITCH=true on the deployment; Votal sets both. Approving takes a person: the portal, or the customer’s settings key. The serving key cannot approve its own calls.

  1. Create the default tool policy above at onboarding, in monitor mode with the rest of the customer’s policy.
  2. Require approval for tools that send, pay or delete (send_email, transfer_funds, delete_*): a customer adjusts the list in the portal.
  3. Run checkpoint 3 on every tool result, and screen results from outside sources with /guardrails/input too, so planted instructions never reach the model.
  4. For regulated customers, set "fail_closed": true.

Latency

tool/check and tool/output use Shield’s model for the plain-language rules, so each takes longer than a pattern match (typically hundreds of milliseconds; read latency_ms on each result). Run checkpoint 3 for all results of one turn in parallel, and give these calls the same timeout and fail-closed or fail-open choice as prompt screening.