Skip to main content
Triggers allow you to automate agent execution based on schedules or external events. Instead of manually starting conversations, your agents can run automatically at specified times or when specific events occur.

Trigger Types

Kortix supports two primary trigger types:

Schedule Triggers

Run agents on a recurring schedule using cron expressions:
  • Daily reports at 9 AM
  • Weekly summaries every Monday
  • Hourly data syncs
  • Custom intervals

Webhook Triggers

Execute agents when external events occur:
  • New customer signup
  • Payment received
  • Form submission
  • API events from third-party services

Event Triggers

Integrate with external platforms (via Composio):
  • Gmail: New email received
  • Slack: Message in channel
  • GitHub: Pull request opened
  • Google Calendar: Event starting soon

Creating a Schedule Trigger

1

Get available providers

List trigger providers to see available options
2

Configure the trigger

Set up cron expression, timezone, and agent prompt
3

Create the trigger

Send the configuration to create an active trigger

List Available Providers

GET /api/triggers/providers
Response:
[
  {
    "provider_id": "schedule",
    "name": "Schedule",
    "description": "Time-based recurring triggers",
    "trigger_type": "schedule",
    "webhook_enabled": false,
    "config_schema": {
      "type": "object",
      "properties": {
        "cron_expression": {
          "type": "string",
          "description": "Cron expression for schedule"
        },
        "timezone": {
          "type": "string",
          "default": "UTC"
        },
        "agent_prompt": {
          "type": "string"
        }
      }
    }
  }
]

Create a Schedule Trigger

POST /api/triggers/agents/{agent_id}/triggers
Content-Type: application/json

{
  "provider_id": "schedule",
  "name": "Daily Report",
  "description": "Generate daily sales report",
  "config": {
    "cron_expression": "0 9 * * *",
    "timezone": "America/New_York",
    "agent_prompt": "Generate a sales report for yesterday and send it to the team"
  }
}
Response:
{
  "trigger_id": "trigger_abc123",
  "agent_id": "agent_xyz789",
  "trigger_type": "schedule",
  "provider_id": "schedule",
  "name": "Daily Report",
  "description": "Generate daily sales report",
  "is_active": true,
  "webhook_url": null,
  "created_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-15T10:30:00Z",
  "config": {
    "cron_expression": "0 9 * * *",
    "timezone": "America/New_York",
    "agent_prompt": "Generate a sales report for yesterday"
  }
}

Cron Expression Examples

ExpressionDescription
0 9 * * *Every day at 9:00 AM
0 */6 * * *Every 6 hours
0 9 * * 1Every Monday at 9:00 AM
*/15 * * * *Every 15 minutes
0 0 1 * *First day of every month at midnight
All cron expressions are interpreted in the timezone specified in the trigger config. If no timezone is specified, UTC is used.

Viewing Upcoming Runs

Check when your scheduled triggers will run next:
GET /api/triggers/agents/{agent_id}/upcoming-runs?limit=10
Response:
{
  "upcoming_runs": [
    {
      "trigger_id": "trigger_abc123",
      "trigger_name": "Daily Report",
      "trigger_type": "schedule",
      "next_run_time": "2024-01-16T14:00:00Z",
      "next_run_time_local": "2024-01-16T09:00:00-05:00",
      "timezone": "America/New_York",
      "cron_expression": "0 9 * * *",
      "agent_prompt": "Generate a sales report for yesterday",
      "is_active": true,
      "human_readable": "Daily at 9:00 AM EST"
    }
  ],
  "total_count": 1
}

Human-Readable Schedules

The system automatically converts cron expressions to readable formats:
def get_human_readable_schedule(cron_expression: str, timezone: str) -> str:
    # "0 9 * * *" -> "Daily at 9:00 AM"
    # "0 */6 * * *" -> "Every 6 hours"
    # "0 9 * * 1" -> "Every Monday at 9:00 AM"
    pass

Managing Triggers

List Agent Triggers

GET /api/triggers/agents/{agent_id}/triggers

Get Trigger Details

GET /api/triggers/{trigger_id}

Update a Trigger

PUT /api/triggers/{trigger_id}
Content-Type: application/json

{
  "name": "Updated Daily Report",
  "config": {
    "cron_expression": "0 10 * * *",
    "timezone": "America/New_York",
    "agent_prompt": "Generate sales and marketing reports"
  },
  "is_active": true
}
Updating the config will teardown and re-setup the trigger. This ensures the new schedule takes effect immediately.

Pause/Resume a Trigger

Toggle the is_active flag:
PUT /api/triggers/{trigger_id}
Content-Type: application/json

{
  "is_active": false
}

Delete a Trigger

DELETE /api/triggers/{trigger_id}

Trigger Execution History

View past executions and their results:
GET /api/triggers/{trigger_id}/executions?limit=20
Response:
{
  "executions": [
    {
      "execution_id": "run_def456",
      "thread_id": "thread_ghi789",
      "trigger_id": "trigger_abc123",
      "agent_id": "agent_xyz789",
      "status": "completed",
      "started_at": "2024-01-15T14:00:00Z",
      "completed_at": "2024-01-15T14:02:30Z",
      "error_message": null
    }
  ],
  "total_count": 15,
  "next_run_time": "2024-01-16T14:00:00Z",
  "next_run_time_local": "2024-01-16T09:00:00-05:00",
  "timezone": "America/New_York",
  "human_readable_schedule": "Daily at 9:00 AM EST"
}

Execution Statuses

  • pending: Execution queued
  • running: Agent is currently running
  • completed: Execution finished successfully
  • failed: Execution encountered an error
  • cancelled: Execution was cancelled

Webhook Triggers

Receive webhook events to trigger agent execution:

Webhook URL Format

POST /api/triggers/{trigger_id}/webhook
X-Trigger-Secret: <your-secret>

Configure Webhook Secret

Set the TRIGGER_WEBHOOK_SECRET environment variable:
export TRIGGER_WEBHOOK_SECRET="your-secret-key-here"

Send Webhook Event

curl -X POST https://api.kortix.com/api/triggers/trigger_abc123/webhook \
  -H "Content-Type: application/json" \
  -H "X-Trigger-Secret: your-secret-key-here" \
  -d '{
    "event": "customer.signup",
    "customer_email": "[email protected]",
    "plan": "pro"
  }'
Response:
{
  "success": true,
  "message": "Trigger processed and worker execution started",
  "execution": {
    "execution_id": "run_xyz123",
    "thread_id": "thread_new456",
    "status": "running"
  },
  "trigger_result": {
    "should_execute_agent": true,
    "agent_prompt": "Process new customer signup"
  }
}

Webhook Authentication

Webhooks use HMAC-based authentication:
# Verify webhook secret
secret = os.getenv("TRIGGER_WEBHOOK_SECRET")
if not secret:
    raise HTTPException(status_code=500, detail="Webhook secret not configured")

incoming_secret = request.headers.get("x-trigger-secret", "")
if not hmac.compare_digest(incoming_secret, secret):
    logger.warning(f"Invalid webhook secret for trigger {trigger_id}")
    raise HTTPException(status_code=401, detail="Unauthorized")

Trigger Limits by Tier

Different subscription tiers have different trigger limits:
TierScheduled TriggersApp/Webhook Triggers
Free00
Basic25
Pro1020
EnterpriseUnlimitedUnlimited
# Check trigger limits
if config.ENV_MODE != EnvMode.LOCAL:
    provider_trigger_type = await provider_service.get_provider_trigger_type(request.provider_id)
    trigger_type_str = 'scheduled' if provider_trigger_type.value == 'schedule' else 'app'
    
    from core.utils.limits_checker import check_trigger_limit
    limit_check = await check_trigger_limit(user_id, agent_id, trigger_type_str)
    
    if not limit_check['can_create']:
        error_detail = {
            "message": f"Maximum of {limit_check['limit']} {trigger_type_str} triggers allowed",
            "current_count": limit_check['current_count'],
            "limit": limit_check['limit'],
            "tier_name": limit_check['tier_name'],
            "trigger_type": trigger_type_str,
            "error_code": "TRIGGER_LIMIT_EXCEEDED"
        }
        raise HTTPException(status_code=402, detail=error_detail)

Trigger Service Architecture

The trigger system consists of several services:

Trigger Service

Manages trigger lifecycle:
async def create_trigger(
    self,
    agent_id: str,
    provider_id: str,
    name: str,
    config: Dict[str, Any],
    description: Optional[str] = None
) -> Trigger:
    trigger_id = str(uuid.uuid4())
    
    # Validate config with provider
    provider_service = get_provider_service(self._db)
    validated_config = await provider_service.validate_trigger_config(provider_id, config)
    
    trigger_type = await provider_service.get_provider_trigger_type(provider_id)
    
    trigger = Trigger(
        trigger_id=trigger_id,
        agent_id=agent_id,
        provider_id=provider_id,
        trigger_type=trigger_type,
        name=name,
        description=description,
        is_active=True,
        config=validated_config,
        created_at=now,
        updated_at=now
    )
    
    # Setup with provider (e.g., register cron job)
    setup_success = await provider_service.setup_trigger(trigger)
    if not setup_success:
        raise ValueError(f"Failed to setup trigger with provider: {provider_id}")
    
    await self._save_trigger(trigger)
    
    return trigger

Provider Service

Handles provider-specific logic (schedules, webhooks, etc.)

Execution Service

Runs agents when triggers fire

Best Practices

Provide clear, specific agent prompts in your trigger config:
  • Good: “Generate a sales report for yesterday and email it to [email protected]
  • Bad: “Do the daily thing”
Run your agent manually with the planned prompt before creating a scheduled trigger. This ensures the output is what you expect.
Regularly check execution history to ensure triggers are running successfully. Failed executions may need prompt or configuration adjustments.
Always specify the timezone in schedule configs to avoid confusion. “9 AM EST” is clearer than “2 PM UTC”.
Use strong, random secrets for webhook authentication. Rotate secrets periodically for security.

Version Control Integration

Triggers are synced to agent version configs:
async def sync_triggers_to_version_config(agent_id: str):
    # Get current agent version
    agent_result = await client.table('agents')
        .select('current_version_id')
        .eq('agent_id', agent_id)
        .single()
        .execute()
    
    current_version_id = agent_result.data['current_version_id']
    
    # Get all triggers for this agent
    triggers_result = await client.table('agent_triggers')
        .select('*')
        .eq('agent_id', agent_id)
        .execute()
    
    triggers = triggers_result.data or []
    
    # Update version config with triggers
    version_result = await client.table('agent_versions')
        .select('config')
        .eq('version_id', current_version_id)
        .single()
        .execute()
    
    config = version_result.data.get('config', {})
    config['triggers'] = triggers
    
    await client.table('agent_versions')
        .update({'config': config})
        .eq('version_id', current_version_id)
        .execute()
    
    # Invalidate cache
    from core.cache.runtime_cache import invalidate_agent_config_cache
    await invalidate_agent_config_cache(agent_id)

Troubleshooting

Trigger Not Firing

  • Check active status: Ensure is_active is true
  • Verify cron expression: Test the expression with a cron validator
  • Check timezone: Ensure the timezone is correct for your location
  • Review logs: Look for setup or execution errors

Webhook Not Receiving Events

  • Verify secret: Ensure the X-Trigger-Secret header matches your configured secret
  • Check URL: Confirm the webhook URL is correct
  • Test manually: Use curl to send a test event
  • Review webhook logs: Check for authentication or processing errors

Execution Failures

  • Review error message: Check the error_message in execution history
  • Test agent prompt: Run the agent manually with the same prompt
  • Check agent configuration: Ensure the agent has necessary tools and permissions
  • Verify resource limits: Ensure you haven’t exceeded API rate limits

API Reference

Endpoints

MethodEndpointDescription
GET/triggers/providersList available trigger providers
GET/triggers/agents/{id}/triggersList agent triggers
POST/triggers/agents/{id}/triggersCreate trigger
GET/triggers/{id}Get trigger details
PUT/triggers/{id}Update trigger
DELETE/triggers/{id}Delete trigger
GET/triggers/agents/{id}/upcoming-runsGet upcoming scheduled runs
GET/triggers/{id}/executionsGet execution history
POST/triggers/{id}/webhookReceive webhook event
GET/triggers/allList all user triggers (across agents)

Build docs developers (and LLMs) love