# Creating triggers (/docs/setting-up-triggers/creating-triggers)

Create a trigger to start receiving events. A trigger watches for a specific event (e.g., `GITHUB_COMMIT_EVENT`) on a specific user's connected account. For an overview of how triggers work, see [Triggers](/docs/triggers).

> **Prerequisites**: * An [auth config](/docs/authentication#how-composio-manages-authentication) for the toolkit you want to monitor
  * A connected account for the user whose events you want to capture
  * A [webhook subscription](/docs/setting-up-triggers/subscribing-to-events) on the project, so events have somewhere to land

You can create triggers using the [SDK](#using-the-sdk) or the Composio [dashboard](#using-the-dashboard). With Composio-managed OAuth, Composio handles all webhook endpoint setup for you. If you bring your own OAuth app and the trigger type's `requires_webhook_endpoint` flag is true, the create call returns a `400` until you set up the endpoint — see [Configuring the webhook endpoint](#configuring-the-webhook-endpoint) below.

# Using the SDK

Before creating a trigger, inspect the trigger type. The response tells you what config it requires and whether you need to set up a webhook endpoint first.

> When you pass a `user_id`, the SDK automatically finds the user's connected account for the relevant toolkit. If the user has multiple connected accounts for the same toolkit, it uses the most recently created one. You can also pass a `connected_account_id`/`connectedAccountId` directly if you need more control.

**Python:**

```python
from composio import Composio

composio = Composio()
user_id = "user-id-123435"

# Inspect the trigger type
trigger_type = composio.triggers.get_type("GITHUB_COMMIT_EVENT")
print(trigger_type.config)
# {"properties": {"owner": {...}, "repo": {...}}, "required": ["owner", "repo"]}

# Create trigger with the required config
trigger = composio.triggers.create(
    slug="GITHUB_COMMIT_EVENT",
    user_id=user_id,
    trigger_config={"owner": "your-repo-owner", "repo": "your-repo-name"},
)
print(f"Trigger created: {trigger.trigger_id}")
```

**TypeScript:**

```typescript
import { Composio } from '@composio/core';

const composio = new Composio();
const userId = 'user-id-123435';

// Inspect the trigger type
const triggerType = await composio.triggers.getType("GITHUB_COMMIT_EVENT");
console.log(triggerType.config);
// {"properties": {"owner": {...}, "repo": {...}}, "required": ["owner", "repo"]}

// Create trigger with the required config
const trigger = await composio.triggers.create(
    userId,
    'GITHUB_COMMIT_EVENT',
    {
        triggerConfig: {
            owner: 'your-repo-owner',
            repo: 'your-repo-name'
        }
    }
);
console.log(`Trigger created: ${trigger.triggerId}`);
```

> Trigger instances default to the `'latest'` toolkit version. If your code parses trigger payloads programmatically against a fixed schema, you can pin a specific version at SDK initialization. See [Toolkit Versioning](/docs/tools-direct/toolkit-versioning#choosing-between-latest-and-a-pinned-version) for details.

# Using the dashboard

1. Navigate to [Auth Configs](https://platform.composio.dev?next_page=%2Fauth-configs) and select the auth config for the relevant toolkit
2. Navigate to **Active Triggers** and click **Create Trigger**
3. Select the connected account for which you want to create a trigger
4. Choose a trigger type and fill in the required configuration. If the trigger requires a webhook endpoint, the dashboard prompts you to configure one before saving.
5. Click **Create Trigger**

# Configuring the webhook endpoint

Skip this section unless all three are true: you bring your own OAuth app, the trigger type's `requires_webhook_endpoint` flag is true, and no matching endpoint exists yet (the create call returned a `400`). With Composio-managed OAuth, Composio configures the webhook endpoint for you transparently — none of these steps apply.

A `webhook_endpoint` is a project-scoped resource that owns the ingress URL the provider posts to and the signing secret used to verify each request. Endpoints are keyed by `(toolkit_slug, project_id, client_id)`, so each OAuth app gets its own URL:

```
https://backend.composio.dev/api/v3.1/webhook_ingress/{toolkit_slug}/{we_xxx}/trigger_event
```

> **A webhook endpoint ties one OAuth app to exactly one project.** If you currently share an OAuth app across multiple Composio projects, either consolidate to one project or register separate OAuth apps per project before creating an endpoint — the provider dashboard accepts only one callback URL per OAuth app.

The walkthrough below uses Slack as the example. Substitute the toolkit slug for any other toolkit on the same flow — the API surface is identical.

## Step 1: Discover what credentials the endpoint needs

Call the schema endpoint for the toolkit. The `setup_fields` in the response tell you exactly what to collect from the provider's app dashboard.

```bash
curl "https://backend.composio.dev/api/v3.1/webhook_endpoints/schema?toolkit_slug=slack" \
  -H "x-api-key: "
```

Sample response:

```json
{
  "toolkit_slug": "slack",
  "setup_fields": {
    "webhook_signing_secret": {
      "display_name": "Signing Secret",
      "description": "Webhook request signing secret from your Slack app dashboard",
      "is_required": true,
      "is_secret": true
    },
    "app_token": {
      "display_name": "App-Level Token",
      "description": "Slack xapp- token with authorizations:read scope for event authorization",
      "is_required": true,
      "is_secret": true
    }
  }
}
```

## Step 2: Create the endpoint

```bash
curl -X POST "https://backend.composio.dev/api/v3.1/webhook_endpoints" \
  -H "x-api-key: " \
  -H "Content-Type: application/json" \
  -d '{
    "toolkit_slug": "slack",
    "client_id": ""
  }'
```

Sample response:

```json
{
  "id": "we_abc123",
  "toolkit_slug": "slack",
  "client_id": "",
  "webhook_url": "https://backend.composio.dev/api/v3.1/webhook_ingress/slack/we_abc123/trigger_event",
  "data": null,
  "created_at": "2026-04-24T10:00:00.000Z"
}
```

Hold on to two values from the response: `id` (used as `` below) and `webhook_url` (you'll paste this into the provider's app dashboard in step 4). The call is **idempotent per `(toolkit_slug, client_id)` within a project** — calling it again with the same pair returns the existing endpoint without rotating the URL or wiping the secret.

## Step 3: Store the credentials returned by the schema

`PATCH` each field returned by the schema. For Slack, that's the signing secret and (when applicable) the app-level token.

```bash
curl -X PATCH "https://backend.composio.dev/api/v3.1/webhook_endpoints/" \
  -H "x-api-key: " \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "webhook_signing_secret": ""
    }
  }'
```

For Slack, the signing secret lives at **Slack app → Basic Information → App Credentials → Signing Secret**.

> **Store the credentials before you switch the provider's callback URL in step 4.** If the provider posts to the URL without a secret on the endpoint, every request fails with `400`, and the provider may auto-disable the endpoint after a window of consecutive failures (Slack: \~36 hours).

For toolkits with additional fields — Slack's `app_token`, for instance, which is required for direct messages, private channels, and reactions — `PATCH` them in the same way:

```bash
curl -X PATCH "https://backend.composio.dev/api/v3.1/webhook_endpoints/" \
  -H "x-api-key: " \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "app_token": "xapp-..."
    }
  }'
```

Generate the Slack app-level token from **Slack app → Basic Information → App-Level Tokens** with scope `authorizations:read`.

## Step 4: Point the provider's app dashboard at the URL

Paste the `webhook_url` from step 2 into the provider's app dashboard:

* **Slack** → Event Subscriptions → Request URL
* **Notion** → Webhook Endpoints (in the integration's settings)

Composio responds to the provider's verification challenge automatically (Slack `url_verification`, Notion's verification token, and so on) — you don't write any handshake code on your side.

## Step 5: Confirm the endpoint is verified

```bash
curl "https://backend.composio.dev/api/v3.1/webhook_endpoints/" \
  -H "x-api-key: "
```

A populated `verified_at` timestamp on the response means the provider has completed the handshake. You can now retry the trigger creation call from [Using the SDK](#using-the-sdk).

## Updating an endpoint

To rotate the signing secret or update any single field, `PATCH` it (other fields are preserved):

```bash
curl -X PATCH "https://backend.composio.dev/api/v3.1/webhook_endpoints/" \
  -H "x-api-key: " \
  -H "Content-Type: application/json" \
  -d '{ "data": { "webhook_signing_secret": "" } }'
```

To **replace** `data` wholesale (any field you don't include is cleared), `POST` to the same URL:

```bash
curl -X POST "https://backend.composio.dev/api/v3.1/webhook_endpoints/" \
  -H "x-api-key: " \
  -H "Content-Type: application/json" \
  -d '{ "data": { "webhook_signing_secret": "", "app_token": "" } }'
```

The `webhook_url` is immutable for the lifetime of the endpoint — rotating the signing secret on the provider side is a `PATCH` on the existing endpoint, not a new one.

To list every endpoint in the current project:

```bash
curl "https://backend.composio.dev/api/v3.1/webhook_endpoints" \
  -H "x-api-key: "
```

# What to read next

- [Subscribing to events](/docs/setting-up-triggers/subscribing-to-events): Set up the webhook subscription URL Composio delivers events to

- [Verifying webhooks](/docs/webhook-verification): Validate webhook signatures so you know payloads came from Composio

- [Managing triggers](/docs/setting-up-triggers/managing-triggers): List, enable, disable, and delete trigger instances

---

📚 **More documentation:** [View all docs](https://docs.composio.dev/llms.txt) | [Glossary](https://docs.composio.dev/llms.mdx/docs/glossary) | [Cookbooks](https://docs.composio.dev/llms.mdx/cookbooks) | [API Reference](https://docs.composio.dev/llms.mdx/reference)

---

# Composio SDK — Instructions for AI Code Generators

**Purpose:** Enforce only the **current** and **correct** patterns for integrating [Composio](https://composio.dev/) tools into AI agents.
**Scope:** All AI-generated code using Composio must follow these guardrails.

---

## 1. Correct Composio Integration

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. CRITICAL INSTRUCTIONS FOR AI MODELS

### 2.1 — ALWAYS DO THE FOLLOWING

1. **Use `composio.create(user_id)`** to create a session — this is the entry point for all Composio integrations.
2. **Use `session.tools()`** for native tool integration or **`session.mcp.url`** for MCP integration.
3. **Import** from the correct provider package (`composio_<provider>` for Python, `@composio/<provider>` for TypeScript).
4. **Pass `user_id` to `composio.create()`**, not to individual tool calls.

### 2.2 — NEVER DO THE FOLLOWING

1. **Do not** instruct users to manually create auth configs or connected accounts before running.
2. **Do not** import from `composio_openai` / `@composio/openai` when using OpenAI Agents SDK — use `composio_openai_agents` / `@composio/openai-agents` instead.

### 2.3 — DISCOURAGED (do not use unless user explicitly asks)

Do **not** generate `composio.tools.get()`, `composio.tools.execute()`, or `composio.provider.handle_tool_calls()` unless the user explicitly requests direct tool execution. These are a supported but **not recommended** low-level interface.


---

## 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.

