Voice commands
A command is something you say to Snag that isn't for an agent: a phrase, an action, and a reply. "log 42 miles" runs an n8n workflow and Snag says the workflow's answer back.
Voice commands live under Control > Voice commands: "Things you can say to Snag that aren't for an agent." They are part of Snag Control, which is Pro (see Plans).
"Say it hands-free or in the car. The in-app mic saves a capture instead." The capture card's mic always saves a capture; a command is heard in Siri's "Snag this", Go hands-free, AirPods and CarPlay.
The page has four sections: Packs, Yours, Repos (once a repo is linked) and Recent runs (once a command has run).
How a command is matched
Commands are checked before agent names and captures, in any hands-free capture and on the car screen.
- The phrase must match everything said, words in order.
- A slot in braces, such as
{miles}, takes one or more words: "log {miles} miles" matches "log 42 miles". - A leading "snag" and a trailing "please" are ignored.
- When several match, the one with the most fixed words wins.
- Your own commands and repo commands come before packs. A pack command with the same id as one of yours is dropped.
Packs
Built-in sets of commands, each with a switch. Packs are off by default, except Agents, which turns on once an agent is connected (until you turn it off). A pack command only runs when what it needs is connected.
| Pack | Phrases | Does |
|---|---|---|
| Daily | "remind me to {x} at {time}" | A task in Apple Reminders, when Reminders is connected. Snag says "Reminder set." |
| "add {item} to {list}" | A task in your default task destination, with {list} as the project. Snag says "Added." | |
| "what's on today" | Needs a service Snag doesn't offer in this version, so it doesn't run. | |
| Agents | "what are my agents doing", "anything waiting for me", "park that" | Control's own status, attention and park. Needs one agent. |
| Work | "log {hours} hours on {project}", "status of {project}" | Needs a time-tracking connection, which Snag doesn't offer in this version, so they don't run. |
Make your own command
- Open Control > Voice commands > New command.
- Pick what it runs: "n8n workflow", "Zapier Zap", "Make scenario" or "Any webhook".
- Follow the steps below. The step count shows at the top ("1 of 4").
| Service | Steps |
|---|---|
| n8n, Make, any webhook | The URL, the signing secret, a test, the phrase |
| Zapier | The URL, a test, the phrase |
1. The URL
| Service | Where the URL comes from |
|---|---|
| n8n | "Copy the URL from your workflow's Webhook node." "Scan QR code" reads the QR code n8n shows. |
| Zapier | "In Zapier, start the Zap with Webhooks by Zapier, Catch Hook. Copy its URL here." Keep it private: anyone with it can start the Zap. |
| Make | "In Make, start the scenario with Webhooks, Custom webhook. Copy its address here." |
| Any webhook | "Paste the URL Snag should call. It gets JSON, signed." |
"Paste" fills the field from the clipboard. The address must be https; plain http works only on your own network ("Use an https address. Plain http works only on your own network.").
2. The signing secret
"Snag signs every call, so your workflow can tell it's really Snag."
Each command gets its own secret, made when the flow opens: whsec_ and 24 random bytes. Tap Copy secret and paste it into the workflow. The secret is kept in this iPhone's Keychain, never in the command itself.
"How to check it in n8n" (or "How to check it") lists the steps: read the raw body and the webhook-id, webhook-timestamp and webhook-signature headers, check the signature (HMAC-SHA256 of id.timestamp.body with the secret), and refuse the call if it doesn't match. In n8n, turn on Raw Body on the Webhook node and check it in a Code node.
Zapier doesn't check signatures, so the Zapier flow skips this step. Its calls are still signed.
The signing format, with examples, is in Webhooks.
3. Test it
Snag sends one real, signed call with sample values: 42 for each slot (25 for a slot with "minute" in its name).
The result card shows what was sent ({"miles": 42}), how long the workflow took ("n8n replied in 0.4 s"), its reply, and "Worked" or "Failed". When it worked, "Snag will say" shows the line Snag would speak. "Send again" repeats the test.
| Message | Meaning |
|---|---|
| "That workflow refused the key." | It answered 401 or 403. |
| "That workflow failed." | Any other error status. |
| "Couldn't reach that workflow." | No answer. |
| "Plain http is only for a local network address. Use https." | A plain http address that isn't on your network. |
For Zapier: "Zapier replies straight away, so there's no answer to read back yet." Snag says "Sent." right away.
4. What do you say?
Type the phrase. "Braces make a slot: log {miles} miles." With slots, the line below shows what Snag sends, for example {"miles": 42}. "That phrase can't be used: check the braces." means the braces don't pair.
Then choose what Snag says back:
- Say the workflow's reply (on): Snag reads the
spokenfield of the workflow's JSON reply (orsay, when there is nospoken), or a plain text reply as it is. With nothing to read, Snag says "Sent.". - Off: write your own line, for example "Logged it." Keep it to 8 words or fewer for the car.
Tap Save command.
Edit a command
Tap a command under Yours.
| Section | Shows |
|---|---|
| When I say | The phrase. |
| Then | The service ("Run an n8n workflow", "Start a Zap", "Run a Make scenario", "Call a webhook"), the Webhook URL, and what it Sends. |
| Snag says | The workflow's reply, or your own line. Zapier always says "Sent.". |
| Signing secret | "Set on this iPhone" with "Copy", or "Not set yet" ("Paste the secret your workflow checks. Without a secret, calls go unsigned."). Paste a new one and tap "Save secret" to replace it. |
| Test with 42 | Sends a test call, as in step 3. |
| Delete command | Asks "Delete this command?" first. |
Tap Save at the top to keep changes. A command from a linked repo is read only: "From you/snag-commands, main. Change it in the file and Snag picks it up."
Import commands
Control > Voice commands > Import commands has three ways in:
| Button | Does |
|---|---|
| File | Opens a commands file: YAML, JSON, plain text, .yml or .snagcommands. |
| URL | Fetches a commands file from an https address. |
| Repo | Opens Commands from a repo (below). |
Before anything is imported, each command shows the address it calls ("Calls n8n.example.net") and a tag: "New", or "Already have" for a phrase you already use. Tap Import 2 to add the new ones; the ones you already have are left alone. If any imported command signs its calls, add each one's secret on the command afterwards.
| Message | Meaning |
|---|---|
| "That file has mistakes: ..." | The file loaded but some commands are invalid. The first three problems are listed. |
| "That isn't a commands file Snag can read." | Not a commands file. |
| "Couldn't open that file." / "Couldn't get that file." | The file or the address couldn't be read. |
Share links
A snag://commands link adds a commands file:
snag://commands?data=...carries the file in the link.snag://commands?url=https://...points at a file on the web.
Opening one switches to Control > Voice commands. The file is added under Repos with "Changes waiting", and nothing runs until you accept it in the "Changes to your commands" sheet.
Recent runs
Each time a webhook command runs, Snag records it. Recent runs shows the latest 10 (Snag keeps 50): what Snag said back, when ("07:58, in the car" or "in the app"), and "Failed" if it failed.
Tap a run to see:
- HEARD: what you said.
- SAID BACK: what Snag said.
- Sent: the slot values sent.
- Workflow: the address and "answered in 0.4 s", with "Worked" or "Failed".
- Run again: sends the same values as a test. Edit opens the command.
What commands can do
Commands made in the app call a webhook. Commands from a file or a repo can also:
| Action | Does |
|---|---|
| webhook | Calls n8n, Zapier, Make or any address, signed. |
| shortcut | Runs an iOS Shortcut. Only with Snag on screen: in the car it parks with "Shortcuts need Snag on screen." and Snag says "That one needs the phone. Parked." |
| agent | An ask or a job for one of your agents, from a template. |
| capture | A note or task to a destination, from a template. |
| builtin | Control's own verbs: status, attention, park, approve, hold, deny, more, next. |
A command can also carry an approval class (it asks "approve or hold" or for a confirm phrase before it runs, as agent approvals do) and a privacy level (a Private reply asks "Say it out loud?" first). The file format is in Commands in a repo.
Commands from a repo
Keep your commands in a file in a GitHub or GitLab repo. Snag reads .snag/commands.yaml on the chosen branch and writes nothing back.
Open Control > Voice commands > Commands from a repo ("GitHub or GitLab"), or Import commands > Repo.
GitLab
- Pick GitLab ("gitlab.com or your own") and tap Continue with GitLab.
- Enter your GitLab address, or leave it as gitlab.com.
- If Snag isn't registered on that GitLab yet, the screen asks for an Application ID: in GitLab, add an application named Snag with redirect URI
snag://oauth/gitlab, not confidential, scoperead_repository, and paste its Application ID. - Tap Sign in to GitLab and approve on GitLab's own page ("Authorize Snag to use your account?", read_repository).
- Pick the project and branch ("Which project?").
- Check the preview and tap Accept.
A pasted address (GitHub, GitLab or a file)
- Pick GitHub or GitLab and tap Paste a repo or file address. In this version GitHub sign-in isn't set up, so Continue with GitHub opens this step too ("GitHub sign-in isn't available in this version.").
- Paste the repo's address, or a commands file's. Add
/tree/<branch>to pick a branch. - For a private repo, add an Access token (read-only is enough: a fine-grained GitHub token with Contents read, or a GitLab project or personal token). A public repo needs none. The token stays in this iPhone's Keychain.
- Turn on It's on my own GitLab for a self-hosted GitLab.
- Tap Continue, check the preview and accept.
The preview
The preview lists what was found ("Found .snag/commands.yaml: 3 commands"), each command with the address it calls, and any agents the repo defines. "Each address is shown before you accept." Tap Accept 3 commands. Nothing runs before that tap.
| Shown | Meaning |
|---|---|
| "Linked." | Accepted. "Change the file and Snag picks it up; anything calling a new address waits for you." |
| "No .snag/commands.yaml on this branch." | The file isn't there. |
| "Couldn't read the file: ..." | See the reasons below. |
| "Already linked, and nothing has changed." | The same repo and branch is linked already. |
Checks each time Snag opens
Snag fetches every linked repo each time it opens, and on "Check now".
- A change that calls no new address (a changed phrase or reply) is taken straight away.
- A change that calls a new address waits. The Voice commands chip shows a count, the repo shows "Changes waiting", and the "Changes to your commands" sheet opens on the Voice commands page.
- Until you accept, the old version keeps working.
- A failed check keeps the last accepted version working.
The repo row shows the command count and when it was last checked, or why the last check failed:
| Shown after "Last check failed, the accepted version keeps working:" | Meaning |
|---|---|
| "sign in again." | The token was refused. |
| "couldn't reach it." | No connection to the host. |
| "not found." | The repo, branch or file isn't there. |
| "the file has a mistake." | The file doesn't load. |
| "it didn't answer." | Any other failure. |
Tap a repo for Review changes (when something waits), Check now and Unlink.
Changes to your commands
The sheet names the repo and the number of changes. Each line is tagged "New", "Changed" or "Removed", with the phrase and the address it calls. A changed command that now calls somewhere else shows "Now calls example.net, a new address". Agents the repo adds or changes are counted too ("1 agent changes too.").
"Until you accept, the old version keeps working and the new one waits." Tap Accept 2 changes, or Not now.
Advanced settings
Settings > Advanced. Turn on Use my own infrastructure ("For people who run their own servers.") to show:
| Row | Does |
|---|---|
| Direct endpoints | Agents and endpoints as JSON (Talk rooms, OpenAI-compatible agents, agent APIs, overrides, user), and keys by name. "Listen before anything's connected" makes Snag hear names and commands with nothing set up. |
| AI and speech | Your own models and transcription, per feature. |
| snag-bridge | A relay address, tagged "Not used in this version": Snag keeps the address and does not use it. |
| Try it | Type what you'd say, with answers separated by |, and run it "On the phone" or "In the car". Shows both sides, or "Not a command or an agent: it would be a capture." |
| ntfy | A topic your server posts to when something is waiting. Snag checks it each time it opens. |
Keys go in the Keychain by name: "Keys stay in this iPhone's Keychain. Saving an empty value removes the key." Details are in Bring your own stack.
Related
- Snag Control
- Webhooks: the signed request, the reply, and async answers.
- Commands in a repo: the file format.
Next: CarPlay