Browse the docs

Triggers

Every way a macro can start, how conflicts and scopes decide which macro runs, and how to find out why a trigger did not fire.

Keyboard Maestro: Triggers, Hot Key Trigger, Typed String Trigger, Time of Day Trigger, Application Trigger, Periodic Trigger

A trigger is the When row of a macro. It names an event: a key combination, a time, an app launching, a file appearing. When the event happens, Switchboard looks for macros that listen for it and starts them.

A macro can have several triggers. Any one of them starts it. Add one with Add a trigger… under When.

Keyboard #

TriggerStarts whenKey settings
HotkeyYou press a key combination.keys, modifiers, repeat
Typed textYou type a string of characters.string, caseSensitive, simulateDeletes

A hotkey that Switchboard owns is swallowed: the app in front never sees it. A modifier-only hotkey (modifiers with no key) starts when you release them, as long as no other key was pressed in between. Hold the key and the macro starts once, unless repeat is on.

A typed trigger watches what you type in any app. It matches the end of a rolling buffer, so ;sig starts as soon as you type the g. Typing a return, tab, escape or arrow key, or any shortcut that holds ⌘ or ⌃, clears the buffer. Backspace removes the last character from it. Turn on simulateDeletes to have Switchboard delete the typed characters before the macro runs. The ;shrug example is a snippet: a typed trigger with simulateDeletes and one Type text step. See Snippets.

Type a date after typing ;date
When
  1. Type “;date”
Do
  1. Type “{{ date('yyyy-MM-dd') }}”
Open in Switchboard to try it

This macro types text into the front app when you run it for real. Typed and hotkey triggers need Input Monitoring.

Time #

TriggerStarts whenKey settings
Time of dayA clock time arrives, or a cron schedule matches.at (HH:MM), days, cron, catchUp
Repeat everyEvery so many seconds.every, catchUp
IdleYou have not touched the keyboard or mouse for so many seconds.after

days is a list of mon to sun. With no days, a time trigger runs every day. cron takes five fields (minute, hour, day of month, month, day of week) and supports lists, ranges, steps and month and day names.

If the Mac was asleep at the scheduled time, the occurrence is skipped, unless the trigger has catchUp on. Then it runs once when the Mac wakes, not once per missed time. An idle trigger runs once when you cross the threshold and again only after you have been active.

Weekday stand-up reminder
When
  1. Every weekday at 09:25
Do
  1. Show a notification “Stand-up in 5 minutes”
Open in Switchboard to try it

Apps and the system #

TriggerStarts whenKey settings
Application eventAn app launches, quits, becomes active or stops being active.bundleId, event
Window title changeThe title of the front window changes.app, pattern, mode
App notificationAny app posts a notification. See Any app's notification.app, title, body, mode
Clipboard changeThe clipboard changes.none
An error dialog appearsAny app shows an alert, sheet or system dialog.app, text, button
A form field takes focusAn email, name, address or similar text field takes focus.kind, app
Before a key, click or quitJust before a key, a click or Quit reaches an app: a check macro lets it through, blocks it or asks. See Guardrails: check before it happens.app, on, keys, deadline
An MCP resource changesA resource on one of your MCP servers changes. See Trigger on anything an MCP server knows.server, uri, filter
Mac wakes / Mac sleepsThe Mac wakes or goes to sleep.none
Screen locks / Screen unlocksThe screen locks or unlocks.none
Switchboard startsThe engine starts, once.none
File or folder changeA file or folder is added, changed, removed, renamed, moved or tagged. Patterns, depth, settling, batching and catch-up are options.path, events
Dropped on the drop panelFiles, pictures, folders, text or links are dropped on the macro's tile in the drop panel, the column of targets that slides out when you drag to the screen edge.accepts, extensions
Device attach or detachA USB device is plugged in or removed.transport, id, event
Network changeYou join a Wi-Fi network, or the network becomes reachable or unreachable.ssid, reachable

A file trigger watches a folder, or one file, and by default everything inside it. ~ works in the path. It waits until a new or changed file has stopped changing before it fires, so a big copy or a download runs the macro once, when it is whole. depth, match and exclude narrow what counts, and the editor lists the last files that matched and the last that were filtered out, with the reason. For a device, id can be the product name, the vendor and product numbers as vid:pid in hex, or the serial number. Reading the Wi-Fi name needs Location access in macOS; without it, a trigger that names a network cannot match.

The window title trigger takes a pattern. mode is equals, contains or regex (the default).

Say hello when Slack opens
When
  1. Slack launches
Do
  1. Show “Slack is open”
Open in Switchboard to try it

Any app's notification #

Most apps expose nothing for a macro to hook into, but nearly all of them post notifications, and a notification carries the app, a title and a body. A App notification trigger matches on those, so "when Slack says mentioned you" or "when Xcode says Succeeded" needs no setup in either app. The title, body, app and (with a regex) the pattern's groups reach the macro as {{title}}, {{body}}, {{appName}} and {{match[1]}}, and {{triggerValue}} is the body.

Switchboard Engine reads the Notification Center database for this, which needs Full Disk Access. Settings › Permissions shows the row once a notification trigger exists. Without the permission it reads the banners on screen instead, and misses notifications a Focus silenced. Read recent notifications reads what arrived earlier, for "what did I miss".

From outside #

TriggerStarts whenKey settings
URLA switchboard://run/<route> link is opened.route
WebhookSomething sends a GET or POST to /hooks/<route>: from this Mac, your network or the internet. It can wait and answer.route, methods, auth

Both pass information in as inputs.

  • URL. switchboard://run/<route>?key=<key>&name=value. The key is a secret generated for your Mac and kept in the engine's settings. A link with the wrong key is refused. Every other query item becomes an input of the macro. A URL whose route matches no trigger also runs a macro whose id or name is that text.

  • Webhook. A GET or POST to /hooks/<route>. On this Mac it uses 127.0.0.1:48080 with the engine's bearer token, or a per-route key (?key=) for senders that cannot set headers, such as a bookmark, an iPhone Shortcut or a Stream Deck button. Set the trigger's reach to "This network" to let your other devices call it, or sign in and choose the internet to get a public address. The macro can answer the request with a Reply to webhook step. A request to an internet address that arrives while your Mac is asleep or offline isn't lost: the server keeps it for up to three days and your Mac runs it, in order, when it reconnects (see Webhook, "While this Mac is away"). The query items, the fields of a JSON or form body and a webhook object with everything about the request become inputs.

Show a message sent by a webhook
When
  1. A webhook is called at /message
Do
  1. Show “{{ message }}”
Open in Switchboard to try it

What the trigger hands to the macro #

A run keeps the event that started it. Use {{ triggerValue }} in any step to read the most useful part of it: the file path for a file trigger, the string for a typed trigger, the title for a window title trigger, the key for a hotkey, or the route for a URL or webhook. It is empty when the trigger carries nothing. See Variables and expressions.

Scope: which macro runs where #

Scope belongs to the Group, not the macro. A Group is either everywhere or limited to some apps, or to every app except some.

  • A macro runs only if its Group is on and in scope for the front app.

  • A Group scoped to the front app beats an "everywhere" Group. If both have the same hotkey, the app's Group wins and nothing is in conflict.

  • A Group scoped to other apps is not considered at all.

Turn a Group off and every macro in it stops listening. Imported Keyboard Maestro groups carry their scopes across.

Make a group for one app #

To make macros that work only in Safari, say, make a Group for it:

  1. Choose New Group in the sidebar (or File > New Group). In the sheet, next to the name, open Runs in and tick Safari. The apps that are open now are listed first; Other App… picks any app in Applications.

  2. Create it. The group shows a small Safari badge in the sidebar, and its macros work only while Safari is in front.

  3. Put macros in it with New Macro while the group is selected, or drag existing ones onto it.

Select any group and the Runs in menu is at the top of its macro list. Change it there at any time: tick more apps, or choose Every app to lift the limit. More Options… opens the Group inspector for "every app except…" and for a State.

Conflicts #

If two macros that are both on, in scope and in the same kind of Group listen for the same hotkey or typed text, neither runs blind. A small panel lists them. Use ↑ ↓ and ⏎ to pick one to run, or ⎋ to run neither. The event is also saved under Not Fired with the reason conflict.

To end the conflict, change one hotkey, turn one macro off, or move one macro to a Group scoped to an app.

Layers #

A layer lets one hotkey open a set of single-key shortcuts. Give a Group a Layer in its inspector, pick the key that opens it (Hyper, Fn or any combination), and the Group's macros can use bare keys: Hyper+S, then J. While the layer is live, every key you press belongs to it. A HUD lists the keys so you don't have to remember them, and fades when you press one.

ModeThe layer is liveLike
Next key (default)For one key, or 3 seconds, then it ends. Pressing the layer key again restarts the wait.A Vim leader
While heldWhile you hold the layer key. Letting go ends it.A modifier
Until EscUntil you press ⎋ or the layer key again.A Vim mode

How a live layer behaves:

  • It is the only scope. Macros in the layer's Group answer to their keys; every other hotkey macro waits, and the event is saved under Not Fired with the reason layerActive.

  • A key the layer has no macro for is swallowed. The HUD says "Nothing on J" for a moment, the event is saved with the reason layerEmpty, and a next key layer ends.

  • ⎋ always ends a layer and is swallowed.

  • The layer opens only where its Group applies. Scope a layer to Slack and Hyper+S is Slack's alone; in Xcode the key is Xcode's.

  • Typed-text triggers are off while a layer is live.

  • Secure input holds layers like any hotkey: a layer will not open, and one that is live ends. If the keyboard tap is ever disabled by the system, a live layer ends too, so a layer can't be left swallowing your typing.

  • Macros in a layer Group answer only inside the layer. Their keys may be bare (J, ⇧J, Space) or carry modifiers. Two layers may both use J.

  • A macro can open a layer with the Open a layer step: from a palette row, a gesture, or another layer's key. Layers nest once.

A bare typing key on a macro that is not in a layer is an error in the library check ("J alone would swallow every J you type"), and the editor refuses to turn the macro on. Function keys, arrows and other named keys are fine on their own.

Layers have a menu bar glyph that shows the layer's initial while one is live, with Leave the layer in its menu.

Secure input #

macOS turns on secure input while a password field is active, and some apps leave it on. While it is on, apps cannot read the keyboard, so hotkey and typed triggers wait and do not run. Other triggers are not affected.

  • The menu bar icon changes to a key, and the menu shows "Secure input is on" with the likely cause.

  • macOS credits the front app, not the true holder, so the name is a best guess: "usually" the app in front, or "a background app".

  • The held events are saved under Not Fired with the reason secureInput.

Secure input ends when the field loses focus. If it seems stuck, quit the app named in the menu.

When two runs overlap #

What happens when a macro is triggered while it is already running is set per macro, under run.concurrent:

ValueBehavior
queue (default)The new run waits until the current one finishes.
cancelPreviousThe current run is cancelled and the new one starts.
parallelBoth run at once, up to 8 copies of the macro.
ignoreThe new run is dropped, and the trace says why.

No more than 64 runs are live at once across all macros. Past that, new runs queue. A run that takes longer than the macro's run.timeout (60 seconds by default) is stopped.

Limits: how often, and when #

Any trigger can carry Limits, a section at the bottom of its settings: a cooldown, "the first time each day", "every 3rd time", "twice within 2 seconds", a chance, "only while I am at the Mac", "only on this Mac", "only in a State". They apply to every kind of trigger in the same way, after the trigger matched and the macro's Only if passed. See Limits on any trigger.

When the Mac can't run it yet #

A run that starts by itself (a file arriving, a schedule, a webhook, an agent) can find the Mac unable to do what the macro needs: the screen is locked or asleep, secure input is on, an app the macro clicks in isn't running, or an extension it uses isn't installed. Instead of failing at the first click, Switchboard holds the run with its trigger, shows "1 held" beside the menu bar icon, and starts it as soon as the blocker clears.

  • Switchboard reads what each macro needs from its steps. A macro that only runs a shell script or an http request is never held behind a lock; one that clicks, types or presses keys needs the screen. A macro that starts with Activate an app doesn't need that app running.

  • A run you cause with your hands (a hotkey, typed text, a gesture, the palette, a button, a URL you click, Run) is never held. It runs now.

  • The menu bar lists each held run ("File it · Mac locked · 2:14 am") with Run Now (ignores the blocker; the run may fail) and Drop. Activity shows them under Waiting. When one starts, its trace begins with "Held 3 h 12 m: Mac locked", and the macro's Only if is checked then, not when it was held.

  • While Switchboard is paused from the menu bar, runs that start by themselves are held too, and start when you resume.

  • An agent calling run_macro gets held and the reason back, and can wait or move on.

Choose what happens per macro in the editor's inspector, under When it can't run: hold for 1 hour, 6 hours, 24 hours (the default) or 7 days, then drop it with a Not Fired heldExpired; Skip (a Not Fired blocked); or Run anyway, for a macro Switchboard wrongly thinks needs the screen. In the macro file these are run.whenBlocked (hold, skip, run) and run.holdFor (seconds). A macro keeps at most 200 held runs; past that the oldest is dropped (heldOverflow). Settings > General > Say when held runs go controls the "Ran 3 held macros" message. From a terminal: switchboard held.

When a trigger does not fire #

Open Activity (⌘2) and pick Not Fired in the Show menu. Each row is an event that matched a trigger, with one reason for each macro that listened:

ReasonMeaning
groupOffThe macro's Group is turned off.
scopeThe Group is scoped to other apps.
conflictAnother macro has the same hotkey or typed text.
secureInputSecure input was on.
conditionFalseAn Only if condition was false. The row names which one and the values it saw.
pausedSwitchboard was paused from the menu bar. (A run that starts by itself is held instead, and starts when you resume.)
blockedThe Mac couldn't do it now and the macro says to skip. The row names what was in the way.
heldOverflowThe macro already had 200 held runs, so the oldest was dropped.
heldExpiredA held run waited as long as its macro allows and was dropped. The row names what it was still waiting on.
disabledThe macro is turned off.
lateA webhook request the server held while the Mac was away waited longer than the trigger's maxDelay.
duplicateA held webhook request had already been handled before the connection dropped, so it didn't run again.
layerActiveA layer was live, so this macro's hotkey was held back. The row names the layer.
layerEmptyA layer was live and had nothing on that key.
cooldown, limitReached, notNth, burstShort, chanceThe trigger's Limits held it back: still cooling down, already ran as often as allowed this period, not the nth time yet, waiting for the rest of a burst, or the coin toss said no. The row says how far along it is.
unattended, attended, otherMac, notInStateThe trigger's Limits are for while you are at (or away from) the Mac, for another Mac, or for a State that was off.
licenceThe license doesn't allow running: the trial ended or the license lapsed. You can buy once, subscribe, or take Founder; a build released after a Buy once licence's updates date asks to renew. Editing, import and export still work.

If an event matched nothing at all, there is no row. In that case check that the trigger's settings are right and that the engine has the permission it needs. See Troubleshooting permissions.

Next steps #