Skip to main content
Glama

agents_trigger_create

Create a new trigger for an AI agent.

Triggers determine when the agent activates.

Trigger types:

  • incoming_message: Activates on new incoming messages

  • schedule: Activates on a schedule

  • webhook: Activates on webhook events

  • event: Activates on system events

Tool call template

Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "agent_id": "<agent_id>",
      "trigger_type": "<trigger_type>"
    },
    "name": "agents_trigger_create"
  }
}

Input Schema

NameRequiredDescriptionDefault
enabledNoWhether the trigger is enabled. OMIT to use the default (true).
agent_idYesID of the agent to create a trigger for
priorityNoTrigger priority — lower numbers run first (default: 100)
send_modeNoSend mode override for this trigger. OMIT to inherit from the agent.
conditionsNoTrigger conditions (JSON). Supported fields for incoming_message: - keywords: ["pricing","demo"] — message must contain keyword(s) (free, no LLM cost) - keyword_match: "any" (default, OR) or "all" (AND) - channel_types: ["telegram","whatsapp","livechat_voice","twilio_voice","telegram_voice","voice",...] — filter by channel. For voice, use EITHER the three per-channel keys (scoped) OR "voice" alone (wildcard matching all three) — mixing them is redundant. Per-channel keys: "livechat_voice" (web widget), "twilio_voice" (PSTN inbound), "telegram_voice" (Telegram p2p calls) - context_types: ["dm","group","channel","livechat"] — filter by chat type - group_mode: "mentions_only" or "questions" — for group chats - channel_account_ids: ["123"] — restrict to specific accounts. Only accounts THIS workspace answers: a trigger never fires for another workspace's channel, so create it on an agent there - folder_ids: [5,10] — restrict to threads in folders - ai_tag_ids: [1,2] — restrict to threads with AI tags - ai_filter_ids: [1,2] — semantic intent filters (message matched via embedding similarity, works in noisy groups) - ai_filter_mode: "any" (default, OR) or "all" (AND) — how multiple AI filters combine - ai_filters: [{id: 1}, {name: "...", description: "..."}] — shorthand: reference existing by id or create inline (calls Voyage embedding API). If a filter with the same name already exists, it is reused by id. Prefer referencing existing filters by id when available. Use ai_filters.create + ai_filters.test for fine-tuning before assigning. - template: the reply a rule_based agent sends, with no model run. Either a string ("Hi {from_name}, we open at 9") or a card object {text, attachments, buttons} — text is required, attachments is a list of file ids (files.upload), buttons is a list of {label, value|url|web_app} carrying exactly ONE target each: value = a reply the bot receives back, url = opens a link (add track: true to count each tap per conversation, readable as analytics_link_clicks_v in analytics.query; add tag: <ai_tag id> beside it to put that tag on the CONVERSATION at the tap, so for people, not groups, where any member's tap tags the whole group; add pass: "<name>" to open the url with <name>=<the chat's id on its channel, a Telegram user id for a bot chat>, so the site can name the person back in its own events), web_app = opens a Mini App over https. The string, and a card's text, substitute {from_name}, {message_text}, {channel_type}, {thread_id}, {raw_data}; button labels and targets are sent verbatim. A card with buttons needs send_mode auto — a draft row cannot carry a keyboard. Buttons need a channel that renders them (telegram_bot). A card needs a chat to land in, so it refuses on webhook, schedule and other thread-less triggers where a string still drafts. - handoff: {agent_id, task} — only on a rule_based agent's trigger (refused elsewhere). After the rule's deterministic part has actually happened — the template's message delivered, or the agent's script completed — the named agent runs on the same thread, in its own send mode, exactly as its own trigger would run it: the customer's message reaches it as the incoming message, and `task` reaches it as the handoff brief from this rule. The rule stays instant; the thinking — a reminder, a referral code, a personal follow-up — happens behind it. Script plus handoff is how a workspace does something deterministic the platform has no verb for (file the thread, stamp a tag, push a row) and still lets an agent answer, with the deterministic part living in that agent's own script rather than in platform code. `task` substitutes {channel_type} and {thread_id} only; {message_text}, {from_name} and {raw_data} are refused (the sender would be writing the instruction — the message and its sender already reach the target as the incoming message). The target must be an active agentic or claude_channels agent in this workspace (the engines that read the brief). The target's run is a normal trigger run: it holds the thread lock, its own safety gates apply, and its final answer is delivered like any DM reply — a follow-up that should stay silent must call agent.silence. Nothing runs when the message was drafted, refused or failed, when the script failed, when a safety gate has the rule in draft mode, or when the event carries no thread for the target to answer on. A target paused or deleted after the trigger was written does not stop the rule's message: it goes out, and the reason the follow-up did not start is recorded in the rule's activity row (error field) and in the target's activity as an execution_error. - contact_states: ["active"] — filter by contact state - cooldown_seconds: 30 — min gap between runs per thread - max_runs_per_thread_per_hour: 5 — rate limit - once_per_thread: true — after this trigger completes on a conversation it steps aside there and the next matching trigger answers later messages. For a rule that files / tags / hands off once, not for the agent that holds the conversation. - answer_delay_s: 15 — voice pickup only (incoming_call, or incoming_message scoped to a voice channel). Ring the human's own devices this long before the agent answers; if they pick up, the agent stands down. 0/absent = answer immediately. Honoured on WhatsApp and Telegram 1:1 — other voice channels have nothing ringing to wait for and still answer at once. - auto_join: true|false — voice pickup, GROUP calls only. Whether the bot enters a matching group call on its own (true) or the call becomes a pending invite an operator accepts (false). OMIT THE KEY to defer to the channel setting (channel_account.state.voice_auto_join_policy, default 'approval'); resolution is trigger-when-present, then channel, then approval, so an explicit false beats a channel set to auto. Absent and false are different answers. - chat_ids: ["-5172634473"] — voice pickup, Telegram GROUP calls only. Scopes the trigger to specific groups; omit for every group call. Any id spelling works (5172634473, -5172634473, -1005172634473 are the same group). A call whose chat is unknown never satisfies it, so do NOT set this together with 1:1 / Twilio / Telnyx / WhatsApp / Android / LiveChat voice channels or the "voice" wildcard: those calls have no chat and the trigger would stop answering them. Supported fields for job_completed (proactive callback when a delegated job finishes): - source_agent_id: <int> — fire only when this agent's job completed - source_agent_slug: <str> — alternate to source_agent_id - job_type: "agentic_session" — match a specific job type (default: any) - outcome: ["completed"] | ["escalated"] | ["completed","escalated"] — default ["completed"] - min_duration_seconds: <int> — skip very-short jobs (noise filter) - thread_filter: {thread_ids: [<int>...]} — restrict to specific threads Supported fields for calendar_event (fires N minutes before a Google Calendar event starts): - window_minutes_before: <int 1-1440> — REQUIRED, fire when an event starts within this window - channel_account_ids: [<int>...] — restrict to specific calendar accounts (default: all) - keywords: ["standup"] — word-boundary match on event title - prepare_meet_join: true — pre-invite the pool bots that can join signed in (none when none can) to the event, for an unattended Meet join Supported fields for task_assigned (a task on the workspace board was assigned to this agent): - target_session: <session_id> — for a Claude Code agent, the session the task goes to (default: the agent's own session) Supported fields for job_continuation (this agent's own call or video finished): - job_types: ["voice_call", "video_generation"] — which kinds re-invoke it (default: all) incoming_message action fields: - action: "reply_text" (default, normal agent run) or "join_voice" (deterministically join the voice channel resolved from the message — requires send_mode=auto) - message_source: "real" (default) | "transcript" | "both" — real messages vs turns during a live call. Transcripts are turns that address the agent. calendar_event run-mode field (incoming_message uses `action` instead): - run_mode: "text" (default) or "voice" (join the meeting — requires send_mode=auto) - voice: {speak_first: <bool — greet immediately vs stay silent until addressed>, vision_mode: "off"|"on_demand"|"continuous_0_3fps"} — pairs with action=join_voice (incoming_message) or run_mode=voice (calendar_event)
thread_idsNoRestrict this trigger to specific threads (chats) by their numeric thread IDs. When set, the trigger only fires for messages in these threads. Only for incoming_message and job_completed triggers. Maps to conditions.thread_filter.thread_ids.
in_workspaceNoRun this one call in this workspace id instead of the session's. Nothing is stored; other sessions are not affected.
trigger_typeYesType of trigger: 'incoming_message', 'incoming_call', 'schedule', 'webhook', 'event', 'blockchain_event', 'job_completed', 'calendar_event', 'lead_captured', 'task_assigned' (a task on the workspace board was assigned to this agent; without one, assigning a task does not start the agent), or 'job_continuation' (this agent's OWN call from calls.make or video from videos.generate finished; without one the agent is not re-invoked to carry on)
target_sessionNoWhich Claude Code desktop session this trigger's runs go to, by the name / client id / session id that workspace.desktops lists. Only meaningful on a claude_channels agent: that engine hands the run to a connected desktop, and without an address it can only be delivered when EXACTLY ONE is connected (a second open window turns every run into channel_target_missing). A task assigned to the agent still wins — its own target_session is per-request and more specific than the trigger's default. Stored as conditions.target_session.
blocked_sender_idsNoNever react to these senders/callers, by their channel-side id — a phone number, a WhatsApp JID, a Telegram user id, an email address. Explicit deny: it beats every allow-list, and it applies to ALL trigger types, including incoming_call, so it is how you stop an agent answering one nuisance caller without silencing it for everybody. Phone-shaped ids are matched by their digits, so '+998901234567', '998901234567' and '998901234567@s.whatsapp.net' are the same person. Maps to conditions.sender_filter.excluded_external_ids.
excluded_thread_idsNoExclude specific threads (chats) by their numeric thread IDs — the opposite of thread_ids. When set, the trigger NEVER fires for messages in these threads, even if thread_ids would otherwise allow them (explicit deny wins). Only for incoming_message and job_completed triggers. Maps to conditions.thread_filter.excluded_thread_ids.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changed
    • changedInput schema / properties / conditions / description
      Previous value: -"Trigger conditions (JSON). Supported fields for incoming_message:\n- keywords: [\"pricing\",\"demo\"] — message must contain keyword(s) (free, no LLM cost)\n- keyword_match: \"any\" (default, OR) or \"all\" (AND)\n- channel_types: [\"telegram\",\"whatsapp\",\"livechat_voice\",\"twilio_voice\",\"telegram_voice\",\"voice\",...] — filter by channel. For voice, use EITHER the three per-channel keys (scoped) OR \"voice\" alone (wildcard matching all three) — mixing them is redundant. Per-channel keys: \"livechat_voice\" (web widget), \"twilio_voice\" (PSTN inbound), \"telegram_voice\" (Telegram p2p calls)\n- context_types: [\"dm\",\"group\",\"channel\",\"livechat\"] — filter by chat type\n- group_mode: \"mentions_only\" or \"questions\" — for group chats\n- channel_account_ids: [\"123\"] — restrict to specific accounts. Only accounts THIS workspace answers: a trigger never fires for another workspace's channel, so create it on an agent there\n- folder_ids: [5,10] — restrict to threads in folders\n- ai_tag_ids: [1,2] — restrict to threads with AI tags\n- ai_filter_ids: [1,2] — semantic intent filters (message matched via embedding similarity, works in noisy groups)\n- ai_filter_mode: \"any\" (default, OR) or \"all\" (AND) — how multiple AI filters combine\n- ai_filters: [{id: 1}, {name: \"...\", description: \"...\"}] — shorthand: reference existing by id or create inline (calls Voyage embedding API). If a filter with the same name already exists, it is reused by id. Prefer referencing existing filters by id when available. Use ai_filters.create + ai_filters.test for fine-tuning before assigning.\n- template: the reply a rule_based agent sends, with no model run. Either a string (\"Hi {from_name}, we open at 9\") or a card object {text, attachments, buttons} — text is required, attachments is a list of file ids (files.upload), buttons is a list of {label, value|url|web_app} carrying exactly ONE target each: value = a reply the bot receives back, url = opens a link (add track: true to count each tap per conversation, readable as analytics_link_clicks_v in analytics.query; add tag: <ai_tag id> beside it to put that tag on the CONVERSATION at the tap, so for people, not groups, where any member's tap tags the whole group; add pass: \"<name>\" to open the url with <name>=<the chat's id on its channel, a Telegram user id for a bot chat>, so the site can name the person back in its own events), web_app = opens a Mini App over https. The string, and a card's text, substitute {from_name}, {message_text}, {channel_type}, {thread_id}, {raw_data}; button labels and targets are sent verbatim. A card with buttons needs send_mode auto — a draft row cannot carry a keyboard. Buttons need a channel that renders them (telegram_bot). A card needs a chat to land in, so it refuses on webhook, schedule and other thread-less triggers where a string still drafts.\n- handoff: {agent_id, task} — only on a rule_based agent's trigger (refused elsewhere). After the rule's deterministic part has actually happened — the template's message delivered, or the agent's script completed — the named agent runs on the same thread, in its own send mode, exactly as its own trigger would run it: the customer's message reaches it as the incoming message, and `task` reaches it as the handoff brief from this rule. The rule stays instant; the thinking — a reminder, a referral code, a personal follow-up — happens behind it. Script plus handoff is how a workspace does something deterministic the platform has no verb for (file the thread, stamp a tag, push a row) and still lets an agent answer, with the deterministic part living in that agent's own script rather than in platform code. `task` substitutes {channel_type} and {thread_id} only; {message_text}, {from_name} and {raw_data} are refused (the sender would be writing the instruction — the message and its sender already reach the target as the incoming message). The target must be an active agentic or claude_channels agent in this workspace (the engines that read the brief). The target's run is a normal trigger run: it holds the thread lock, its own safety gates apply, and its final answer is delivered like any DM reply — a follow-up that should stay silent must call agent.silence. Nothing runs when the message was drafted, refused or failed, when the script failed, when a safety gate has the rule in draft mode, or when the event carries no thread for the target to answer on. A target paused or deleted after the trigger was written does not stop the rule's message: it goes out, and the reason the follow-up did not start is recorded in the rule's activity row (error field) and in the target's activity as an execution_error.\n- contact_states: [\"active\"] — filter by contact state\n- cooldown_seconds: 30 — min gap between runs per thread\n- max_runs_per_thread_per_hour: 5 — rate limit\n- once_per_thread: true — after this trigger completes on a conversation it steps aside there and the next matching trigger answers later messages. For a rule that files / tags / hands off once, not for the agent that holds the conversation.\n- answer_delay_s: 15 — voice pickup only (incoming_call, or incoming_message scoped to a voice channel). Ring the human's own devices this long before the agent answers; if they pick up, the agent stands down. 0/absent = answer immediately. Honoured on WhatsApp and Telegram 1:1 — other voice channels have nothing ringing to wait for and still answer at once.\n- auto_join: true|false — voice pickup, GROUP calls only. Whether the bot enters a matching group call on its own (true) or the call becomes a pending invite an operator accepts (false). OMIT THE KEY to defer to the channel setting (channel_account.state.voice_auto_join_policy, default 'approval'); resolution is trigger-when-present, then channel, then approval, so an explicit false beats a channel set to auto. Absent and false are different answers.\n- chat_ids: [\"-5172634473\"] — voice pickup, Telegram GROUP calls only. Scopes the trigger to specific groups; omit for every group call. Any id spelling works (5172634473, -5172634473, -1005172634473 are the same group). A call whose chat is unknown never satisfies it, so do NOT set this together with 1:1 / Twilio / Telnyx / WhatsApp / Android / LiveChat voice channels or the \"voice\" wildcard: those calls have no chat and the trigger would stop answering them.\n\nSupported fields for job_completed (proactive callback when a delegated job finishes):\n- source_agent_id: <int> — fire only when this agent's job completed\n- source_agent_slug: <str> — alternate to source_agent_id\n- job_type: \"agentic_session\" — match a specific job type (default: any)\n- outcome: [\"completed\"] | [\"escalated\"] | [\"completed\",\"escalated\"] — default [\"completed\"]\n- min_duration_seconds: <int> — skip very-short jobs (noise filter)\n- thread_filter: {thread_ids: [<int>...]} — restrict to specific threads\n\nSupported fields for calendar_event (fires N minutes before a Google Calendar event starts):\n- window_minutes_before: <int 1-1440> — REQUIRED, fire when an event starts within this window\n- channel_account_ids: [<int>...] — restrict to specific calendar accounts (default: all)\n- keywords: [\"standup\"] — word-boundary match on event title\n- prepare_meet_join: true — pre-invite pool bots to the event (enables unattended Meet join)\n\nSupported fields for task_assigned (a task on the workspace board was assigned to this agent):\n- target_session: <session_id> — for a Claude Code agent, the session the task goes to (default: the agent's own session)\n\nSupported fields for job_continuation (this agent's own call or video finished):\n- job_types: [\"voice_call\", \"video_generation\"] — which kinds re-invoke it (default: all)\n\nincoming_message action fields:\n- action: \"reply_text\" (default, normal agent run) or \"join_voice\" (deterministically join the voice channel resolved from the message — requires send_mode=auto)\n- message_source: \"real\" (default) | \"transcript\" | \"both\" — real messages vs turns during a live call. Transcripts are turns that address the agent.\n\ncalendar_event run-mode field (incoming_message uses `action` instead):\n- run_mode: \"text\" (default) or \"voice\" (join the meeting — requires send_mode=auto)\n- voice: {speak_first: <bool — greet immediately vs stay silent until addressed>, vision_mode: \"off\"|\"on_demand\"|\"continuous_0_3fps\"} — pairs with action=join_voice (incoming_message) or run_mode=voice (calendar_event)"New value: +"Trigger conditions (JSON). Supported fields for incoming_message:\n- keywords: [\"pricing\",\"demo\"] — message must contain keyword(s) (free, no LLM cost)\n- keyword_match: \"any\" (default, OR) or \"all\" (AND)\n- channel_types: [\"telegram\",\"whatsapp\",\"livechat_voice\",\"twilio_voice\",\"telegram_voice\",\"voice\",...] — filter by channel. For voice, use EITHER the three per-channel keys (scoped) OR \"voice\" alone (wildcard matching all three) — mixing them is redundant. Per-channel keys: \"livechat_voice\" (web widget), \"twilio_voice\" (PSTN inbound), \"telegram_voice\" (Telegram p2p calls)\n- context_types: [\"dm\",\"group\",\"channel\",\"livechat\"] — filter by chat type\n- group_mode: \"mentions_only\" or \"questions\" — for group chats\n- channel_account_ids: [\"123\"] — restrict to specific accounts. Only accounts THIS workspace answers: a trigger never fires for another workspace's channel, so create it on an agent there\n- folder_ids: [5,10] — restrict to threads in folders\n- ai_tag_ids: [1,2] — restrict to threads with AI tags\n- ai_filter_ids: [1,2] — semantic intent filters (message matched via embedding similarity, works in noisy groups)\n- ai_filter_mode: \"any\" (default, OR) or \"all\" (AND) — how multiple AI filters combine\n- ai_filters: [{id: 1}, {name: \"...\", description: \"...\"}] — shorthand: reference existing by id or create inline (calls Voyage embedding API). If a filter with the same name already exists, it is reused by id. Prefer referencing existing filters by id when available. Use ai_filters.create + ai_filters.test for fine-tuning before assigning.\n- template: the reply a rule_based agent sends, with no model run. Either a string (\"Hi {from_name}, we open at 9\") or a card object {text, attachments, buttons} — text is required, attachments is a list of file ids (files.upload), buttons is a list of {label, value|url|web_app} carrying exactly ONE target each: value = a reply the bot receives back, url = opens a link (add track: true to count each tap per conversation, readable as analytics_link_clicks_v in analytics.query; add tag: <ai_tag id> beside it to put that tag on the CONVERSATION at the tap, so for people, not groups, where any member's tap tags the whole group; add pass: \"<name>\" to open the url with <name>=<the chat's id on its channel, a Telegram user id for a bot chat>, so the site can name the person back in its own events), web_app = opens a Mini App over https. The string, and a card's text, substitute {from_name}, {message_text}, {channel_type}, {thread_id}, {raw_data}; button labels and targets are sent verbatim. A card with buttons needs send_mode auto — a draft row cannot carry a keyboard. Buttons need a channel that renders them (telegram_bot). A card needs a chat to land in, so it refuses on webhook, schedule and other thread-less triggers where a string still drafts.\n- handoff: {agent_id, task} — only on a rule_based agent's trigger (refused elsewhere). After the rule's deterministic part has actually happened — the template's message delivered, or the agent's script completed — the named agent runs on the same thread, in its own send mode, exactly as its own trigger would run it: the customer's message reaches it as the incoming message, and `task` reaches it as the handoff brief from this rule. The rule stays instant; the thinking — a reminder, a referral code, a personal follow-up — happens behind it. Script plus handoff is how a workspace does something deterministic the platform has no verb for (file the thread, stamp a tag, push a row) and still lets an agent answer, with the deterministic part living in that agent's own script rather than in platform code. `task` substitutes {channel_type} and {thread_id} only; {message_text}, {from_name} and {raw_data} are refused (the sender would be writing the instruction — the message and its sender already reach the target as the incoming message). The target must be an active agentic or claude_channels agent in this workspace (the engines that read the brief). The target's run is a normal trigger run: it holds the thread lock, its own safety gates apply, and its final answer is delivered like any DM reply — a follow-up that should stay silent must call agent.silence. Nothing runs when the message was drafted, refused or failed, when the script failed, when a safety gate has the rule in draft mode, or when the event carries no thread for the target to answer on. A target paused or deleted after the trigger was written does not stop the rule's message: it goes out, and the reason the follow-up did not start is recorded in the rule's activity row (error field) and in the target's activity as an execution_error.\n- contact_states: [\"active\"] — filter by contact state\n- cooldown_seconds: 30 — min gap between runs per thread\n- max_runs_per_thread_per_hour: 5 — rate limit\n- once_per_thread: true — after this trigger completes on a conversation it steps aside there and the next matching trigger answers later messages. For a rule that files / tags / hands off once, not for the agent that holds the conversation.\n- answer_delay_s: 15 — voice pickup only (incoming_call, or incoming_message scoped to a voice channel). Ring the human's own devices this long before the agent answers; if they pick up, the agent stands down. 0/absent = answer immediately. Honoured on WhatsApp and Telegram 1:1 — other voice channels have nothing ringing to wait for and still answer at once.\n- auto_join: true|false — voice pickup, GROUP calls only. Whether the bot enters a matching group call on its own (true) or the call becomes a pending invite an operator accepts (false). OMIT THE KEY to defer to the channel setting (channel_account.state.voice_auto_join_policy, default 'approval'); resolution is trigger-when-present, then channel, then approval, so an explicit false beats a channel set to auto. Absent and false are different answers.\n- chat_ids: [\"-5172634473\"] — voice pickup, Telegram GROUP calls only. Scopes the trigger to specific groups; omit for every group call. Any id spelling works (5172634473, -5172634473, -1005172634473 are the same group). A call whose chat is unknown never satisfies it, so do NOT set this together with 1:1 / Twilio / Telnyx / WhatsApp / Android / LiveChat voice channels or the \"voice\" wildcard: those calls have no chat and the trigger would stop answering them.\n\nSupported fields for job_completed (proactive callback when a delegated job finishes):\n- source_agent_id: <int> — fire only when this agent's job completed\n- source_agent_slug: <str> — alternate to source_agent_id\n- job_type: \"agentic_session\" — match a specific job type (default: any)\n- outcome: [\"completed\"] | [\"escalated\"] | [\"completed\",\"escalated\"] — default [\"completed\"]\n- min_duration_seconds: <int> — skip very-short jobs (noise filter)\n- thread_filter: {thread_ids: [<int>...]} — restrict to specific threads\n\nSupported fields for calendar_event (fires N minutes before a Google Calendar event starts):\n- window_minutes_before: <int 1-1440> — REQUIRED, fire when an event starts within this window\n- channel_account_ids: [<int>...] — restrict to specific calendar accounts (default: all)\n- keywords: [\"standup\"] — word-boundary match on event title\n- prepare_meet_join: true — pre-invite the pool bots that can join signed in (none when none can) to the event, for an unattended Meet join\n\nSupported fields for task_assigned (a task on the workspace board was assigned to this agent):\n- target_session: <session_id> — for a Claude Code agent, the session the task goes to (default: the agent's own session)\n\nSupported fields for job_continuation (this agent's own call or video finished):\n- job_types: [\"voice_call\", \"video_generation\"] — which kinds re-invoke it (default: all)\n\nincoming_message action fields:\n- action: \"reply_text\" (default, normal agent run) or \"join_voice\" (deterministically join the voice channel resolved from the message — requires send_mode=auto)\n- message_source: \"real\" (default) | \"transcript\" | \"both\" — real messages vs turns during a live call. Transcripts are turns that address the agent.\n\ncalendar_event run-mode field (incoming_message uses `action` instead):\n- run_mode: \"text\" (default) or \"voice\" (join the meeting — requires send_mode=auto)\n- voice: {speak_first: <bool — greet immediately vs stay silent until addressed>, vision_mode: \"off\"|\"on_demand\"|\"continuous_0_3fps\"} — pairs with action=join_voice (incoming_message) or run_mode=voice (calendar_event)"
  2. Changed3 schema fields changed
    • changedInput schema / properties / conditions / description
      Previous value: -"Trigger conditions (JSON). Supported fields for incoming_message:\n- keywords: [\"pricing\",\"demo\"] — message must contain keyword(s) (free, no LLM cost)\n- keyword_match: \"any\" (default, OR) or \"all\" (AND)\n- channel_types: [\"telegram\",\"whatsapp\",\"livechat_voice\",\"twilio_voice\",\"telegram_voice\",\"voice\",...] — filter by channel. For voice, use EITHER the three per-channel keys (scoped) OR \"voice\" alone (wildcard matching all three) — mixing them is redundant. Per-channel keys: \"livechat_voice\" (web widget), \"twilio_voice\" (PSTN inbound), \"telegram_voice\" (Telegram p2p calls)\n- context_types: [\"dm\",\"group\",\"channel\",\"livechat\"] — filter by chat type\n- group_mode: \"mentions_only\" or \"questions\" — for group chats\n- channel_account_ids: [\"123\"] — restrict to specific accounts. Only accounts THIS workspace answers: a trigger never fires for another workspace's channel, so create it on an agent there\n- folder_ids: [5,10] — restrict to threads in folders\n- ai_tag_ids: [1,2] — restrict to threads with AI tags\n- ai_filter_ids: [1,2] — semantic intent filters (message matched via embedding similarity, works in noisy groups)\n- ai_filter_mode: \"any\" (default, OR) or \"all\" (AND) — how multiple AI filters combine\n- ai_filters: [{id: 1}, {name: \"...\", description: \"...\"}] — shorthand: reference existing by id or create inline (calls Voyage embedding API). If a filter with the same name already exists, it is reused by id. Prefer referencing existing filters by id when available. Use ai_filters.create + ai_filters.test for fine-tuning before assigning.\n- template: the reply a rule_based agent sends, with no model run. Either a string (\"Hi {from_name}, we open at 9\") or a card object {text, attachments, buttons} — text is required, attachments is a list of file ids (files.upload), buttons is a list of {label, value|url|web_app} carrying exactly ONE target each: value = a reply the bot receives back, url = opens a link (add track: true to count each tap per conversation, readable as analytics_link_clicks_v in analytics.query; add tag: <ai_tag id> beside it to put that tag on the CONVERSATION at the tap, so for people, not groups, where any member's tap tags the whole group; add pass: \"<name>\" to open the url with <name>=<the chat's id on its channel, a Telegram user id for a bot chat>, so the site can name the person back in its own events), web_app = opens a Mini App over https. The string, and a card's text, substitute {from_name}, {message_text}, {channel_type}, {thread_id}, {raw_data}; button labels and targets are sent verbatim. A card with buttons needs send_mode auto — a draft row cannot carry a keyboard. Buttons need a channel that renders them (telegram_bot). A card needs a chat to land in, so it refuses on webhook, schedule and other thread-less triggers where a string still drafts.\n- handoff: {agent_id, task} — only on a rule_based agent's trigger (refused elsewhere). After the rule's deterministic part has actually happened — the template's message delivered, or the agent's script completed — the named agent runs on the same thread, in its own send mode, exactly as its own trigger would run it: the customer's message reaches it as the incoming message, and `task` reaches it as the handoff brief from this rule. The rule stays instant; the thinking — a reminder, a referral code, a personal follow-up — happens behind it. Script plus handoff is how a workspace does something deterministic the platform has no verb for (file the thread, stamp a tag, push a row) and still lets an agent answer, with the deterministic part living in that agent's own script rather than in platform code. `task` substitutes {channel_type} and {thread_id} only; {message_text}, {from_name} and {raw_data} are refused (the sender would be writing the instruction — the message and its sender already reach the target as the incoming message). The target must be an active agentic or claude_channels agent in this workspace (the engines that read the brief). The target's run is a normal trigger run: it holds the thread lock, its own safety gates apply, and its final answer is delivered like any DM reply — a follow-up that should stay silent must call agent.silence. Nothing runs when the message was drafted, refused or failed, when the script failed, when a safety gate has the rule in draft mode, or when the event carries no thread for the target to answer on. A target paused or deleted after the trigger was written does not stop the rule's message: it goes out, and the reason the follow-up did not start is recorded in the rule's activity row (error field) and in the target's activity as an execution_error.\n- contact_states: [\"active\"] — filter by contact state\n- cooldown_seconds: 30 — min gap between runs per thread\n- max_runs_per_thread_per_hour: 5 — rate limit\n- once_per_thread: true — after this trigger completes on a conversation it steps aside there and the next matching trigger answers later messages. For a rule that files / tags / hands off once, not for the agent that holds the conversation.\n- answer_delay_s: 15 — voice pickup only (incoming_call, or incoming_message scoped to a voice channel). Ring the human's own devices this long before the agent answers; if they pick up, the agent stands down. 0/absent = answer immediately. Honoured on WhatsApp and Telegram 1:1 — other voice channels have nothing ringing to wait for and still answer at once.\n- auto_join: true|false — voice pickup, GROUP calls only. Whether the bot enters a matching group call on its own (true) or the call becomes a pending invite an operator accepts (false). OMIT THE KEY to defer to the channel setting (channel_account.state.voice_auto_join_policy, default 'approval'); resolution is trigger-when-present, then channel, then approval, so an explicit false beats a channel set to auto. Absent and false are different answers.\n- chat_ids: [\"-5172634473\"] — voice pickup, Telegram GROUP calls only. Scopes the trigger to specific groups; omit for every group call. Any id spelling works (5172634473, -5172634473, -1005172634473 are the same group). A call whose chat is unknown never satisfies it, so do NOT set this together with 1:1 / Twilio / Telnyx / WhatsApp / Android / LiveChat voice channels or the \"voice\" wildcard: those calls have no chat and the trigger would stop answering them.\n\nSupported fields for job_completed (proactive callback when a delegated job finishes):\n- source_agent_id: <int> — fire only when this agent's job completed\n- source_agent_slug: <str> — alternate to source_agent_id\n- job_type: \"agentic_session\" — match a specific job type (default: any)\n- outcome: [\"completed\"] | [\"escalated\"] | [\"completed\",\"escalated\"] — default [\"completed\"]\n- min_duration_seconds: <int> — skip very-short jobs (noise filter)\n- thread_filter: {thread_ids: [<int>...]} — restrict to specific threads\n\nSupported fields for calendar_event (fires N minutes before a Google Calendar event starts):\n- window_minutes_before: <int 1-1440> — REQUIRED, fire when an event starts within this window\n- channel_account_ids: [<int>...] — restrict to specific calendar accounts (default: all)\n- keywords: [\"standup\"] — word-boundary match on event title\n- prepare_meet_join: true — pre-invite pool bots to the event (enables unattended Meet join)\n\nSupported fields for task_assigned (a task on the workspace board was assigned to this agent):\n- target_session: <session_id> — for a Claude Code agent, the session the task goes to (default: the agent's own session)\n\nincoming_message action fields:\n- action: \"reply_text\" (default, normal agent run) or \"join_voice\" (deterministically join the voice channel resolved from the message — requires send_mode=auto)\n- message_source: \"real\" (default) | \"transcript\" | \"both\" — real messages vs turns during a live call. Transcripts are turns that address the agent.\n\ncalendar_event run-mode field (incoming_message uses `action` instead):\n- run_mode: \"text\" (default) or \"voice\" (join the meeting — requires send_mode=auto)\n- voice: {speak_first: <bool — greet immediately vs stay silent until addressed>, vision_mode: \"off\"|\"on_demand\"|\"continuous_0_3fps\"} — pairs with action=join_voice (incoming_message) or run_mode=voice (calendar_event)"New value: +"Trigger conditions (JSON). Supported fields for incoming_message:\n- keywords: [\"pricing\",\"demo\"] — message must contain keyword(s) (free, no LLM cost)\n- keyword_match: \"any\" (default, OR) or \"all\" (AND)\n- channel_types: [\"telegram\",\"whatsapp\",\"livechat_voice\",\"twilio_voice\",\"telegram_voice\",\"voice\",...] — filter by channel. For voice, use EITHER the three per-channel keys (scoped) OR \"voice\" alone (wildcard matching all three) — mixing them is redundant. Per-channel keys: \"livechat_voice\" (web widget), \"twilio_voice\" (PSTN inbound), \"telegram_voice\" (Telegram p2p calls)\n- context_types: [\"dm\",\"group\",\"channel\",\"livechat\"] — filter by chat type\n- group_mode: \"mentions_only\" or \"questions\" — for group chats\n- channel_account_ids: [\"123\"] — restrict to specific accounts. Only accounts THIS workspace answers: a trigger never fires for another workspace's channel, so create it on an agent there\n- folder_ids: [5,10] — restrict to threads in folders\n- ai_tag_ids: [1,2] — restrict to threads with AI tags\n- ai_filter_ids: [1,2] — semantic intent filters (message matched via embedding similarity, works in noisy groups)\n- ai_filter_mode: \"any\" (default, OR) or \"all\" (AND) — how multiple AI filters combine\n- ai_filters: [{id: 1}, {name: \"...\", description: \"...\"}] — shorthand: reference existing by id or create inline (calls Voyage embedding API). If a filter with the same name already exists, it is reused by id. Prefer referencing existing filters by id when available. Use ai_filters.create + ai_filters.test for fine-tuning before assigning.\n- template: the reply a rule_based agent sends, with no model run. Either a string (\"Hi {from_name}, we open at 9\") or a card object {text, attachments, buttons} — text is required, attachments is a list of file ids (files.upload), buttons is a list of {label, value|url|web_app} carrying exactly ONE target each: value = a reply the bot receives back, url = opens a link (add track: true to count each tap per conversation, readable as analytics_link_clicks_v in analytics.query; add tag: <ai_tag id> beside it to put that tag on the CONVERSATION at the tap, so for people, not groups, where any member's tap tags the whole group; add pass: \"<name>\" to open the url with <name>=<the chat's id on its channel, a Telegram user id for a bot chat>, so the site can name the person back in its own events), web_app = opens a Mini App over https. The string, and a card's text, substitute {from_name}, {message_text}, {channel_type}, {thread_id}, {raw_data}; button labels and targets are sent verbatim. A card with buttons needs send_mode auto — a draft row cannot carry a keyboard. Buttons need a channel that renders them (telegram_bot). A card needs a chat to land in, so it refuses on webhook, schedule and other thread-less triggers where a string still drafts.\n- handoff: {agent_id, task} — only on a rule_based agent's trigger (refused elsewhere). After the rule's deterministic part has actually happened — the template's message delivered, or the agent's script completed — the named agent runs on the same thread, in its own send mode, exactly as its own trigger would run it: the customer's message reaches it as the incoming message, and `task` reaches it as the handoff brief from this rule. The rule stays instant; the thinking — a reminder, a referral code, a personal follow-up — happens behind it. Script plus handoff is how a workspace does something deterministic the platform has no verb for (file the thread, stamp a tag, push a row) and still lets an agent answer, with the deterministic part living in that agent's own script rather than in platform code. `task` substitutes {channel_type} and {thread_id} only; {message_text}, {from_name} and {raw_data} are refused (the sender would be writing the instruction — the message and its sender already reach the target as the incoming message). The target must be an active agentic or claude_channels agent in this workspace (the engines that read the brief). The target's run is a normal trigger run: it holds the thread lock, its own safety gates apply, and its final answer is delivered like any DM reply — a follow-up that should stay silent must call agent.silence. Nothing runs when the message was drafted, refused or failed, when the script failed, when a safety gate has the rule in draft mode, or when the event carries no thread for the target to answer on. A target paused or deleted after the trigger was written does not stop the rule's message: it goes out, and the reason the follow-up did not start is recorded in the rule's activity row (error field) and in the target's activity as an execution_error.\n- contact_states: [\"active\"] — filter by contact state\n- cooldown_seconds: 30 — min gap between runs per thread\n- max_runs_per_thread_per_hour: 5 — rate limit\n- once_per_thread: true — after this trigger completes on a conversation it steps aside there and the next matching trigger answers later messages. For a rule that files / tags / hands off once, not for the agent that holds the conversation.\n- answer_delay_s: 15 — voice pickup only (incoming_call, or incoming_message scoped to a voice channel). Ring the human's own devices this long before the agent answers; if they pick up, the agent stands down. 0/absent = answer immediately. Honoured on WhatsApp and Telegram 1:1 — other voice channels have nothing ringing to wait for and still answer at once.\n- auto_join: true|false — voice pickup, GROUP calls only. Whether the bot enters a matching group call on its own (true) or the call becomes a pending invite an operator accepts (false). OMIT THE KEY to defer to the channel setting (channel_account.state.voice_auto_join_policy, default 'approval'); resolution is trigger-when-present, then channel, then approval, so an explicit false beats a channel set to auto. Absent and false are different answers.\n- chat_ids: [\"-5172634473\"] — voice pickup, Telegram GROUP calls only. Scopes the trigger to specific groups; omit for every group call. Any id spelling works (5172634473, -5172634473, -1005172634473 are the same group). A call whose chat is unknown never satisfies it, so do NOT set this together with 1:1 / Twilio / Telnyx / WhatsApp / Android / LiveChat voice channels or the \"voice\" wildcard: those calls have no chat and the trigger would stop answering them.\n\nSupported fields for job_completed (proactive callback when a delegated job finishes):\n- source_agent_id: <int> — fire only when this agent's job completed\n- source_agent_slug: <str> — alternate to source_agent_id\n- job_type: \"agentic_session\" — match a specific job type (default: any)\n- outcome: [\"completed\"] | [\"escalated\"] | [\"completed\",\"escalated\"] — default [\"completed\"]\n- min_duration_seconds: <int> — skip very-short jobs (noise filter)\n- thread_filter: {thread_ids: [<int>...]} — restrict to specific threads\n\nSupported fields for calendar_event (fires N minutes before a Google Calendar event starts):\n- window_minutes_before: <int 1-1440> — REQUIRED, fire when an event starts within this window\n- channel_account_ids: [<int>...] — restrict to specific calendar accounts (default: all)\n- keywords: [\"standup\"] — word-boundary match on event title\n- prepare_meet_join: true — pre-invite pool bots to the event (enables unattended Meet join)\n\nSupported fields for task_assigned (a task on the workspace board was assigned to this agent):\n- target_session: <session_id> — for a Claude Code agent, the session the task goes to (default: the agent's own session)\n\nSupported fields for job_continuation (this agent's own call or video finished):\n- job_types: [\"voice_call\", \"video_generation\"] — which kinds re-invoke it (default: all)\n\nincoming_message action fields:\n- action: \"reply_text\" (default, normal agent run) or \"join_voice\" (deterministically join the voice channel resolved from the message — requires send_mode=auto)\n- message_source: \"real\" (default) | \"transcript\" | \"both\" — real messages vs turns during a live call. Transcripts are turns that address the agent.\n\ncalendar_event run-mode field (incoming_message uses `action` instead):\n- run_mode: \"text\" (default) or \"voice\" (join the meeting — requires send_mode=auto)\n- voice: {speak_first: <bool — greet immediately vs stay silent until addressed>, vision_mode: \"off\"|\"on_demand\"|\"continuous_0_3fps\"} — pairs with action=join_voice (incoming_message) or run_mode=voice (calendar_event)"
    • changedInput schema / properties / trigger_type / description
      Previous value: -"Type of trigger: 'incoming_message', 'incoming_call', 'schedule', 'webhook', 'event', 'blockchain_event', 'job_completed', 'calendar_event', 'lead_captured', or 'task_assigned' (a task on the workspace board was assigned to this agent; without one, assigning a task does not start the agent)"New value: +"Type of trigger: 'incoming_message', 'incoming_call', 'schedule', 'webhook', 'event', 'blockchain_event', 'job_completed', 'calendar_event', 'lead_captured', 'task_assigned' (a task on the workspace board was assigned to this agent; without one, assigning a task does not start the agent), or 'job_continuation' (this agent's OWN call from calls.make or video from videos.generate finished; without one the agent is not re-invoked to carry on)"
    • changedInput schema / properties / trigger_type / enum
      Previous value: -[
      -  "incoming_message",
      -  "incoming_call",
      -  "schedule",
      -  "webhook",
      -  "event",
      -  "blockchain_event",
      -  "job_completed",
      -  "calendar_event",
      -  "lead_captured",
      -  "task_assigned"
      -]New value: +[
      +  "incoming_message",
      +  "incoming_call",
      +  "schedule",
      +  "webhook",
      +  "event",
      +  "blockchain_event",
      +  "job_completed",
      +  "calendar_event",
      +  "lead_captured",
      +  "task_assigned",
      +  "job_continuation"
      +]
  3. Changed3 schema fields changed
    • changedInput schema / properties / conditions / description
      Previous value: -"Trigger conditions (JSON). Supported fields for incoming_message:\n- keywords: [\"pricing\",\"demo\"] — message must contain keyword(s) (free, no LLM cost)\n- keyword_match: \"any\" (default, OR) or \"all\" (AND)\n- channel_types: [\"telegram\",\"whatsapp\",\"livechat_voice\",\"twilio_voice\",\"telegram_voice\",\"voice\",...] — filter by channel. For voice, use EITHER the three per-channel keys (scoped) OR \"voice\" alone (wildcard matching all three) — mixing them is redundant. Per-channel keys: \"livechat_voice\" (web widget), \"twilio_voice\" (PSTN inbound), \"telegram_voice\" (Telegram p2p calls)\n- context_types: [\"dm\",\"group\",\"channel\",\"livechat\"] — filter by chat type\n- group_mode: \"mentions_only\" or \"questions\" — for group chats\n- channel_account_ids: [\"123\"] — restrict to specific accounts. Only accounts THIS workspace answers: a trigger never fires for another workspace's channel, so create it on an agent there\n- folder_ids: [5,10] — restrict to threads in folders\n- ai_tag_ids: [1,2] — restrict to threads with AI tags\n- ai_filter_ids: [1,2] — semantic intent filters (message matched via embedding similarity, works in noisy groups)\n- ai_filter_mode: \"any\" (default, OR) or \"all\" (AND) — how multiple AI filters combine\n- ai_filters: [{id: 1}, {name: \"...\", description: \"...\"}] — shorthand: reference existing by id or create inline (calls Voyage embedding API). If a filter with the same name already exists, it is reused by id. Prefer referencing existing filters by id when available. Use ai_filters.create + ai_filters.test for fine-tuning before assigning.\n- template: the reply a rule_based agent sends, with no model run. Either a string (\"Hi {from_name}, we open at 9\") or a card object {text, attachments, buttons} — text is required, attachments is a list of file ids (files.upload), buttons is a list of {label, value|url|web_app} carrying exactly ONE target each: value = a reply the bot receives back, url = opens a link (add track: true to count each tap per conversation, readable as analytics_link_clicks_v in analytics.query; add tag: <ai_tag id> beside it to put that tag on the CONVERSATION at the tap, so for people, not groups, where any member's tap tags the whole group; add pass: \"<name>\" to open the url with <name>=<the chat's id on its channel, a Telegram user id for a bot chat>, so the site can name the person back in its own events), web_app = opens a Mini App over https. The string, and a card's text, substitute {from_name}, {message_text}, {channel_type}, {thread_id}, {raw_data}; button labels and targets are sent verbatim. A card with buttons needs send_mode auto — a draft row cannot carry a keyboard. Buttons need a channel that renders them (telegram_bot). A card needs a chat to land in, so it refuses on webhook, schedule and other thread-less triggers where a string still drafts.\n- handoff: {agent_id, task} — only on a rule_based agent's trigger (refused elsewhere). After the rule's deterministic part has actually happened — the template's message delivered, or the agent's script completed — the named agent runs on the same thread, in its own send mode, exactly as its own trigger would run it: the customer's message reaches it as the incoming message, and `task` reaches it as the handoff brief from this rule. The rule stays instant; the thinking — a reminder, a referral code, a personal follow-up — happens behind it. Script plus handoff is how a workspace does something deterministic the platform has no verb for (file the thread, stamp a tag, push a row) and still lets an agent answer, with the deterministic part living in that agent's own script rather than in platform code. `task` substitutes {channel_type} and {thread_id} only; {message_text}, {from_name} and {raw_data} are refused (the sender would be writing the instruction — the message and its sender already reach the target as the incoming message). The target must be an active agentic or claude_channels agent in this workspace (the engines that read the brief). The target's run is a normal trigger run: it holds the thread lock, its own safety gates apply, and its final answer is delivered like any DM reply — a follow-up that should stay silent must call agent.silence. Nothing runs when the message was drafted, refused or failed, when the script failed, when a safety gate has the rule in draft mode, or when the event carries no thread for the target to answer on. A target paused or deleted after the trigger was written does not stop the rule's message: it goes out, and the reason the follow-up did not start is recorded in the rule's activity row (error field) and in the target's activity as an execution_error.\n- contact_states: [\"active\"] — filter by contact state\n- cooldown_seconds: 30 — min gap between runs per thread\n- max_runs_per_thread_per_hour: 5 — rate limit\n- once_per_thread: true — after this trigger completes on a conversation it steps aside there and the next matching trigger answers later messages. For a rule that files / tags / hands off once, not for the agent that holds the conversation.\n- answer_delay_s: 15 — voice pickup only (incoming_call, or incoming_message scoped to a voice channel). Ring the human's own devices this long before the agent answers; if they pick up, the agent stands down. 0/absent = answer immediately. Honoured on WhatsApp and Telegram 1:1 — other voice channels have nothing ringing to wait for and still answer at once.\n- auto_join: true|false — voice pickup, GROUP calls only. Whether the bot enters a matching group call on its own (true) or the call becomes a pending invite an operator accepts (false). OMIT THE KEY to defer to the channel setting (channel_account.state.voice_auto_join_policy, default 'approval'); resolution is trigger-when-present, then channel, then approval, so an explicit false beats a channel set to auto. Absent and false are different answers.\n- chat_ids: [\"-5172634473\"] — voice pickup, Telegram GROUP calls only. Scopes the trigger to specific groups; omit for every group call. Any id spelling works (5172634473, -5172634473, -1005172634473 are the same group). A call whose chat is unknown never satisfies it, so do NOT set this together with 1:1 / Twilio / Telnyx / WhatsApp / Android / LiveChat voice channels or the \"voice\" wildcard: those calls have no chat and the trigger would stop answering them.\n\nSupported fields for job_completed (proactive callback when a delegated job finishes):\n- source_agent_id: <int> — fire only when this agent's job completed\n- source_agent_slug: <str> — alternate to source_agent_id\n- job_type: \"agentic_session\" — match a specific job type (default: any)\n- outcome: [\"completed\"] | [\"escalated\"] | [\"completed\",\"escalated\"] — default [\"completed\"]\n- min_duration_seconds: <int> — skip very-short jobs (noise filter)\n- thread_filter: {thread_ids: [<int>...]} — restrict to specific threads\n\nSupported fields for calendar_event (fires N minutes before a Google Calendar event starts):\n- window_minutes_before: <int 1-1440> — REQUIRED, fire when an event starts within this window\n- channel_account_ids: [<int>...] — restrict to specific calendar accounts (default: all)\n- keywords: [\"standup\"] — word-boundary match on event title\n- prepare_meet_join: true — pre-invite pool bots to the event (enables unattended Meet join)\n\nincoming_message action fields:\n- action: \"reply_text\" (default, normal agent run) or \"join_voice\" (deterministically join the voice channel resolved from the message — requires send_mode=auto)\n- message_source: \"real\" (default) | \"transcript\" | \"both\" — real messages vs turns during a live call. Transcripts are turns that address the agent.\n\ncalendar_event run-mode field (incoming_message uses `action` instead):\n- run_mode: \"text\" (default) or \"voice\" (join the meeting — requires send_mode=auto)\n- voice: {speak_first: <bool — greet immediately vs stay silent until addressed>, vision_mode: \"off\"|\"on_demand\"|\"continuous_0_3fps\"} — pairs with action=join_voice (incoming_message) or run_mode=voice (calendar_event)"New value: +"Trigger conditions (JSON). Supported fields for incoming_message:\n- keywords: [\"pricing\",\"demo\"] — message must contain keyword(s) (free, no LLM cost)\n- keyword_match: \"any\" (default, OR) or \"all\" (AND)\n- channel_types: [\"telegram\",\"whatsapp\",\"livechat_voice\",\"twilio_voice\",\"telegram_voice\",\"voice\",...] — filter by channel. For voice, use EITHER the three per-channel keys (scoped) OR \"voice\" alone (wildcard matching all three) — mixing them is redundant. Per-channel keys: \"livechat_voice\" (web widget), \"twilio_voice\" (PSTN inbound), \"telegram_voice\" (Telegram p2p calls)\n- context_types: [\"dm\",\"group\",\"channel\",\"livechat\"] — filter by chat type\n- group_mode: \"mentions_only\" or \"questions\" — for group chats\n- channel_account_ids: [\"123\"] — restrict to specific accounts. Only accounts THIS workspace answers: a trigger never fires for another workspace's channel, so create it on an agent there\n- folder_ids: [5,10] — restrict to threads in folders\n- ai_tag_ids: [1,2] — restrict to threads with AI tags\n- ai_filter_ids: [1,2] — semantic intent filters (message matched via embedding similarity, works in noisy groups)\n- ai_filter_mode: \"any\" (default, OR) or \"all\" (AND) — how multiple AI filters combine\n- ai_filters: [{id: 1}, {name: \"...\", description: \"...\"}] — shorthand: reference existing by id or create inline (calls Voyage embedding API). If a filter with the same name already exists, it is reused by id. Prefer referencing existing filters by id when available. Use ai_filters.create + ai_filters.test for fine-tuning before assigning.\n- template: the reply a rule_based agent sends, with no model run. Either a string (\"Hi {from_name}, we open at 9\") or a card object {text, attachments, buttons} — text is required, attachments is a list of file ids (files.upload), buttons is a list of {label, value|url|web_app} carrying exactly ONE target each: value = a reply the bot receives back, url = opens a link (add track: true to count each tap per conversation, readable as analytics_link_clicks_v in analytics.query; add tag: <ai_tag id> beside it to put that tag on the CONVERSATION at the tap, so for people, not groups, where any member's tap tags the whole group; add pass: \"<name>\" to open the url with <name>=<the chat's id on its channel, a Telegram user id for a bot chat>, so the site can name the person back in its own events), web_app = opens a Mini App over https. The string, and a card's text, substitute {from_name}, {message_text}, {channel_type}, {thread_id}, {raw_data}; button labels and targets are sent verbatim. A card with buttons needs send_mode auto — a draft row cannot carry a keyboard. Buttons need a channel that renders them (telegram_bot). A card needs a chat to land in, so it refuses on webhook, schedule and other thread-less triggers where a string still drafts.\n- handoff: {agent_id, task} — only on a rule_based agent's trigger (refused elsewhere). After the rule's deterministic part has actually happened — the template's message delivered, or the agent's script completed — the named agent runs on the same thread, in its own send mode, exactly as its own trigger would run it: the customer's message reaches it as the incoming message, and `task` reaches it as the handoff brief from this rule. The rule stays instant; the thinking — a reminder, a referral code, a personal follow-up — happens behind it. Script plus handoff is how a workspace does something deterministic the platform has no verb for (file the thread, stamp a tag, push a row) and still lets an agent answer, with the deterministic part living in that agent's own script rather than in platform code. `task` substitutes {channel_type} and {thread_id} only; {message_text}, {from_name} and {raw_data} are refused (the sender would be writing the instruction — the message and its sender already reach the target as the incoming message). The target must be an active agentic or claude_channels agent in this workspace (the engines that read the brief). The target's run is a normal trigger run: it holds the thread lock, its own safety gates apply, and its final answer is delivered like any DM reply — a follow-up that should stay silent must call agent.silence. Nothing runs when the message was drafted, refused or failed, when the script failed, when a safety gate has the rule in draft mode, or when the event carries no thread for the target to answer on. A target paused or deleted after the trigger was written does not stop the rule's message: it goes out, and the reason the follow-up did not start is recorded in the rule's activity row (error field) and in the target's activity as an execution_error.\n- contact_states: [\"active\"] — filter by contact state\n- cooldown_seconds: 30 — min gap between runs per thread\n- max_runs_per_thread_per_hour: 5 — rate limit\n- once_per_thread: true — after this trigger completes on a conversation it steps aside there and the next matching trigger answers later messages. For a rule that files / tags / hands off once, not for the agent that holds the conversation.\n- answer_delay_s: 15 — voice pickup only (incoming_call, or incoming_message scoped to a voice channel). Ring the human's own devices this long before the agent answers; if they pick up, the agent stands down. 0/absent = answer immediately. Honoured on WhatsApp and Telegram 1:1 — other voice channels have nothing ringing to wait for and still answer at once.\n- auto_join: true|false — voice pickup, GROUP calls only. Whether the bot enters a matching group call on its own (true) or the call becomes a pending invite an operator accepts (false). OMIT THE KEY to defer to the channel setting (channel_account.state.voice_auto_join_policy, default 'approval'); resolution is trigger-when-present, then channel, then approval, so an explicit false beats a channel set to auto. Absent and false are different answers.\n- chat_ids: [\"-5172634473\"] — voice pickup, Telegram GROUP calls only. Scopes the trigger to specific groups; omit for every group call. Any id spelling works (5172634473, -5172634473, -1005172634473 are the same group). A call whose chat is unknown never satisfies it, so do NOT set this together with 1:1 / Twilio / Telnyx / WhatsApp / Android / LiveChat voice channels or the \"voice\" wildcard: those calls have no chat and the trigger would stop answering them.\n\nSupported fields for job_completed (proactive callback when a delegated job finishes):\n- source_agent_id: <int> — fire only when this agent's job completed\n- source_agent_slug: <str> — alternate to source_agent_id\n- job_type: \"agentic_session\" — match a specific job type (default: any)\n- outcome: [\"completed\"] | [\"escalated\"] | [\"completed\",\"escalated\"] — default [\"completed\"]\n- min_duration_seconds: <int> — skip very-short jobs (noise filter)\n- thread_filter: {thread_ids: [<int>...]} — restrict to specific threads\n\nSupported fields for calendar_event (fires N minutes before a Google Calendar event starts):\n- window_minutes_before: <int 1-1440> — REQUIRED, fire when an event starts within this window\n- channel_account_ids: [<int>...] — restrict to specific calendar accounts (default: all)\n- keywords: [\"standup\"] — word-boundary match on event title\n- prepare_meet_join: true — pre-invite pool bots to the event (enables unattended Meet join)\n\nSupported fields for task_assigned (a task on the workspace board was assigned to this agent):\n- target_session: <session_id> — for a Claude Code agent, the session the task goes to (default: the agent's own session)\n\nincoming_message action fields:\n- action: \"reply_text\" (default, normal agent run) or \"join_voice\" (deterministically join the voice channel resolved from the message — requires send_mode=auto)\n- message_source: \"real\" (default) | \"transcript\" | \"both\" — real messages vs turns during a live call. Transcripts are turns that address the agent.\n\ncalendar_event run-mode field (incoming_message uses `action` instead):\n- run_mode: \"text\" (default) or \"voice\" (join the meeting — requires send_mode=auto)\n- voice: {speak_first: <bool — greet immediately vs stay silent until addressed>, vision_mode: \"off\"|\"on_demand\"|\"continuous_0_3fps\"} — pairs with action=join_voice (incoming_message) or run_mode=voice (calendar_event)"
    • changedInput schema / properties / trigger_type / description
      Previous value: -"Type of trigger: 'incoming_message', 'incoming_call', 'schedule', 'webhook', 'event', 'blockchain_event', 'job_completed', 'calendar_event', or 'lead_captured'"New value: +"Type of trigger: 'incoming_message', 'incoming_call', 'schedule', 'webhook', 'event', 'blockchain_event', 'job_completed', 'calendar_event', 'lead_captured', or 'task_assigned' (a task on the workspace board was assigned to this agent; without one, assigning a task does not start the agent)"
    • changedInput schema / properties / trigger_type / enum
      Previous value: -[
      -  "incoming_message",
      -  "incoming_call",
      -  "schedule",
      -  "webhook",
      -  "event",
      -  "blockchain_event",
      -  "job_completed",
      -  "calendar_event",
      -  "lead_captured"
      -]New value: +[
      +  "incoming_message",
      +  "incoming_call",
      +  "schedule",
      +  "webhook",
      +  "event",
      +  "blockchain_event",
      +  "job_completed",
      +  "calendar_event",
      +  "lead_captured",
      +  "task_assigned"
      +]
  4. Changed1 schema field changed
    • changedInput schema / properties / conditions / description
      Previous value: -"Trigger conditions (JSON). Supported fields for incoming_message:\n- keywords: [\"pricing\",\"demo\"] — message must contain keyword(s) (free, no LLM cost)\n- keyword_match: \"any\" (default, OR) or \"all\" (AND)\n- channel_types: [\"telegram\",\"whatsapp\",\"livechat_voice\",\"twilio_voice\",\"telegram_voice\",\"voice\",...] — filter by channel. For voice, use EITHER the three per-channel keys (scoped) OR \"voice\" alone (wildcard matching all three) — mixing them is redundant. Per-channel keys: \"livechat_voice\" (web widget), \"twilio_voice\" (PSTN inbound), \"telegram_voice\" (Telegram p2p calls)\n- context_types: [\"dm\",\"group\",\"channel\",\"livechat\"] — filter by chat type\n- group_mode: \"mentions_only\" or \"questions\" — for group chats\n- channel_account_ids: [\"123\"] — restrict to specific accounts. Only accounts THIS workspace answers: a trigger never fires for another workspace's channel, so create it on an agent there\n- folder_ids: [5,10] — restrict to threads in folders\n- ai_tag_ids: [1,2] — restrict to threads with AI tags\n- ai_filter_ids: [1,2] — semantic intent filters (message matched via embedding similarity, works in noisy groups)\n- ai_filter_mode: \"any\" (default, OR) or \"all\" (AND) — how multiple AI filters combine\n- ai_filters: [{id: 1}, {name: \"...\", description: \"...\"}] — shorthand: reference existing by id or create inline (calls Voyage embedding API). If a filter with the same name already exists, it is reused by id. Prefer referencing existing filters by id when available. Use ai_filters.create + ai_filters.test for fine-tuning before assigning.\n- template: the reply a rule_based agent sends, with no model run. Either a string (\"Hi {from_name}, we open at 9\") or a card object {text, attachments, buttons} — text is required, attachments is a list of file ids (files.upload), buttons is a list of {label, value|url|web_app} carrying exactly ONE target each: value = a reply the bot receives back, url = opens a link (add track: true to count each tap per conversation, readable as analytics_link_clicks_v in analytics.query; add tag: <ai_tag id> beside it to put that tag on the CONVERSATION at the tap, so for people, not groups, where any member's tap tags the whole group), web_app = opens a Mini App over https. The string, and a card's text, substitute {from_name}, {message_text}, {channel_type}, {thread_id}, {raw_data}; button labels and targets are sent verbatim. A card with buttons needs send_mode auto — a draft row cannot carry a keyboard. Buttons need a channel that renders them (telegram_bot). A card needs a chat to land in, so it refuses on webhook, schedule and other thread-less triggers where a string still drafts.\n- handoff: {agent_id, task} — only on a rule_based agent's trigger (refused elsewhere). After the rule's deterministic part has actually happened — the template's message delivered, or the agent's script completed — the named agent runs on the same thread, in its own send mode, exactly as its own trigger would run it: the customer's message reaches it as the incoming message, and `task` reaches it as the handoff brief from this rule. The rule stays instant; the thinking — a reminder, a referral code, a personal follow-up — happens behind it. Script plus handoff is how a workspace does something deterministic the platform has no verb for (file the thread, stamp a tag, push a row) and still lets an agent answer, with the deterministic part living in that agent's own script rather than in platform code. `task` substitutes {channel_type} and {thread_id} only; {message_text}, {from_name} and {raw_data} are refused (the sender would be writing the instruction — the message and its sender already reach the target as the incoming message). The target must be an active agentic or claude_channels agent in this workspace (the engines that read the brief). The target's run is a normal trigger run: it holds the thread lock, its own safety gates apply, and its final answer is delivered like any DM reply — a follow-up that should stay silent must call agent.silence. Nothing runs when the message was drafted, refused or failed, when the script failed, when a safety gate has the rule in draft mode, or when the event carries no thread for the target to answer on. A target paused or deleted after the trigger was written does not stop the rule's message: it goes out, and the reason the follow-up did not start is recorded in the rule's activity row (error field) and in the target's activity as an execution_error.\n- contact_states: [\"active\"] — filter by contact state\n- cooldown_seconds: 30 — min gap between runs per thread\n- max_runs_per_thread_per_hour: 5 — rate limit\n- once_per_thread: true — after this trigger completes on a conversation it steps aside there and the next matching trigger answers later messages. For a rule that files / tags / hands off once, not for the agent that holds the conversation.\n- answer_delay_s: 15 — voice pickup only (incoming_call, or incoming_message scoped to a voice channel). Ring the human's own devices this long before the agent answers; if they pick up, the agent stands down. 0/absent = answer immediately. Honoured on WhatsApp and Telegram 1:1 — other voice channels have nothing ringing to wait for and still answer at once.\n- auto_join: true|false — voice pickup, GROUP calls only. Whether the bot enters a matching group call on its own (true) or the call becomes a pending invite an operator accepts (false). OMIT THE KEY to defer to the channel setting (channel_account.state.voice_auto_join_policy, default 'approval'); resolution is trigger-when-present, then channel, then approval, so an explicit false beats a channel set to auto. Absent and false are different answers.\n- chat_ids: [\"-5172634473\"] — voice pickup, Telegram GROUP calls only. Scopes the trigger to specific groups; omit for every group call. Any id spelling works (5172634473, -5172634473, -1005172634473 are the same group). A call whose chat is unknown never satisfies it, so do NOT set this together with 1:1 / Twilio / Telnyx / WhatsApp / Android / LiveChat voice channels or the \"voice\" wildcard: those calls have no chat and the trigger would stop answering them.\n\nSupported fields for job_completed (proactive callback when a delegated job finishes):\n- source_agent_id: <int> — fire only when this agent's job completed\n- source_agent_slug: <str> — alternate to source_agent_id\n- job_type: \"agentic_session\" — match a specific job type (default: any)\n- outcome: [\"completed\"] | [\"escalated\"] | [\"completed\",\"escalated\"] — default [\"completed\"]\n- min_duration_seconds: <int> — skip very-short jobs (noise filter)\n- thread_filter: {thread_ids: [<int>...]} — restrict to specific threads\n\nSupported fields for calendar_event (fires N minutes before a Google Calendar event starts):\n- window_minutes_before: <int 1-1440> — REQUIRED, fire when an event starts within this window\n- channel_account_ids: [<int>...] — restrict to specific calendar accounts (default: all)\n- keywords: [\"standup\"] — word-boundary match on event title\n- prepare_meet_join: true — pre-invite pool bots to the event (enables unattended Meet join)\n\nincoming_message action fields:\n- action: \"reply_text\" (default, normal agent run) or \"join_voice\" (deterministically join the voice channel resolved from the message — requires send_mode=auto)\n- message_source: \"real\" (default) | \"transcript\" | \"both\" — real messages vs turns during a live call. Transcripts are turns that address the agent.\n\ncalendar_event run-mode field (incoming_message uses `action` instead):\n- run_mode: \"text\" (default) or \"voice\" (join the meeting — requires send_mode=auto)\n- voice: {speak_first: <bool — greet immediately vs stay silent until addressed>, vision_mode: \"off\"|\"on_demand\"|\"continuous_0_3fps\"} — pairs with action=join_voice (incoming_message) or run_mode=voice (calendar_event)"New value: +"Trigger conditions (JSON). Supported fields for incoming_message:\n- keywords: [\"pricing\",\"demo\"] — message must contain keyword(s) (free, no LLM cost)\n- keyword_match: \"any\" (default, OR) or \"all\" (AND)\n- channel_types: [\"telegram\",\"whatsapp\",\"livechat_voice\",\"twilio_voice\",\"telegram_voice\",\"voice\",...] — filter by channel. For voice, use EITHER the three per-channel keys (scoped) OR \"voice\" alone (wildcard matching all three) — mixing them is redundant. Per-channel keys: \"livechat_voice\" (web widget), \"twilio_voice\" (PSTN inbound), \"telegram_voice\" (Telegram p2p calls)\n- context_types: [\"dm\",\"group\",\"channel\",\"livechat\"] — filter by chat type\n- group_mode: \"mentions_only\" or \"questions\" — for group chats\n- channel_account_ids: [\"123\"] — restrict to specific accounts. Only accounts THIS workspace answers: a trigger never fires for another workspace's channel, so create it on an agent there\n- folder_ids: [5,10] — restrict to threads in folders\n- ai_tag_ids: [1,2] — restrict to threads with AI tags\n- ai_filter_ids: [1,2] — semantic intent filters (message matched via embedding similarity, works in noisy groups)\n- ai_filter_mode: \"any\" (default, OR) or \"all\" (AND) — how multiple AI filters combine\n- ai_filters: [{id: 1}, {name: \"...\", description: \"...\"}] — shorthand: reference existing by id or create inline (calls Voyage embedding API). If a filter with the same name already exists, it is reused by id. Prefer referencing existing filters by id when available. Use ai_filters.create + ai_filters.test for fine-tuning before assigning.\n- template: the reply a rule_based agent sends, with no model run. Either a string (\"Hi {from_name}, we open at 9\") or a card object {text, attachments, buttons} — text is required, attachments is a list of file ids (files.upload), buttons is a list of {label, value|url|web_app} carrying exactly ONE target each: value = a reply the bot receives back, url = opens a link (add track: true to count each tap per conversation, readable as analytics_link_clicks_v in analytics.query; add tag: <ai_tag id> beside it to put that tag on the CONVERSATION at the tap, so for people, not groups, where any member's tap tags the whole group; add pass: \"<name>\" to open the url with <name>=<the chat's id on its channel, a Telegram user id for a bot chat>, so the site can name the person back in its own events), web_app = opens a Mini App over https. The string, and a card's text, substitute {from_name}, {message_text}, {channel_type}, {thread_id}, {raw_data}; button labels and targets are sent verbatim. A card with buttons needs send_mode auto — a draft row cannot carry a keyboard. Buttons need a channel that renders them (telegram_bot). A card needs a chat to land in, so it refuses on webhook, schedule and other thread-less triggers where a string still drafts.\n- handoff: {agent_id, task} — only on a rule_based agent's trigger (refused elsewhere). After the rule's deterministic part has actually happened — the template's message delivered, or the agent's script completed — the named agent runs on the same thread, in its own send mode, exactly as its own trigger would run it: the customer's message reaches it as the incoming message, and `task` reaches it as the handoff brief from this rule. The rule stays instant; the thinking — a reminder, a referral code, a personal follow-up — happens behind it. Script plus handoff is how a workspace does something deterministic the platform has no verb for (file the thread, stamp a tag, push a row) and still lets an agent answer, with the deterministic part living in that agent's own script rather than in platform code. `task` substitutes {channel_type} and {thread_id} only; {message_text}, {from_name} and {raw_data} are refused (the sender would be writing the instruction — the message and its sender already reach the target as the incoming message). The target must be an active agentic or claude_channels agent in this workspace (the engines that read the brief). The target's run is a normal trigger run: it holds the thread lock, its own safety gates apply, and its final answer is delivered like any DM reply — a follow-up that should stay silent must call agent.silence. Nothing runs when the message was drafted, refused or failed, when the script failed, when a safety gate has the rule in draft mode, or when the event carries no thread for the target to answer on. A target paused or deleted after the trigger was written does not stop the rule's message: it goes out, and the reason the follow-up did not start is recorded in the rule's activity row (error field) and in the target's activity as an execution_error.\n- contact_states: [\"active\"] — filter by contact state\n- cooldown_seconds: 30 — min gap between runs per thread\n- max_runs_per_thread_per_hour: 5 — rate limit\n- once_per_thread: true — after this trigger completes on a conversation it steps aside there and the next matching trigger answers later messages. For a rule that files / tags / hands off once, not for the agent that holds the conversation.\n- answer_delay_s: 15 — voice pickup only (incoming_call, or incoming_message scoped to a voice channel). Ring the human's own devices this long before the agent answers; if they pick up, the agent stands down. 0/absent = answer immediately. Honoured on WhatsApp and Telegram 1:1 — other voice channels have nothing ringing to wait for and still answer at once.\n- auto_join: true|false — voice pickup, GROUP calls only. Whether the bot enters a matching group call on its own (true) or the call becomes a pending invite an operator accepts (false). OMIT THE KEY to defer to the channel setting (channel_account.state.voice_auto_join_policy, default 'approval'); resolution is trigger-when-present, then channel, then approval, so an explicit false beats a channel set to auto. Absent and false are different answers.\n- chat_ids: [\"-5172634473\"] — voice pickup, Telegram GROUP calls only. Scopes the trigger to specific groups; omit for every group call. Any id spelling works (5172634473, -5172634473, -1005172634473 are the same group). A call whose chat is unknown never satisfies it, so do NOT set this together with 1:1 / Twilio / Telnyx / WhatsApp / Android / LiveChat voice channels or the \"voice\" wildcard: those calls have no chat and the trigger would stop answering them.\n\nSupported fields for job_completed (proactive callback when a delegated job finishes):\n- source_agent_id: <int> — fire only when this agent's job completed\n- source_agent_slug: <str> — alternate to source_agent_id\n- job_type: \"agentic_session\" — match a specific job type (default: any)\n- outcome: [\"completed\"] | [\"escalated\"] | [\"completed\",\"escalated\"] — default [\"completed\"]\n- min_duration_seconds: <int> — skip very-short jobs (noise filter)\n- thread_filter: {thread_ids: [<int>...]} — restrict to specific threads\n\nSupported fields for calendar_event (fires N minutes before a Google Calendar event starts):\n- window_minutes_before: <int 1-1440> — REQUIRED, fire when an event starts within this window\n- channel_account_ids: [<int>...] — restrict to specific calendar accounts (default: all)\n- keywords: [\"standup\"] — word-boundary match on event title\n- prepare_meet_join: true — pre-invite pool bots to the event (enables unattended Meet join)\n\nincoming_message action fields:\n- action: \"reply_text\" (default, normal agent run) or \"join_voice\" (deterministically join the voice channel resolved from the message — requires send_mode=auto)\n- message_source: \"real\" (default) | \"transcript\" | \"both\" — real messages vs turns during a live call. Transcripts are turns that address the agent.\n\ncalendar_event run-mode field (incoming_message uses `action` instead):\n- run_mode: \"text\" (default) or \"voice\" (join the meeting — requires send_mode=auto)\n- voice: {speak_first: <bool — greet immediately vs stay silent until addressed>, vision_mode: \"off\"|\"on_demand\"|\"continuous_0_3fps\"} — pairs with action=join_voice (incoming_message) or run_mode=voice (calendar_event)"
  5. Changed1 schema field changed
    • changedInput schema / properties / conditions / description
      Previous value: -"Trigger conditions (JSON). Supported fields for incoming_message:\n- keywords: [\"pricing\",\"demo\"] — message must contain keyword(s) (free, no LLM cost)\n- keyword_match: \"any\" (default, OR) or \"all\" (AND)\n- channel_types: [\"telegram\",\"whatsapp\",\"livechat_voice\",\"twilio_voice\",\"telegram_voice\",\"voice\",...] — filter by channel. For voice, use EITHER the three per-channel keys (scoped) OR \"voice\" alone (wildcard matching all three) — mixing them is redundant. Per-channel keys: \"livechat_voice\" (web widget), \"twilio_voice\" (PSTN inbound), \"telegram_voice\" (Telegram p2p calls)\n- context_types: [\"dm\",\"group\",\"channel\",\"livechat\"] — filter by chat type\n- group_mode: \"mentions_only\" or \"questions\" — for group chats\n- channel_account_ids: [\"123\"] — restrict to specific accounts. Only accounts THIS workspace answers: a trigger never fires for another workspace's channel, so create it on an agent there\n- folder_ids: [5,10] — restrict to threads in folders\n- ai_tag_ids: [1,2] — restrict to threads with AI tags\n- ai_filter_ids: [1,2] — semantic intent filters (message matched via embedding similarity, works in noisy groups)\n- ai_filter_mode: \"any\" (default, OR) or \"all\" (AND) — how multiple AI filters combine\n- ai_filters: [{id: 1}, {name: \"...\", description: \"...\"}] — shorthand: reference existing by id or create inline (calls Voyage embedding API). If a filter with the same name already exists, it is reused by id. Prefer referencing existing filters by id when available. Use ai_filters.create + ai_filters.test for fine-tuning before assigning.\n- template: the reply a rule_based agent sends, with no model run. Either a string (\"Hi {from_name}, we open at 9\") or a card object {text, attachments, buttons} — text is required, attachments is a list of file ids (files.upload), buttons is a list of {label, value|url|web_app} carrying exactly ONE target each: value = a reply the bot receives back, url = opens a link (add track: true to count each tap per conversation, readable as analytics_link_clicks_v in analytics.query), web_app = opens a Mini App over https. The string, and a card's text, substitute {from_name}, {message_text}, {channel_type}, {thread_id}, {raw_data}; button labels and targets are sent verbatim. A card with buttons needs send_mode auto — a draft row cannot carry a keyboard. Buttons need a channel that renders them (telegram_bot). A card needs a chat to land in, so it refuses on webhook, schedule and other thread-less triggers where a string still drafts.\n- handoff: {agent_id, task} — only on a rule_based agent's trigger (refused elsewhere). After the rule's deterministic part has actually happened — the template's message delivered, or the agent's script completed — the named agent runs on the same thread, in its own send mode, exactly as its own trigger would run it: the customer's message reaches it as the incoming message, and `task` reaches it as the handoff brief from this rule. The rule stays instant; the thinking — a reminder, a referral code, a personal follow-up — happens behind it. Script plus handoff is how a workspace does something deterministic the platform has no verb for (file the thread, stamp a tag, push a row) and still lets an agent answer, with the deterministic part living in that agent's own script rather than in platform code. `task` substitutes {channel_type} and {thread_id} only; {message_text}, {from_name} and {raw_data} are refused (the sender would be writing the instruction — the message and its sender already reach the target as the incoming message). The target must be an active agentic or claude_channels agent in this workspace (the engines that read the brief). The target's run is a normal trigger run: it holds the thread lock, its own safety gates apply, and its final answer is delivered like any DM reply — a follow-up that should stay silent must call agent.silence. Nothing runs when the message was drafted, refused or failed, when the script failed, when a safety gate has the rule in draft mode, or when the event carries no thread for the target to answer on. A target paused or deleted after the trigger was written does not stop the rule's message: it goes out, and the reason the follow-up did not start is recorded in the rule's activity row (error field) and in the target's activity as an execution_error.\n- contact_states: [\"active\"] — filter by contact state\n- cooldown_seconds: 30 — min gap between runs per thread\n- max_runs_per_thread_per_hour: 5 — rate limit\n- once_per_thread: true — after this trigger completes on a conversation it steps aside there and the next matching trigger answers later messages. For a rule that files / tags / hands off once, not for the agent that holds the conversation.\n- answer_delay_s: 15 — voice pickup only (incoming_call, or incoming_message scoped to a voice channel). Ring the human's own devices this long before the agent answers; if they pick up, the agent stands down. 0/absent = answer immediately. Honoured on WhatsApp and Telegram 1:1 — other voice channels have nothing ringing to wait for and still answer at once.\n- auto_join: true|false — voice pickup, GROUP calls only. Whether the bot enters a matching group call on its own (true) or the call becomes a pending invite an operator accepts (false). OMIT THE KEY to defer to the channel setting (channel_account.state.voice_auto_join_policy, default 'approval'); resolution is trigger-when-present, then channel, then approval, so an explicit false beats a channel set to auto. Absent and false are different answers.\n- chat_ids: [\"-5172634473\"] — voice pickup, Telegram GROUP calls only. Scopes the trigger to specific groups; omit for every group call. Any id spelling works (5172634473, -5172634473, -1005172634473 are the same group). A call whose chat is unknown never satisfies it, so do NOT set this together with 1:1 / Twilio / Telnyx / WhatsApp / Android / LiveChat voice channels or the \"voice\" wildcard: those calls have no chat and the trigger would stop answering them.\n\nSupported fields for job_completed (proactive callback when a delegated job finishes):\n- source_agent_id: <int> — fire only when this agent's job completed\n- source_agent_slug: <str> — alternate to source_agent_id\n- job_type: \"agentic_session\" — match a specific job type (default: any)\n- outcome: [\"completed\"] | [\"escalated\"] | [\"completed\",\"escalated\"] — default [\"completed\"]\n- min_duration_seconds: <int> — skip very-short jobs (noise filter)\n- thread_filter: {thread_ids: [<int>...]} — restrict to specific threads\n\nSupported fields for calendar_event (fires N minutes before a Google Calendar event starts):\n- window_minutes_before: <int 1-1440> — REQUIRED, fire when an event starts within this window\n- channel_account_ids: [<int>...] — restrict to specific calendar accounts (default: all)\n- keywords: [\"standup\"] — word-boundary match on event title\n- prepare_meet_join: true — pre-invite pool bots to the event (enables unattended Meet join)\n\nincoming_message action fields:\n- action: \"reply_text\" (default, normal agent run) or \"join_voice\" (deterministically join the voice channel resolved from the message — requires send_mode=auto)\n- message_source: \"real\" (default) | \"transcript\" | \"both\" — real messages vs turns during a live call. Transcripts are turns that address the agent.\n\ncalendar_event run-mode field (incoming_message uses `action` instead):\n- run_mode: \"text\" (default) or \"voice\" (join the meeting — requires send_mode=auto)\n- voice: {speak_first: <bool — greet immediately vs stay silent until addressed>, vision_mode: \"off\"|\"on_demand\"|\"continuous_0_3fps\"} — pairs with action=join_voice (incoming_message) or run_mode=voice (calendar_event)"New value: +"Trigger conditions (JSON). Supported fields for incoming_message:\n- keywords: [\"pricing\",\"demo\"] — message must contain keyword(s) (free, no LLM cost)\n- keyword_match: \"any\" (default, OR) or \"all\" (AND)\n- channel_types: [\"telegram\",\"whatsapp\",\"livechat_voice\",\"twilio_voice\",\"telegram_voice\",\"voice\",...] — filter by channel. For voice, use EITHER the three per-channel keys (scoped) OR \"voice\" alone (wildcard matching all three) — mixing them is redundant. Per-channel keys: \"livechat_voice\" (web widget), \"twilio_voice\" (PSTN inbound), \"telegram_voice\" (Telegram p2p calls)\n- context_types: [\"dm\",\"group\",\"channel\",\"livechat\"] — filter by chat type\n- group_mode: \"mentions_only\" or \"questions\" — for group chats\n- channel_account_ids: [\"123\"] — restrict to specific accounts. Only accounts THIS workspace answers: a trigger never fires for another workspace's channel, so create it on an agent there\n- folder_ids: [5,10] — restrict to threads in folders\n- ai_tag_ids: [1,2] — restrict to threads with AI tags\n- ai_filter_ids: [1,2] — semantic intent filters (message matched via embedding similarity, works in noisy groups)\n- ai_filter_mode: \"any\" (default, OR) or \"all\" (AND) — how multiple AI filters combine\n- ai_filters: [{id: 1}, {name: \"...\", description: \"...\"}] — shorthand: reference existing by id or create inline (calls Voyage embedding API). If a filter with the same name already exists, it is reused by id. Prefer referencing existing filters by id when available. Use ai_filters.create + ai_filters.test for fine-tuning before assigning.\n- template: the reply a rule_based agent sends, with no model run. Either a string (\"Hi {from_name}, we open at 9\") or a card object {text, attachments, buttons} — text is required, attachments is a list of file ids (files.upload), buttons is a list of {label, value|url|web_app} carrying exactly ONE target each: value = a reply the bot receives back, url = opens a link (add track: true to count each tap per conversation, readable as analytics_link_clicks_v in analytics.query; add tag: <ai_tag id> beside it to put that tag on the CONVERSATION at the tap, so for people, not groups, where any member's tap tags the whole group), web_app = opens a Mini App over https. The string, and a card's text, substitute {from_name}, {message_text}, {channel_type}, {thread_id}, {raw_data}; button labels and targets are sent verbatim. A card with buttons needs send_mode auto — a draft row cannot carry a keyboard. Buttons need a channel that renders them (telegram_bot). A card needs a chat to land in, so it refuses on webhook, schedule and other thread-less triggers where a string still drafts.\n- handoff: {agent_id, task} — only on a rule_based agent's trigger (refused elsewhere). After the rule's deterministic part has actually happened — the template's message delivered, or the agent's script completed — the named agent runs on the same thread, in its own send mode, exactly as its own trigger would run it: the customer's message reaches it as the incoming message, and `task` reaches it as the handoff brief from this rule. The rule stays instant; the thinking — a reminder, a referral code, a personal follow-up — happens behind it. Script plus handoff is how a workspace does something deterministic the platform has no verb for (file the thread, stamp a tag, push a row) and still lets an agent answer, with the deterministic part living in that agent's own script rather than in platform code. `task` substitutes {channel_type} and {thread_id} only; {message_text}, {from_name} and {raw_data} are refused (the sender would be writing the instruction — the message and its sender already reach the target as the incoming message). The target must be an active agentic or claude_channels agent in this workspace (the engines that read the brief). The target's run is a normal trigger run: it holds the thread lock, its own safety gates apply, and its final answer is delivered like any DM reply — a follow-up that should stay silent must call agent.silence. Nothing runs when the message was drafted, refused or failed, when the script failed, when a safety gate has the rule in draft mode, or when the event carries no thread for the target to answer on. A target paused or deleted after the trigger was written does not stop the rule's message: it goes out, and the reason the follow-up did not start is recorded in the rule's activity row (error field) and in the target's activity as an execution_error.\n- contact_states: [\"active\"] — filter by contact state\n- cooldown_seconds: 30 — min gap between runs per thread\n- max_runs_per_thread_per_hour: 5 — rate limit\n- once_per_thread: true — after this trigger completes on a conversation it steps aside there and the next matching trigger answers later messages. For a rule that files / tags / hands off once, not for the agent that holds the conversation.\n- answer_delay_s: 15 — voice pickup only (incoming_call, or incoming_message scoped to a voice channel). Ring the human's own devices this long before the agent answers; if they pick up, the agent stands down. 0/absent = answer immediately. Honoured on WhatsApp and Telegram 1:1 — other voice channels have nothing ringing to wait for and still answer at once.\n- auto_join: true|false — voice pickup, GROUP calls only. Whether the bot enters a matching group call on its own (true) or the call becomes a pending invite an operator accepts (false). OMIT THE KEY to defer to the channel setting (channel_account.state.voice_auto_join_policy, default 'approval'); resolution is trigger-when-present, then channel, then approval, so an explicit false beats a channel set to auto. Absent and false are different answers.\n- chat_ids: [\"-5172634473\"] — voice pickup, Telegram GROUP calls only. Scopes the trigger to specific groups; omit for every group call. Any id spelling works (5172634473, -5172634473, -1005172634473 are the same group). A call whose chat is unknown never satisfies it, so do NOT set this together with 1:1 / Twilio / Telnyx / WhatsApp / Android / LiveChat voice channels or the \"voice\" wildcard: those calls have no chat and the trigger would stop answering them.\n\nSupported fields for job_completed (proactive callback when a delegated job finishes):\n- source_agent_id: <int> — fire only when this agent's job completed\n- source_agent_slug: <str> — alternate to source_agent_id\n- job_type: \"agentic_session\" — match a specific job type (default: any)\n- outcome: [\"completed\"] | [\"escalated\"] | [\"completed\",\"escalated\"] — default [\"completed\"]\n- min_duration_seconds: <int> — skip very-short jobs (noise filter)\n- thread_filter: {thread_ids: [<int>...]} — restrict to specific threads\n\nSupported fields for calendar_event (fires N minutes before a Google Calendar event starts):\n- window_minutes_before: <int 1-1440> — REQUIRED, fire when an event starts within this window\n- channel_account_ids: [<int>...] — restrict to specific calendar accounts (default: all)\n- keywords: [\"standup\"] — word-boundary match on event title\n- prepare_meet_join: true — pre-invite pool bots to the event (enables unattended Meet join)\n\nincoming_message action fields:\n- action: \"reply_text\" (default, normal agent run) or \"join_voice\" (deterministically join the voice channel resolved from the message — requires send_mode=auto)\n- message_source: \"real\" (default) | \"transcript\" | \"both\" — real messages vs turns during a live call. Transcripts are turns that address the agent.\n\ncalendar_event run-mode field (incoming_message uses `action` instead):\n- run_mode: \"text\" (default) or \"voice\" (join the meeting — requires send_mode=auto)\n- voice: {speak_first: <bool — greet immediately vs stay silent until addressed>, vision_mode: \"off\"|\"on_demand\"|\"continuous_0_3fps\"} — pairs with action=join_voice (incoming_message) or run_mode=voice (calendar_event)"
  6. Changed1 schema field changed
    • changedInput schema / properties / conditions / description
      Previous value: -"Trigger conditions (JSON). Supported fields for incoming_message:\n- keywords: [\"pricing\",\"demo\"] — message must contain keyword(s) (free, no LLM cost)\n- keyword_match: \"any\" (default, OR) or \"all\" (AND)\n- channel_types: [\"telegram\",\"whatsapp\",\"livechat_voice\",\"twilio_voice\",\"telegram_voice\",\"voice\",...] — filter by channel. For voice, use EITHER the three per-channel keys (scoped) OR \"voice\" alone (wildcard matching all three) — mixing them is redundant. Per-channel keys: \"livechat_voice\" (web widget), \"twilio_voice\" (PSTN inbound), \"telegram_voice\" (Telegram p2p calls)\n- context_types: [\"dm\",\"group\",\"channel\",\"livechat\"] — filter by chat type\n- group_mode: \"mentions_only\" or \"questions\" — for group chats\n- channel_account_ids: [\"123\"] — restrict to specific accounts\n- folder_ids: [5,10] — restrict to threads in folders\n- ai_tag_ids: [1,2] — restrict to threads with AI tags\n- ai_filter_ids: [1,2] — semantic intent filters (message matched via embedding similarity, works in noisy groups)\n- ai_filter_mode: \"any\" (default, OR) or \"all\" (AND) — how multiple AI filters combine\n- ai_filters: [{id: 1}, {name: \"...\", description: \"...\"}] — shorthand: reference existing by id or create inline (calls Voyage embedding API). If a filter with the same name already exists, it is reused by id. Prefer referencing existing filters by id when available. Use ai_filters.create + ai_filters.test for fine-tuning before assigning.\n- template: the reply a rule_based agent sends, with no model run. Either a string (\"Hi {from_name}, we open at 9\") or a card object {text, attachments, buttons} — text is required, attachments is a list of file ids (files.upload), buttons is a list of {label, value|url|web_app} carrying exactly ONE target each: value = a reply the bot receives back, url = opens a link (add track: true to count each tap per conversation, readable as analytics_link_clicks_v in analytics.query), web_app = opens a Mini App over https. The string, and a card's text, substitute {from_name}, {message_text}, {channel_type}, {thread_id}, {raw_data}; button labels and targets are sent verbatim. A card with buttons needs send_mode auto — a draft row cannot carry a keyboard. Buttons need a channel that renders them (telegram_bot). A card needs a chat to land in, so it refuses on webhook, schedule and other thread-less triggers where a string still drafts.\n- handoff: {agent_id, task} — only on a rule_based agent's trigger (refused elsewhere). After the rule's deterministic part has actually happened — the template's message delivered, or the agent's script completed — the named agent runs on the same thread, in its own send mode, exactly as its own trigger would run it: the customer's message reaches it as the incoming message, and `task` reaches it as the handoff brief from this rule. The rule stays instant; the thinking — a reminder, a referral code, a personal follow-up — happens behind it. Script plus handoff is how a workspace does something deterministic the platform has no verb for (file the thread, stamp a tag, push a row) and still lets an agent answer, with the deterministic part living in that agent's own script rather than in platform code. `task` substitutes {channel_type} and {thread_id} only; {message_text}, {from_name} and {raw_data} are refused (the sender would be writing the instruction — the message and its sender already reach the target as the incoming message). The target must be an active agentic or claude_channels agent in this workspace (the engines that read the brief). The target's run is a normal trigger run: it holds the thread lock, its own safety gates apply, and its final answer is delivered like any DM reply — a follow-up that should stay silent must call agent.silence. Nothing runs when the message was drafted, refused or failed, when the script failed, when a safety gate has the rule in draft mode, or when the event carries no thread for the target to answer on. A target paused or deleted after the trigger was written does not stop the rule's message: it goes out, and the reason the follow-up did not start is recorded in the rule's activity row (error field) and in the target's activity as an execution_error.\n- contact_states: [\"active\"] — filter by contact state\n- cooldown_seconds: 30 — min gap between runs per thread\n- max_runs_per_thread_per_hour: 5 — rate limit\n- once_per_thread: true — after this trigger completes on a conversation it steps aside there and the next matching trigger answers later messages. For a rule that files / tags / hands off once, not for the agent that holds the conversation.\n- answer_delay_s: 15 — voice pickup only (incoming_call, or incoming_message scoped to a voice channel). Ring the human's own devices this long before the agent answers; if they pick up, the agent stands down. 0/absent = answer immediately. Honoured on WhatsApp and Telegram 1:1 — other voice channels have nothing ringing to wait for and still answer at once.\n- auto_join: true|false — voice pickup, GROUP calls only. Whether the bot enters a matching group call on its own (true) or the call becomes a pending invite an operator accepts (false). OMIT THE KEY to defer to the channel setting (channel_account.state.voice_auto_join_policy, default 'approval'); resolution is trigger-when-present, then channel, then approval, so an explicit false beats a channel set to auto. Absent and false are different answers.\n- chat_ids: [\"-5172634473\"] — voice pickup, Telegram GROUP calls only. Scopes the trigger to specific groups; omit for every group call. Any id spelling works (5172634473, -5172634473, -1005172634473 are the same group). A call whose chat is unknown never satisfies it, so do NOT set this together with 1:1 / Twilio / Telnyx / WhatsApp / Android / LiveChat voice channels or the \"voice\" wildcard: those calls have no chat and the trigger would stop answering them.\n\nSupported fields for job_completed (proactive callback when a delegated job finishes):\n- source_agent_id: <int> — fire only when this agent's job completed\n- source_agent_slug: <str> — alternate to source_agent_id\n- job_type: \"agentic_session\" — match a specific job type (default: any)\n- outcome: [\"completed\"] | [\"escalated\"] | [\"completed\",\"escalated\"] — default [\"completed\"]\n- min_duration_seconds: <int> — skip very-short jobs (noise filter)\n- thread_filter: {thread_ids: [<int>...]} — restrict to specific threads\n\nSupported fields for calendar_event (fires N minutes before a Google Calendar event starts):\n- window_minutes_before: <int 1-1440> — REQUIRED, fire when an event starts within this window\n- channel_account_ids: [<int>...] — restrict to specific calendar accounts (default: all)\n- keywords: [\"standup\"] — word-boundary match on event title\n- prepare_meet_join: true — pre-invite pool bots to the event (enables unattended Meet join)\n\nincoming_message action fields:\n- action: \"reply_text\" (default, normal agent run) or \"join_voice\" (deterministically join the voice channel resolved from the message — requires send_mode=auto)\n- message_source: \"real\" (default) | \"transcript\" | \"both\" — real messages vs turns during a live call. Transcripts are turns that address the agent.\n\ncalendar_event run-mode field (incoming_message uses `action` instead):\n- run_mode: \"text\" (default) or \"voice\" (join the meeting — requires send_mode=auto)\n- voice: {speak_first: <bool — greet immediately vs stay silent until addressed>, vision_mode: \"off\"|\"on_demand\"|\"continuous_0_3fps\"} — pairs with action=join_voice (incoming_message) or run_mode=voice (calendar_event)"New value: +"Trigger conditions (JSON). Supported fields for incoming_message:\n- keywords: [\"pricing\",\"demo\"] — message must contain keyword(s) (free, no LLM cost)\n- keyword_match: \"any\" (default, OR) or \"all\" (AND)\n- channel_types: [\"telegram\",\"whatsapp\",\"livechat_voice\",\"twilio_voice\",\"telegram_voice\",\"voice\",...] — filter by channel. For voice, use EITHER the three per-channel keys (scoped) OR \"voice\" alone (wildcard matching all three) — mixing them is redundant. Per-channel keys: \"livechat_voice\" (web widget), \"twilio_voice\" (PSTN inbound), \"telegram_voice\" (Telegram p2p calls)\n- context_types: [\"dm\",\"group\",\"channel\",\"livechat\"] — filter by chat type\n- group_mode: \"mentions_only\" or \"questions\" — for group chats\n- channel_account_ids: [\"123\"] — restrict to specific accounts. Only accounts THIS workspace answers: a trigger never fires for another workspace's channel, so create it on an agent there\n- folder_ids: [5,10] — restrict to threads in folders\n- ai_tag_ids: [1,2] — restrict to threads with AI tags\n- ai_filter_ids: [1,2] — semantic intent filters (message matched via embedding similarity, works in noisy groups)\n- ai_filter_mode: \"any\" (default, OR) or \"all\" (AND) — how multiple AI filters combine\n- ai_filters: [{id: 1}, {name: \"...\", description: \"...\"}] — shorthand: reference existing by id or create inline (calls Voyage embedding API). If a filter with the same name already exists, it is reused by id. Prefer referencing existing filters by id when available. Use ai_filters.create + ai_filters.test for fine-tuning before assigning.\n- template: the reply a rule_based agent sends, with no model run. Either a string (\"Hi {from_name}, we open at 9\") or a card object {text, attachments, buttons} — text is required, attachments is a list of file ids (files.upload), buttons is a list of {label, value|url|web_app} carrying exactly ONE target each: value = a reply the bot receives back, url = opens a link (add track: true to count each tap per conversation, readable as analytics_link_clicks_v in analytics.query), web_app = opens a Mini App over https. The string, and a card's text, substitute {from_name}, {message_text}, {channel_type}, {thread_id}, {raw_data}; button labels and targets are sent verbatim. A card with buttons needs send_mode auto — a draft row cannot carry a keyboard. Buttons need a channel that renders them (telegram_bot). A card needs a chat to land in, so it refuses on webhook, schedule and other thread-less triggers where a string still drafts.\n- handoff: {agent_id, task} — only on a rule_based agent's trigger (refused elsewhere). After the rule's deterministic part has actually happened — the template's message delivered, or the agent's script completed — the named agent runs on the same thread, in its own send mode, exactly as its own trigger would run it: the customer's message reaches it as the incoming message, and `task` reaches it as the handoff brief from this rule. The rule stays instant; the thinking — a reminder, a referral code, a personal follow-up — happens behind it. Script plus handoff is how a workspace does something deterministic the platform has no verb for (file the thread, stamp a tag, push a row) and still lets an agent answer, with the deterministic part living in that agent's own script rather than in platform code. `task` substitutes {channel_type} and {thread_id} only; {message_text}, {from_name} and {raw_data} are refused (the sender would be writing the instruction — the message and its sender already reach the target as the incoming message). The target must be an active agentic or claude_channels agent in this workspace (the engines that read the brief). The target's run is a normal trigger run: it holds the thread lock, its own safety gates apply, and its final answer is delivered like any DM reply — a follow-up that should stay silent must call agent.silence. Nothing runs when the message was drafted, refused or failed, when the script failed, when a safety gate has the rule in draft mode, or when the event carries no thread for the target to answer on. A target paused or deleted after the trigger was written does not stop the rule's message: it goes out, and the reason the follow-up did not start is recorded in the rule's activity row (error field) and in the target's activity as an execution_error.\n- contact_states: [\"active\"] — filter by contact state\n- cooldown_seconds: 30 — min gap between runs per thread\n- max_runs_per_thread_per_hour: 5 — rate limit\n- once_per_thread: true — after this trigger completes on a conversation it steps aside there and the next matching trigger answers later messages. For a rule that files / tags / hands off once, not for the agent that holds the conversation.\n- answer_delay_s: 15 — voice pickup only (incoming_call, or incoming_message scoped to a voice channel). Ring the human's own devices this long before the agent answers; if they pick up, the agent stands down. 0/absent = answer immediately. Honoured on WhatsApp and Telegram 1:1 — other voice channels have nothing ringing to wait for and still answer at once.\n- auto_join: true|false — voice pickup, GROUP calls only. Whether the bot enters a matching group call on its own (true) or the call becomes a pending invite an operator accepts (false). OMIT THE KEY to defer to the channel setting (channel_account.state.voice_auto_join_policy, default 'approval'); resolution is trigger-when-present, then channel, then approval, so an explicit false beats a channel set to auto. Absent and false are different answers.\n- chat_ids: [\"-5172634473\"] — voice pickup, Telegram GROUP calls only. Scopes the trigger to specific groups; omit for every group call. Any id spelling works (5172634473, -5172634473, -1005172634473 are the same group). A call whose chat is unknown never satisfies it, so do NOT set this together with 1:1 / Twilio / Telnyx / WhatsApp / Android / LiveChat voice channels or the \"voice\" wildcard: those calls have no chat and the trigger would stop answering them.\n\nSupported fields for job_completed (proactive callback when a delegated job finishes):\n- source_agent_id: <int> — fire only when this agent's job completed\n- source_agent_slug: <str> — alternate to source_agent_id\n- job_type: \"agentic_session\" — match a specific job type (default: any)\n- outcome: [\"completed\"] | [\"escalated\"] | [\"completed\",\"escalated\"] — default [\"completed\"]\n- min_duration_seconds: <int> — skip very-short jobs (noise filter)\n- thread_filter: {thread_ids: [<int>...]} — restrict to specific threads\n\nSupported fields for calendar_event (fires N minutes before a Google Calendar event starts):\n- window_minutes_before: <int 1-1440> — REQUIRED, fire when an event starts within this window\n- channel_account_ids: [<int>...] — restrict to specific calendar accounts (default: all)\n- keywords: [\"standup\"] — word-boundary match on event title\n- prepare_meet_join: true — pre-invite pool bots to the event (enables unattended Meet join)\n\nincoming_message action fields:\n- action: \"reply_text\" (default, normal agent run) or \"join_voice\" (deterministically join the voice channel resolved from the message — requires send_mode=auto)\n- message_source: \"real\" (default) | \"transcript\" | \"both\" — real messages vs turns during a live call. Transcripts are turns that address the agent.\n\ncalendar_event run-mode field (incoming_message uses `action` instead):\n- run_mode: \"text\" (default) or \"voice\" (join the meeting — requires send_mode=auto)\n- voice: {speak_first: <bool — greet immediately vs stay silent until addressed>, vision_mode: \"off\"|\"on_demand\"|\"continuous_0_3fps\"} — pairs with action=join_voice (incoming_message) or run_mode=voice (calendar_event)"
  7. Changed1 schema field changed
    • changedInput schema / properties / conditions / description
      Previous value: -"Trigger conditions (JSON). Supported fields for incoming_message:\n- keywords: [\"pricing\",\"demo\"] — message must contain keyword(s) (free, no LLM cost)\n- keyword_match: \"any\" (default, OR) or \"all\" (AND)\n- channel_types: [\"telegram\",\"whatsapp\",\"livechat_voice\",\"twilio_voice\",\"telegram_voice\",\"voice\",...] — filter by channel. For voice, use EITHER the three per-channel keys (scoped) OR \"voice\" alone (wildcard matching all three) — mixing them is redundant. Per-channel keys: \"livechat_voice\" (web widget), \"twilio_voice\" (PSTN inbound), \"telegram_voice\" (Telegram p2p calls)\n- context_types: [\"dm\",\"group\",\"channel\",\"livechat\"] — filter by chat type\n- group_mode: \"mentions_only\" or \"questions\" — for group chats\n- channel_account_ids: [\"123\"] — restrict to specific accounts\n- folder_ids: [5,10] — restrict to threads in folders\n- ai_tag_ids: [1,2] — restrict to threads with AI tags\n- ai_filter_ids: [1,2] — semantic intent filters (message matched via embedding similarity, works in noisy groups)\n- ai_filter_mode: \"any\" (default, OR) or \"all\" (AND) — how multiple AI filters combine\n- ai_filters: [{id: 1}, {name: \"...\", description: \"...\"}] — shorthand: reference existing by id or create inline (calls Voyage embedding API). If a filter with the same name already exists, it is reused by id. Prefer referencing existing filters by id when available. Use ai_filters.create + ai_filters.test for fine-tuning before assigning.\n- template: the reply a rule_based agent sends, with no model run. Either a string (\"Hi {from_name}, we open at 9\") or a card object {text, attachments, buttons} — text is required, attachments is a list of file ids (files.upload), buttons is a list of {label, value|url|web_app} carrying exactly ONE target each: value = a reply the bot receives back, url = opens a link, web_app = opens a Mini App over https. The string, and a card's text, substitute {from_name}, {message_text}, {channel_type}, {thread_id}, {raw_data}; button labels and targets are sent verbatim. A card with buttons needs send_mode auto — a draft row cannot carry a keyboard. Buttons need a channel that renders them (telegram_bot). A card needs a chat to land in, so it refuses on webhook, schedule and other thread-less triggers where a string still drafts.\n- handoff: {agent_id, task} — only on a rule_based agent's trigger (refused elsewhere). After the rule's deterministic part has actually happened — the template's message delivered, or the agent's script completed — the named agent runs on the same thread, in its own send mode, exactly as its own trigger would run it: the customer's message reaches it as the incoming message, and `task` reaches it as the handoff brief from this rule. The rule stays instant; the thinking — a reminder, a referral code, a personal follow-up — happens behind it. Script plus handoff is how a workspace does something deterministic the platform has no verb for (file the thread, stamp a tag, push a row) and still lets an agent answer, with the deterministic part living in that agent's own script rather than in platform code. `task` substitutes {channel_type} and {thread_id} only; {message_text}, {from_name} and {raw_data} are refused (the sender would be writing the instruction — the message and its sender already reach the target as the incoming message). The target must be an active agentic or claude_channels agent in this workspace (the engines that read the brief). The target's run is a normal trigger run: it holds the thread lock, its own safety gates apply, and its final answer is delivered like any DM reply — a follow-up that should stay silent must call agent.silence. Nothing runs when the message was drafted, refused or failed, when the script failed, when a safety gate has the rule in draft mode, or when the event carries no thread for the target to answer on. A target paused or deleted after the trigger was written does not stop the rule's message: it goes out, and the reason the follow-up did not start is recorded in the rule's activity row (error field) and in the target's activity as an execution_error.\n- contact_states: [\"active\"] — filter by contact state\n- cooldown_seconds: 30 — min gap between runs per thread\n- max_runs_per_thread_per_hour: 5 — rate limit\n- once_per_thread: true — after this trigger completes on a conversation it steps aside there and the next matching trigger answers later messages. For a rule that files / tags / hands off once, not for the agent that holds the conversation.\n- answer_delay_s: 15 — voice pickup only (incoming_call, or incoming_message scoped to a voice channel). Ring the human's own devices this long before the agent answers; if they pick up, the agent stands down. 0/absent = answer immediately. Honoured on WhatsApp and Telegram 1:1 — other voice channels have nothing ringing to wait for and still answer at once.\n- auto_join: true|false — voice pickup, GROUP calls only. Whether the bot enters a matching group call on its own (true) or the call becomes a pending invite an operator accepts (false). OMIT THE KEY to defer to the channel setting (channel_account.state.voice_auto_join_policy, default 'approval'); resolution is trigger-when-present, then channel, then approval, so an explicit false beats a channel set to auto. Absent and false are different answers.\n- chat_ids: [\"-5172634473\"] — voice pickup, Telegram GROUP calls only. Scopes the trigger to specific groups; omit for every group call. Any id spelling works (5172634473, -5172634473, -1005172634473 are the same group). A call whose chat is unknown never satisfies it, so do NOT set this together with 1:1 / Twilio / Telnyx / WhatsApp / Android / LiveChat voice channels or the \"voice\" wildcard: those calls have no chat and the trigger would stop answering them.\n\nSupported fields for job_completed (proactive callback when a delegated job finishes):\n- source_agent_id: <int> — fire only when this agent's job completed\n- source_agent_slug: <str> — alternate to source_agent_id\n- job_type: \"agentic_session\" — match a specific job type (default: any)\n- outcome: [\"completed\"] | [\"escalated\"] | [\"completed\",\"escalated\"] — default [\"completed\"]\n- min_duration_seconds: <int> — skip very-short jobs (noise filter)\n- thread_filter: {thread_ids: [<int>...]} — restrict to specific threads\n\nSupported fields for calendar_event (fires N minutes before a Google Calendar event starts):\n- window_minutes_before: <int 1-1440> — REQUIRED, fire when an event starts within this window\n- channel_account_ids: [<int>...] — restrict to specific calendar accounts (default: all)\n- keywords: [\"standup\"] — word-boundary match on event title\n- prepare_meet_join: true — pre-invite pool bots to the event (enables unattended Meet join)\n\nincoming_message action fields:\n- action: \"reply_text\" (default, normal agent run) or \"join_voice\" (deterministically join the voice channel resolved from the message — requires send_mode=auto)\n- message_source: \"real\" (default) | \"transcript\" | \"both\" — real messages vs turns during a live call. Transcripts are turns that address the agent.\n\ncalendar_event run-mode field (incoming_message uses `action` instead):\n- run_mode: \"text\" (default) or \"voice\" (join the meeting — requires send_mode=auto)\n- voice: {speak_first: <bool — greet immediately vs stay silent until addressed>, vision_mode: \"off\"|\"on_demand\"|\"continuous_0_3fps\"} — pairs with action=join_voice (incoming_message) or run_mode=voice (calendar_event)"New value: +"Trigger conditions (JSON). Supported fields for incoming_message:\n- keywords: [\"pricing\",\"demo\"] — message must contain keyword(s) (free, no LLM cost)\n- keyword_match: \"any\" (default, OR) or \"all\" (AND)\n- channel_types: [\"telegram\",\"whatsapp\",\"livechat_voice\",\"twilio_voice\",\"telegram_voice\",\"voice\",...] — filter by channel. For voice, use EITHER the three per-channel keys (scoped) OR \"voice\" alone (wildcard matching all three) — mixing them is redundant. Per-channel keys: \"livechat_voice\" (web widget), \"twilio_voice\" (PSTN inbound), \"telegram_voice\" (Telegram p2p calls)\n- context_types: [\"dm\",\"group\",\"channel\",\"livechat\"] — filter by chat type\n- group_mode: \"mentions_only\" or \"questions\" — for group chats\n- channel_account_ids: [\"123\"] — restrict to specific accounts\n- folder_ids: [5,10] — restrict to threads in folders\n- ai_tag_ids: [1,2] — restrict to threads with AI tags\n- ai_filter_ids: [1,2] — semantic intent filters (message matched via embedding similarity, works in noisy groups)\n- ai_filter_mode: \"any\" (default, OR) or \"all\" (AND) — how multiple AI filters combine\n- ai_filters: [{id: 1}, {name: \"...\", description: \"...\"}] — shorthand: reference existing by id or create inline (calls Voyage embedding API). If a filter with the same name already exists, it is reused by id. Prefer referencing existing filters by id when available. Use ai_filters.create + ai_filters.test for fine-tuning before assigning.\n- template: the reply a rule_based agent sends, with no model run. Either a string (\"Hi {from_name}, we open at 9\") or a card object {text, attachments, buttons} — text is required, attachments is a list of file ids (files.upload), buttons is a list of {label, value|url|web_app} carrying exactly ONE target each: value = a reply the bot receives back, url = opens a link (add track: true to count each tap per conversation, readable as analytics_link_clicks_v in analytics.query), web_app = opens a Mini App over https. The string, and a card's text, substitute {from_name}, {message_text}, {channel_type}, {thread_id}, {raw_data}; button labels and targets are sent verbatim. A card with buttons needs send_mode auto — a draft row cannot carry a keyboard. Buttons need a channel that renders them (telegram_bot). A card needs a chat to land in, so it refuses on webhook, schedule and other thread-less triggers where a string still drafts.\n- handoff: {agent_id, task} — only on a rule_based agent's trigger (refused elsewhere). After the rule's deterministic part has actually happened — the template's message delivered, or the agent's script completed — the named agent runs on the same thread, in its own send mode, exactly as its own trigger would run it: the customer's message reaches it as the incoming message, and `task` reaches it as the handoff brief from this rule. The rule stays instant; the thinking — a reminder, a referral code, a personal follow-up — happens behind it. Script plus handoff is how a workspace does something deterministic the platform has no verb for (file the thread, stamp a tag, push a row) and still lets an agent answer, with the deterministic part living in that agent's own script rather than in platform code. `task` substitutes {channel_type} and {thread_id} only; {message_text}, {from_name} and {raw_data} are refused (the sender would be writing the instruction — the message and its sender already reach the target as the incoming message). The target must be an active agentic or claude_channels agent in this workspace (the engines that read the brief). The target's run is a normal trigger run: it holds the thread lock, its own safety gates apply, and its final answer is delivered like any DM reply — a follow-up that should stay silent must call agent.silence. Nothing runs when the message was drafted, refused or failed, when the script failed, when a safety gate has the rule in draft mode, or when the event carries no thread for the target to answer on. A target paused or deleted after the trigger was written does not stop the rule's message: it goes out, and the reason the follow-up did not start is recorded in the rule's activity row (error field) and in the target's activity as an execution_error.\n- contact_states: [\"active\"] — filter by contact state\n- cooldown_seconds: 30 — min gap between runs per thread\n- max_runs_per_thread_per_hour: 5 — rate limit\n- once_per_thread: true — after this trigger completes on a conversation it steps aside there and the next matching trigger answers later messages. For a rule that files / tags / hands off once, not for the agent that holds the conversation.\n- answer_delay_s: 15 — voice pickup only (incoming_call, or incoming_message scoped to a voice channel). Ring the human's own devices this long before the agent answers; if they pick up, the agent stands down. 0/absent = answer immediately. Honoured on WhatsApp and Telegram 1:1 — other voice channels have nothing ringing to wait for and still answer at once.\n- auto_join: true|false — voice pickup, GROUP calls only. Whether the bot enters a matching group call on its own (true) or the call becomes a pending invite an operator accepts (false). OMIT THE KEY to defer to the channel setting (channel_account.state.voice_auto_join_policy, default 'approval'); resolution is trigger-when-present, then channel, then approval, so an explicit false beats a channel set to auto. Absent and false are different answers.\n- chat_ids: [\"-5172634473\"] — voice pickup, Telegram GROUP calls only. Scopes the trigger to specific groups; omit for every group call. Any id spelling works (5172634473, -5172634473, -1005172634473 are the same group). A call whose chat is unknown never satisfies it, so do NOT set this together with 1:1 / Twilio / Telnyx / WhatsApp / Android / LiveChat voice channels or the \"voice\" wildcard: those calls have no chat and the trigger would stop answering them.\n\nSupported fields for job_completed (proactive callback when a delegated job finishes):\n- source_agent_id: <int> — fire only when this agent's job completed\n- source_agent_slug: <str> — alternate to source_agent_id\n- job_type: \"agentic_session\" — match a specific job type (default: any)\n- outcome: [\"completed\"] | [\"escalated\"] | [\"completed\",\"escalated\"] — default [\"completed\"]\n- min_duration_seconds: <int> — skip very-short jobs (noise filter)\n- thread_filter: {thread_ids: [<int>...]} — restrict to specific threads\n\nSupported fields for calendar_event (fires N minutes before a Google Calendar event starts):\n- window_minutes_before: <int 1-1440> — REQUIRED, fire when an event starts within this window\n- channel_account_ids: [<int>...] — restrict to specific calendar accounts (default: all)\n- keywords: [\"standup\"] — word-boundary match on event title\n- prepare_meet_join: true — pre-invite pool bots to the event (enables unattended Meet join)\n\nincoming_message action fields:\n- action: \"reply_text\" (default, normal agent run) or \"join_voice\" (deterministically join the voice channel resolved from the message — requires send_mode=auto)\n- message_source: \"real\" (default) | \"transcript\" | \"both\" — real messages vs turns during a live call. Transcripts are turns that address the agent.\n\ncalendar_event run-mode field (incoming_message uses `action` instead):\n- run_mode: \"text\" (default) or \"voice\" (join the meeting — requires send_mode=auto)\n- voice: {speak_first: <bool — greet immediately vs stay silent until addressed>, vision_mode: \"off\"|\"on_demand\"|\"continuous_0_3fps\"} — pairs with action=join_voice (incoming_message) or run_mode=voice (calendar_event)"
  8. Changed1 schema field changed
    • addedInput schema / properties / in_workspace
      Added value: +{
      +  "description": "Run this one call in this workspace id instead of the session's. Nothing is stored; other sessions are not affected.",
      +  "type": "integer"
      +}
  9. First observed

TDQS

Score is being calculated.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.