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
Get available providers
List trigger providers to see available options
Configure the trigger
Set up cron expression, timezone, and agent prompt
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
Expression Description 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:
POST /api/triggers/{trigger_id}/webhook
X-Trigger-Secret: <your-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:
Tier Scheduled Triggers App/Webhook Triggers Free 0 0 Basic 2 5 Pro 10 20 Enterprise Unlimited Unlimited
# 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:
trigger_service.py (lines 55-94)
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.
Use appropriate timezones
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
Method Endpoint Description 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)