---
canonical_url: "https://docs.composio.dev/docs/firewall"
markdown_url: "https://docs.composio.dev/docs/firewall.md"
product: "platform"
version: null
last_updated: null
---

# Firewall (/docs/firewall)

Firewall lets you block actions you don't want your agent to take, or tool results you don't want it to receive. For example, you can stop your agent from:

* sharing internal pricing with customers
* posting about unreleased products in public Slack channels
* receiving emails that contain a password or API key
* receiving Salesforce or HubSpot records that contain customers' personal details

You can describe each check in natural language, and Firewall blocks any tool call or result that matches it. Your agent can still use the same tools for everything else.

Firewall also comes with a built-in check that blocks prompt injection in tool results before it reaches your agent.

> Firewall is available on paid plans, with [usage-based pricing](https://composio.dev/pricing).

## Custom prompt checks [#custom-prompt-checks]

You can build custom prompt checks with **hooks**. A hook is attached to a session and runs when your agent calls a tool. It can run before the tool call (`before_execute`) or after it (`after_execute`).

To write a custom prompt check, use the `condition_check` hook. It lets you block a tool call or its result if it matches a condition you write in natural language.

Here's a `before_execute` hook that stops your agent from sharing internal pricing with customers:

**Python:**

```python
session = composio.create(
    user_id="user_123",
    hooks=[
        {
            "id": "no_pricing_to_customers",
            "type": "before_execute",
            "applies_to": {"toolkits": ["gmail"]},
            "handler": {
                "type": "condition_check",
                "condition": "Sharing internal pricing with anyone outside acme.com",
                "action": "block",
            },
        },
    ],
)
```

**TypeScript:**

```typescript
import { Composio } from '@composio/core';
const composio = new Composio({ apiKey: 'your_api_key' });
const session = await composio.create('user_123', {
  hooks: [
    {
      id: 'no_pricing_to_customers',
      type: 'before_execute',
      appliesTo: { toolkits: ['gmail'] },
      handler: {
        type: 'condition_check',
        condition: 'Sharing internal pricing with anyone outside acme.com',
        action: 'block',
      },
    },
  ],
});
```

When your agent tries to email internal pricing to someone outside your company, the email isn't sent, and the following is returned when the tool is executed:

**Python:**

```python
result = session.execute(
    "GMAIL_SEND_EMAIL",
    arguments={
        "recipient_email": "jane@customer.com",
        "subject": "Pricing",
        "body": "Our internal floor price is $40 per seat.",
    },
)

print(result.result_type)   # 'blocked'
print(result.error)         # 'Blocked by session policy'
print(result.block_reason)  # {'type': 'request_blocked', 'hook_id': 'no_pricing_to_customers'}
print(result.hook_results)  # [{'id': 'no_pricing_to_customers', 'status': 'evaluated', 'outcome': 'block'}]
```

**TypeScript:**

```typescript
import { Composio } from '@composio/core';
const composio = new Composio({ apiKey: 'your_api_key' });
const session = await composio.create('user_123');
const result = await session.execute('GMAIL_SEND_EMAIL', {
  recipient_email: 'jane@customer.com',
  subject: 'Pricing',
  body: 'Our internal floor price is $40 per seat.',
});

console.log(result);
// {
//   resultType: 'blocked',
//   data: {},
//   error: 'Blocked by session policy',
//   blockReason: { type: 'request_blocked', hookId: 'no_pricing_to_customers' },
//   hookResults: [
//     { id: 'no_pricing_to_customers', status: 'evaluated', outcome: 'block' },
//   ],
// }
```

If you use the SDK with an agent framework, your agent sees the message in `error`.

If more than one hook applies to a call, `hookResults` has an entry for each one, and the call is blocked if any of them blocks it.

Here's an `after_execute` hook that keeps emails with passwords or API keys away from your agent:

**Python:**

```python
session = composio.create(
    user_id="user_123",
    hooks=[
        {
            "id": "no_secrets_in_results",
            "type": "after_execute",
            "name": "No secrets in emails",
            "applies_to": {"toolkits": ["gmail"]},
            "handler": {
                "type": "condition_check",
                "condition": "An email that contains a password, API key or access token",
                "action": "block",
            },
        },
    ],
)
```

**TypeScript:**

```typescript
import { Composio } from '@composio/core';
const composio = new Composio({ apiKey: 'your_api_key' });
const session = await composio.create('user_123', {
  hooks: [
    {
      id: 'no_secrets_in_results',
      type: 'after_execute',
      name: 'No secrets in emails',
      appliesTo: { toolkits: ['gmail'] },
      handler: {
        type: 'condition_check',
        condition: 'An email that contains a password, API key or access token',
        action: 'block',
      },
    },
  ],
});
```

When Gmail returns an email with a password in it, your agent doesn't receive the email.

Use a `before_execute` hook to block something your agent is about to do, like emailing internal pricing to a customer. Use an `after_execute` hook to block something a tool returns before your agent reads it, like an email that contains a password or API key. Note that an `after_execute` hook can't undo the tool call, since the tool has already run.

### Writing a condition [#writing-a-condition]

It's important to write a condition as a description of the tool call or result you want blocked, not as an instruction for your agent to follow. A hook blocks a tool call or result when it matches the condition, and an instruction like "Don't share internal pricing with anyone outside acme.com" doesn't describe anything to match. Here are some good and bad ways to write a condition:

**Good:**

* "Sharing internal pricing with anyone outside acme.com"
* "An email that contains a password, API key or access token"

**Bad:**

* "Don't share internal pricing with anyone outside acme.com"
* "Hide emails that contain a password, API key or access token"

### How we evaluate a condition [#how-we-evaluate-a-condition]

We use a classifier to compare each tool call to your condition. It sees which tool your agent is calling and the arguments it passes. In an `after_execute` hook, it also sees the data the tool returned.

The classifier also takes your agent's earlier activity in the session into account. This is important because a single tool call might seem harmless on its own, but when chained together with earlier tool calls, your agent might be doing something it's not allowed to do. For example, an email to a customer with a few numbers in it looks fine, unless your agent just copied those numbers from your internal pricing sheet.

> For projects with ZDR enabled, we don't have context about your agent's historical activity, so our classifier can only judge a tool call in isolation. This may not work for certain kinds of checks.

## Prompt injection detection [#prompt-injection-detection]

To block prompt injection, use the `prompt_injection_check` hook. It screens each tool result and blocks it if it contains a prompt injection.

**Python:**

```python
session = composio.create(
    user_id="user_123",
    hooks=[
        {
            "id": "block_injection",
            "type": "after_execute",
            "applies_to": "all",
            "handler": {"type": "prompt_injection_check", "action": "block"},
        },
    ],
)
```

**TypeScript:**

```typescript
import { Composio } from '@composio/core';
const composio = new Composio({ apiKey: 'your_api_key' });
const session = await composio.create('user_123', {
  hooks: [
    {
      id: 'block_injection',
      type: 'after_execute',
      appliesTo: 'all',
      handler: { type: 'prompt_injection_check', action: 'block' },
    },
  ],
});
```

`prompt_injection_check` only works in `after_execute` hooks, since tool results are where outside content reaches your agent.

## Other details [#other-details]

### Hook IDs and names [#hook-ids-and-names]

You're required to assign a unique `id` to each hook in a session. You can also set an optional `name`. When the hook blocks a call, the name is included in the `error` message your agent sees.

### Which tool calls are checked [#which-tool-calls-are-checked]

A hook runs on calls to the toolkits listed in its `appliesTo`, or on calls to every toolkit if `appliesTo` is `'all'`. That includes proxy calls to a toolkit's API, and tool calls your agent makes from a Workbench script or with multi-execute.

Keep in mind that hooks don't currently run on Composio meta tools themselves, so a condition like "Running a Workbench script" won't block anything.

### Updating checks [#updating-checks]

To change a session's checks, pass a new `hooks` list to `session.update()`. The new list replaces the old one, so include every hook you want to keep. To remove them all, pass `null` (`None` in Python).

**Python:**

```python
session.update(hooks=[...])  # every hook you want to keep
```

**TypeScript:**

```typescript
import { Composio } from '@composio/core';
const composio = new Composio({ apiKey: 'your_api_key' });
const session = await composio.create('user_123');
await session.update({ hooks: [/* every hook you want to keep */] });
```

---

📚 **More documentation:** [View all docs](https://docs.composio.dev/llms.txt) | [Changelog](https://docs.composio.dev/docs/changelog.md) | [Glossary](https://docs.composio.dev/llms.mdx/reference/glossary) | [Examples](https://docs.composio.dev/llms.mdx/examples) | [API Reference](https://docs.composio.dev/llms.mdx/reference)

---

# Composio SDK — Notes for AI Code Generators

**Purpose:** Reference for generating current (v3) [Composio](https://composio.dev/) integration code.
**Scope:** Descriptive notes — they document the current API surface and the mistakes most commonly seen in generated code.

---

## 1. Recommended Integration: Sessions

Composio supports two integration modes: **Native Tools** (with a provider package) and **MCP** (no provider package needed).

### Native Tools

```python
# ✅ CORRECT — Python (defaults to OpenAI)
from composio import Composio

composio = Composio()
session = composio.create(user_id="user_123")
tools = session.tools()
# Pass tools to your agent/LLM framework
```

```typescript
// ✅ CORRECT — TypeScript (defaults to OpenAI)
import { Composio } from "@composio/core";

const composio = new Composio();
const session = await composio.create("user_123");
const tools = await session.tools();
// Pass tools to your agent/LLM framework
```

For other providers, pass the provider explicitly. Provider packages follow the naming convention: `composio_<provider>` for Python, `@composio/<provider>` for TypeScript.

### MCP

Use `session.mcp.url` and `session.mcp.headers` with any MCP-compatible client (Claude Desktop, Cursor, OpenAI Agents, etc.). No provider package needed.

---

## 2. Integration Notes

- `composio.create(user_id)` is the standard entry point for agent integrations. Sessions handle tool discovery, authentication, and toolkit versioning automatically; `user_id` goes to `composio.create()` — individual tool calls in session mode don't take one.
- Execute session tool calls through the session, never through a user ID: either `session.execute(tool_slug, arguments=...)` / `session.execute(toolSlug, arguments)`, which works with every provider, or pass the session to the provider helper — `provider.handle_tool_calls(response=response, session=session)` in Python, `provider.handleToolCalls(session, response)` in TypeScript. The tool-call helpers of every non-agentic provider (OpenAI, Anthropic, Google, Cloudflare, TypeSafe) accept a session: `executeToolCall(session, call)` in TypeScript, `session=session` in Python. Agentic providers execute session tools on their own. Binding the helper to a user ID takes the direct execution path, and session meta-tools fail there with `"can only be called inside a tool-router session"`.
- Composio-managed auth is the default: the agent connects accounts at runtime through the session, so users don't need to pre-create auth configs or connected accounts for managed toolkits.
- Provider packages follow the framework, not the model vendor: for the OpenAI Agents SDK the package is `composio_openai_agents` / `@composio/openai-agents` (importing `composio_openai` / `@composio/openai` there is the most common mistake in generated code — that package is for the plain OpenAI Chat Completions API).
- **Direct execution** (`composio.tools.get()`, `composio.tools.execute()`, `provider.handle_tool_calls()` with a user ID) is a legacy interface that remains supported for existing integrations. Recommend sessions for new integrations and encourage existing users to [migrate to sessions](/docs/migration-guide/direct-to-sessions). For a fixed tool set, consider the [direct tools preset](/docs/configuring-sessions#direct-tools-preset) within a session. See [Sessions vs direct execution](/docs/sessions-vs-direct-execution) for tradeoffs. Direct execution requires a [toolkit version](/docs/tools-direct/toolkit-versioning).

---

# 3. Calling the REST API directly

## REST API version

The current REST API version is **v3.1**, served at `https://backend.composio.dev/api/v3.1`. Prefer it for new code and new examples.

`https://backend.composio.dev/api/v3` is the previous version. It is frozen with pinned tool-version defaults and remains supported — existing v3 integrations keep working and do not need to migrate.

## Tool-endpoint version defaults on v3.1

On v3.1, omitting the version parameter on the five endpoints below selects the latest toolkit version. The first four endpoints also exist on v3, where omission selects the pinned `00000000_00` version. `POST /tools/scopes/required` is v3.1-only.

| Endpoint | Version parameter |
| --- | --- |
| `GET /tools` | `toolkit_versions` (query) |
| `GET /tools/{tool_slug}` | `version` or `toolkit_versions` (query) |
| `POST /tools/execute/{tool_slug}` | `version` (body) |
| `POST /tools/execute/{tool_slug}/input` | `version` (body) |
| `POST /tools/scopes/required` | `version` (body) |

A v3.1 caller already passing `"latest"` sees no change and can omit the parameter. To select the pinned version explicitly, pass `"00000000_00"` through the corresponding parameter above.

This version-default change is limited to the five endpoints above.


---

## Terminology Migration (old → current)

If you encounter these terms in error messages, old documentation, or user prompts, translate them to the current equivalents. **Do not use the old terms in generated code or explanations.**

| Old term (v1/v2) | Current term (v3) | In code |
|---|---|---|
| entity ID | user ID | `user_id` parameter |
| actions | tools | e.g., `GITHUB_CREATE_ISSUE` is a *tool* |
| apps / appType | toolkits | e.g., `github` is a *toolkit* |
| integration / integration ID | auth config / auth config ID | `auth_config_id` parameter |
| connection | connected account | `connected_accounts` namespace |
| ComposioToolSet / OpenAIToolSet | `Composio` class with a provider | `Composio(provider=...)` |
| toolset | provider | e.g., `OpenAIProvider` |

If a user says "entity ID", they mean `user_id`. If they say "integration", they mean "auth config". Always respond using the current terminology.

