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

Prerequisites

  • An auth config for the toolkit you want to monitor
  • A connected account for the user whose events you want to capture
  • A webhook subscription on the project, so events have somewhere to land

You can create triggers using the SDK or the Composio dashboard. Some webhook triggers also need a webhook endpoint configured first — covered in Configuring the webhook endpoint below.

Configuring the webhook endpoint

Some webhook triggers require a webhook endpoint registered with the provider before they can fire. With Composio-managed OAuth, this is already done for you. You only run the steps below when you bring your own OAuth app and the trigger type's requires_webhook_endpoint_setup flag is true.

Each OAuth app you bring gets its own ingress URL within a project:

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

A single OAuth app can serve at most one Composio project: providers accept only one callback URL per OAuth app, and each ingress URL routes to a single project. In return, every project becomes its own webhook tenant — with:

  • Its own ingress rate limit and backpressure budget
  • Project-scoped credentials — the signing secret and app-level token you provide are stored against this project alone, never shared across projects. Repeat verification handshakes are rejected after the endpoint is verified, so the signing secret can't be silently swapped by a forged challenge.
  • Clean fan-out — events reach only that project's trigger instances
  • Per-project metering

Every inbound event is signature-checked at ingress before any trigger fires:

  • HMAC-SHA256 for Slack, Ed25519 or shared-token matching for other providers
  • Timestamp replay protection — when the provider signs a request timestamp, requests outside the allowed skew window are rejected
  • Unsigned or tampered requests are rejected with 400 at ingress, so third parties can't spoof events onto your triggers

Sharing one OAuth app across projects? Consolidate to a single project or register separate OAuth apps per project before continuing.

The walkthrough below uses Slack as the example and walks through the Webhook Endpoints API. For setup notes specific to each toolkit, see its FAQ section — e.g., Slack, Notion.

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.

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

Sample response:

{
  "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

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

Sample response:

{
  "id": "we_abc123",
  "toolkit_slug": "slack",
  "client_id": "<YOUR_OAUTH_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 <ENDPOINT_ID> 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 all the fields the schema returned in a single request. For Slack, that's the signing secret and (when needed) the app-level token together:

curl -X PATCH "https://backend.composio.dev/api/v3.1/webhook_endpoints/<ENDPOINT_ID>" \
  -H "x-api-key: <YOUR_COMPOSIO_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "webhook_signing_secret": "<SIGNING_SECRET>",
      "app_token": "xapp-..."
    }
  }'

For Slack, the credentials come from:

  • Signing secret — Slack app → Basic Information → App Credentials → Signing Secret.
  • App-level token — Slack app → Basic Information → App-Level Tokens, with scope authorizations:read. Required for direct messages, private channels, and reactions; omit it if you only need public-channel events.

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

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)

For providers that issue a verification challenge on save (Slack url_verification, Notion's verification token, and so on), Composio responds automatically — no handshake code on your side. Once the provider accepts the URL, continue to Using the SDK to create your trigger.

Updating an endpoint

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

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

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

curl -X POST "https://backend.composio.dev/api/v3.1/webhook_endpoints/<ENDPOINT_ID>" \
  -H "x-api-key: <YOUR_COMPOSIO_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{ "data": { "webhook_signing_secret": "<NEW_SECRET>", "app_token": "<NEW_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 inspect a single endpoint:

curl "https://backend.composio.dev/api/v3.1/webhook_endpoints/<ENDPOINT_ID>" \
  -H "x-api-key: <YOUR_COMPOSIO_API_KEY>"

To list every endpoint in the current project:

curl "https://backend.composio.dev/api/v3.1/webhook_endpoints" \
  -H "x-api-key: <YOUR_COMPOSIO_API_KEY>"

Using the SDK

Before creating a trigger, inspect the trigger type to see what configuration it requires. Then create the trigger with the required config.

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.

from composio import Composio

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

# Check what configuration is required
trigger_type = composio.triggers.get_type("GITHUB_COMMIT_EVENT")
print(trigger_type.config)
# Returns: {"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}")
import { Composio } from '@composio/core';

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

// Check what configuration is required
const triggerType = await composio.triggers.getType("GITHUB_COMMIT_EVENT");
console.log(triggerType.config);
// Returns: {"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 for details.

Using the dashboard

  1. Navigate to Auth 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
  5. Click Create Trigger
Creating a GitHub Star Added trigger from the dashboard