Automations let you trigger actions automatically when events happen in Craft Agent. You can run shell commands, send prompts to start new sessions, or schedule recurring workflows — all configured in a single JSON file.Documentation Index
Fetch the complete documentation index at: https://agents.craft.do/docs/llms.txt
Use this file to discover all available pages before exploring further.
Getting Started
The easiest way to set up automations is to describe what you want in plain language. Here are some prompts you can try:| What you want | What to say |
|---|---|
| Scheduled briefing | ”Set up a daily standup briefing every weekday at 9am” |
| Desktop notifications | ”Notify me with a macOS notification when a session is labelled urgent” |
| Audit logging | ”Log all permission mode changes to a file” |
| Auto-label | ”When a session starts, run a command that checks the working directory and logs it” |
| Recurring report | ”Every Friday at 5pm, create a session that summarises this week’s completed tasks” |
| Webhook integration | ”When I flag a session, send a curl request to my webhook URL” |
Your First Automation
Here’s the simplest possible automation — it logs a message every time a label is added to a session:~/.craft-agent/workspaces/{workspaceId}/automations.json and it starts working immediately — no restart needed.
Scheduling a Prompt
Want Craft Agent to do something for you on a schedule? Use a prompt action with a cron expression:How Automations Work
When an event fires (e.g., a label is added, a tool runs, or a cron schedule matches), Craft Agent checks your configuration for matching entries and executes them. There are two types of actions:- Command actions — execute shell commands with event data available as environment variables
- Prompt actions — send a prompt to Craft Agent, creating a new session (App events only)
Configuration File
Automations are configured per-workspace inautomations.json:
Basic Structure
Managing Automations
In the UI
Automations are listed in the sidebar under Automations. From here you can:- Enable / Disable — Toggle individual automations on or off without removing them
- Duplicate — Create a copy of an existing automation with a “Copy” suffix
- Delete — Remove an automation permanently
- Test — Manually trigger an automation to verify it works before waiting for the real event. The test runner resolves
@mentions, enables sources, and uses the configuredllmConnection/model— the same code path as the scheduler. - Execution history — Each automation records success and failure to a timeline, viewable in the detail page. History is stored in
automations-history.jsonland retained to the last 20 runs per automation (max 1000 entries total).
In the Config File
You can also manage automations by editingautomations.json directly. Changes take effect immediately — no restart required.
Enabling and disabling: Set "enabled": false on any matcher to temporarily disable it without removing the configuration. Omit the field or set it to true to re-enable.
automations.json. If an event type has no remaining matchers, you can remove the entire event key.
Events
App Events
Triggered by Craft Agent itself. Support both command and prompt actions.| Event | Trigger | Match Value |
|---|---|---|
LabelAdd | Label added to session | Label ID (e.g., urgent) |
LabelRemove | Label removed from session | Label ID |
LabelConfigChange | Label configuration changed | Always matches |
PermissionModeChange | Permission mode changed | New mode name |
FlagChange | Session flagged/unflagged | true or false |
SessionStatusChange | Session status changed | New status (e.g., done) |
SchedulerTick | Runs every minute | Uses cron matching |
Renamed:
TodoStateChange was renamed to SessionStatusChange. The old name still works as a deprecated alias but will show a validation warning. Update your automations to use SessionStatusChange.Agent Events
Passed to the Claude SDK. Only command actions are supported (no prompt actions).| Event | Trigger | Match Value |
|---|---|---|
PreToolUse | Before a tool executes | Tool name |
PostToolUse | After a tool succeeds | Tool name |
PostToolUseFailure | After a tool fails | Tool name |
UserPromptSubmit | User submits a prompt | Event data JSON |
SessionStart | Session starts | Event data JSON |
SessionEnd | Session ends | Event data JSON |
Stop | Agent stops | Event data JSON |
SubagentStart | Subagent spawned | Event data JSON |
SubagentStop | Subagent completes | Event data JSON |
Notification | Notification received | Event data JSON |
PreCompact | Before context compaction | Event data JSON |
PermissionRequest | Permission requested | Event data JSON |
Setup | Agent setup/initialization | Event data JSON |
Action Types
Command Actions
Execute a shell command when the event fires. Event data is available through environment variables.| Property | Type | Default | Description |
|---|---|---|---|
type | "command" | Required | Action type |
command | string | Required | Shell command to execute |
timeout | number | 60000 | Timeout in milliseconds |
Prompt Actions
Send a prompt to Craft Agent, creating a new session. Only works with App events.| Property | Type | Default | Description |
|---|---|---|---|
type | "prompt" | Required | Action type |
prompt | string | Required | Prompt text to send |
llmConnection | string | Workspace default | LLM connection slug (configured in AI Settings) |
model | string | Workspace default | Model ID for the created session |
thinkingLevel | "off" | "low" | "medium" | "high" | "xhigh" | "max" | Workspace default | Thinking level for the spawned session |
- Use
@mentionsto reference sources or skills (e.g.,@github,@linear) - Environment variables are expanded (e.g.,
$CRAFT_LABEL,${CRAFT_SESSION_NAME}) - Mentioned sources are automatically activated for the new session
llmConnection value is the slug of an LLM connection configured in AI Settings. The model value is a model ID supported by the provider. If either is invalid or not found, it gracefully falls back to the workspace default.
Thinking level resolution: thinkingLevel on the action takes precedence; if omitted, the workspace default applies; if neither is set, sessions default to "medium". The legacy "think" value from older configs is silently migrated to "medium". Backends gracefully cap unsupported values (e.g. Anthropic auto-degrades xhigh → high on models that don’t support it; Pi caps max at xhigh).
Matchers
Regex Matching
Most events use regex matching against the event’s match value:matcher field to match all events of that type.
Examples:
| Pattern | Matches |
|---|---|
^urgent$ | Exact match for “urgent” |
bug|feature | Either “bug” or “feature” |
^prod- | Any value starting with “prod-” |
| (omitted) | All events of that type |
Cron Matching
ForSchedulerTick events, use cron expressions instead of regex:
minute hour day-of-month month day-of-week
| Field | Range | Examples |
|---|---|---|
| Minute | 0–59 | 0, */15 |
| Hour | 0–23 | 9, 14 |
| Day of month | 1–31 | 1, 15 |
| Month | 1–12 | 1, */3 |
| Day of week | 0–6 (0 = Sunday) | 1-5, 0,6 |
| Cron | Schedule |
|---|---|
*/15 * * * * | Every 15 minutes |
0 9 * * * | Daily at 9:00 AM |
0 9 * * 1-5 | Weekdays at 9:00 AM |
30 14 1 * * | 1st of each month at 2:30 PM |
0 */6 * * * | Every 6 hours |
Europe/Budapest, America/New_York). Defaults to system timezone if not specified. Verify with crontab.guru.
Conditions
Conditions are optional filters that run after the matcher/cron matches but before actions fire. All conditions in the array must pass (implicit AND). If the array is empty or omitted, actions fire unconditionally.Time Conditions
Check time-of-day and day-of-week in a given timezone.| Property | Type | Description |
|---|---|---|
after | "HH:MM" | Start of time window (inclusive) |
before | "HH:MM" | End of time window (exclusive) |
weekday | string[] | Allowed days: mon, tue, wed, thu, fri, sat, sun |
timezone | string | IANA timezone. Falls back to matcher timezone, then system local |
Overnight ranges: If
after is later than before (e.g., "after": "22:00", "before": "06:00"), the range wraps across midnight.State Conditions
Check fields from the event payload. Useful for filtering on specific transitions or values.| Property | Type | Description |
|---|---|---|
field | string | Payload field name (e.g., permissionMode, sessionStatus, labels, isFlagged) |
value | any | Exact match |
from | any | Previous value (for transition events) |
to | any | New value (for transition events) |
contains | string | Array membership check (e.g., check if a label is present) |
not_value | any | Matches anything except this value |
Transition fields: For
permissionMode and sessionStatus, from/to automatically resolve to the correct payload keys (oldMode/newMode, oldState/newState).Logical Composition
Combine conditions withand, or, and not:
| Type | Behaviour |
|---|---|
and | All sub-conditions must pass |
or | At least one sub-condition must pass |
not | None of the sub-conditions may pass |
Matcher Options
Each matcher entry supports these optional fields:| Property | Type | Default | Description |
|---|---|---|---|
matcher | string | Match all | Regex pattern for event filtering |
cron | string | — | Cron expression (SchedulerTick only) |
timezone | string | System TZ | IANA timezone for cron |
conditions | array | [] | Conditions that must all pass before actions fire (see Conditions) |
permissionMode | string | "safe" | Security mode for commands |
labels | string[] | [] | Labels applied to prompt-created sessions |
telegramTopic | string | — | Routes spawned sessions to a Telegram forum topic — see Telegram Topic Routing |
enabled | boolean | true | Set to false to disable without deleting |
actions | array | Required | Array of command/prompt actions |
Telegram Topic Routing
When you have a Telegram supergroup paired in Settings → Messaging → Telegram, you can route the sessions a matcher spawns into a dedicated forum topic by setting thetelegramTopic field.
telegramTopic value) post into the same topic.
Activation requirements
All of these must hold; otherwise the field is silently ignored and the session runs without a Telegram binding (same as if the field were absent):- A Telegram supergroup is paired at Settings → Messaging → Telegram
- The Telegram bot is connected
- The bot has the Manage Topics admin permission in the supergroup
Setting up the Telegram supergroup
If you haven’t paired a supergroup yet, follow these steps. The whole flow takes about a minute.1. Create a supergroup with topics enabled
A regular Telegram group can’t host topics — you need a supergroup with “Topics” mode enabled.- New group: Telegram → New Group → add anyone (or yourself for a solo workspace) → set a name. Telegram converts groups to supergroups automatically once members exceed a threshold or you flip Topics on.
- Enable Topics: Open the group → tap the group name → Edit (pencil icon on mobile, ⋯ on desktop) → toggle Topics on → Save. The group is now a forum supergroup; you’ll see a “General” topic and a ”+” button to create more.
2. Add the bot to the supergroup
Open the supergroup → tap the group name → Add members → search for your bot’s username (e.g.@CraftAgentsBot) → add it.
3. Make the bot an admin with “Manage Topics”
This is the step most people miss — the bot needs explicit permission to create new topics, otherwise topic-creation calls return400: Bad Request: not enough rights to create a topic.
- In the supergroup, tap the group name → Edit → Administrators.
- Tap Add Administrator → pick the bot.
- In the permissions list, toggle on Manage Topics (other permissions like Delete Messages or Pin Messages are optional and not required by the topic-routing feature).
- Tap Save / Done.
4. Pair the supergroup with the workspace
- In the Craft Agent app: Settings → Messaging → Telegram → Pair Supergroup.
- The dialog shows a 6-digit code, a deep link to the bot, and a 5-minute countdown.
- In Telegram, in any topic of your supergroup, type
/pair <code>(replace<code>with the digits from the dialog). - The bot replies with a confirmation. The dialog closes automatically and the Settings row updates with the supergroup’s title and chat ID.
telegramTopic set will now create topics in
this supergroup.
Notes
- Topic names are 1–128 characters and case-sensitive (
"Reports"and"reports"are different topics). - Two matchers using the same
telegramTopicvalue share one topic — useful for grouping related automations. - If you change a matcher’s
telegramTopicvalue, the next run uses (or creates) the new topic; the old topic remains in Telegram with its history intact. - Renaming the topic in Telegram doesn’t sync back — the binding follows the topic ID, not the display name.
Environment Variables
Common Variables
Available to all command actions:| Variable | Description |
|---|---|
CRAFT_EVENT | Event name (e.g., LabelAdd, PreToolUse) |
CRAFT_EVENT_DATA | Full event payload as JSON |
CRAFT_SESSION_ID | Current session ID |
CRAFT_SESSION_NAME | Current session name |
CRAFT_WORKSPACE_ID | Current workspace ID |
App Event Variables
Dynamically generated from event payloads: Label events (LabelAdd, LabelRemove):
CRAFT_LABEL— Label ID being added/removed
CRAFT_OLD_MODE— Previous permission modeCRAFT_NEW_MODE— New permission mode
CRAFT_IS_FLAGGED—trueorfalse
CRAFT_OLD_STATE— Previous session statusCRAFT_NEW_STATE— New session status
CRAFT_LOCAL_TIME— Current time (HH:MM)CRAFT_LOCAL_DATE— Current date (YYYY-MM-DD)
Agent Event Variables
Tool events (PreToolUse, PostToolUse, PostToolUseFailure):
CRAFT_TOOL_NAME— Tool being usedCRAFT_TOOL_INPUT— Tool parameters as JSONCRAFT_TOOL_RESPONSE— Tool output (PostToolUse only)CRAFT_ERROR— Error message (PostToolUseFailure only)
SessionStart):
CRAFT_SOURCE— Session source (e.g.,startup,resume)CRAFT_MODEL— Model name
SubagentStart, SubagentStop):
CRAFT_AGENT_ID— Subagent IDCRAFT_AGENT_TYPE— Subagent type
Permission Modes
Command actions run with security checks by default. You can adjust this per matcher:| Mode | Behaviour | Use Case |
|---|---|---|
safe | Commands checked against allowlist | Default, recommended |
allow-all | Bypass security checks | Trusted automation only |
Labels for Prompt Actions
Prompt actions can attach labels to the sessions they create. This makes it easy to filter and organise scheduled sessions:"priority::${CRAFT_LABEL}".
Rate Limits
To prevent runaway loops (e.g., an automation that indirectly triggers itself), the event bus enforces rate limits:| Event | Max fires / minute |
|---|---|
SchedulerTick | 60 (1/sec) |
| All other events | 10 |
Examples
Daily Morning Briefing
Schedule a prompt every weekday at 9 AM:Weekday-Only AI News (with Conditions)
Use atime condition to restrict a daily schedule to weekdays:
Permission Mode Gate (with Conditions)
Only notify when permission mode changes specifically fromsafe to allow-all:
Log Label Changes
Track when labels are added or removed:macOS Notification on Urgent Label
Audit Permission Mode Changes
Multiple Schedules with Enable/Disable
Run different automations at different times, and disable some without deleting:"enabled" is set to true or removed.
Prompt with Custom Connection, Model, and Thinking Level
Override provider, model, and thinking level for a specific automation:Validation
Ask Craft Agent to validate your automations:config_validate tool with target: "all".
The validator checks for:
- Invalid JSON syntax
- Unknown event names
- Empty actions arrays
- Invalid cron expressions or timezones
- Invalid or unsafe regex patterns (ReDoS prevention)
- References to non-existent labels
- Invalid condition types, field names, or weekday values
- Condition nesting depth exceeding limits
- Missing
llmConnectionslugs (error — will fail at runtime) - Model/provider mismatches when both
llmConnectionandmodelare specified (warning) - Invalid
thinkingLevelvalues (legacy"think"is silently migrated to"medium")
Security
Automations include several built-in safety measures:- Shell injection prevention — User-controlled values in environment variables (session names, labels, prompts) are automatically escaped
- ReDoS protection — Regex patterns are limited to 500 characters and checked for catastrophic backtracking patterns
- Rate limiting — Prevents infinite loops from automations that trigger other automations
- Permission modes — Commands are checked against an allowlist by default
- Timeouts — Commands are killed after their timeout expires (with SIGKILL fallback)
Best Practices
Best Practices
- Start simple — Test with
echocommands before writing complex scripts - Use labels — Tag scheduled sessions for easy filtering
- Set timeouts — Prevent runaway commands with the
timeoutfield - Log failures — Redirect stderr to track issues:
command 2>> ~/automation-errors.log - Be specific — Use matchers to avoid triggering on every event
- Test cron — Use crontab.guru to verify expressions
- Use
enabled: false— Temporarily disable automations instead of deleting them during debugging
Troubleshooting: Automation not firing
Troubleshooting: Automation not firing
- Check event name — Must be exact (e.g.,
LabelAddnotlabeladd) - Check matcher — Regex must match the event’s match value
- Check cron — For SchedulerTick, verify your cron expression
- Check enabled — Ensure the matcher doesn’t have
"enabled": false - Check logs — Look for
[automations]in the application logs
Troubleshooting: Command blocked
Troubleshooting: Command blocked
If you see “Bash command blocked” errors:
- Add
"permissionMode": "allow-all"to the matcher - Or simplify the command to avoid shell constructs like
$()
Troubleshooting: Prompt not creating session
Troubleshooting: Prompt not creating session
- Ensure the event is an App event (prompt actions don’t work with Agent events)
- Check that the prompt text is not empty
- Verify
@mentionsreference valid sources or skills
Troubleshooting: Condition not matching
Troubleshooting: Condition not matching
- Check weekday spelling — Must be 3-letter lowercase:
mon,tue,wed,thu,fri,sat,sun - Check timezone — Use IANA names (e.g.,
Europe/Budapest). Invalid timezones silently fall back to system local - Check time format — Must be
HH:MMin 24-hour format (e.g.,09:00, not9:00 AM) - Check state field names — Use
permissionMode,sessionStatus,labels,isFlagged - Check nesting depth — Conditions nested deeper than 8 levels always evaluate to false
- Check logs — Look for
[automations]entries that mention condition evaluation