The Control API is the keyboard-friendly automation surface. Three transports speak the same verbs:
- Unix domain socket at
~/Library/Application Support/ApexDock/api/control.sock— line-delimited JSON, request/response. Connection stays open across requests. apexdock://URL scheme — fire-and-forget triggers from Shortcuts.app, Raycast, browsers.apexdockCLI —ApexDock.app/Contents/Resources/bin/apexdock, install via Settings → Integrations.
All control verbs require an active ApexDock license grant except settings.open,
which remains available while locked so you can reach licensing and account
settings.
Wire format
Requests are NDJSON, one per line:
{"v":1,"id":"opt-correlation-id","cmd":"workspace.switch","args":{"name":"Work"}}
Responses are NDJSON, one per line:
{"v":1,"id":"opt-correlation-id","ok":true,"data":{"id":"…","name":"Work"}}
| Field | Notes |
|---|---|
v | Schema version. Currently 1. |
id | Optional correlation ID echoed in the response. |
cmd | The verb. |
args | Object of string/bool/number values. |
Errors:
{"v":1,"id":"opt-correlation-id","ok":false,"error":"no workspace matches: foo"}
Verbs
cmd | Args | Returns | Notes |
|---|---|---|---|
workspace.list | — | array of {id, name, accent?, folder?} | |
workspace.switch | {id} or {name} | {id, name} | Name match: case-insensitive, exact > prefix > contains |
workspace.current | — | {id, name} or {} | Empty when no workspace is active |
app.focus | {bundleId} | {bundleId, launched} | Activates if running, otherwise launches via NSWorkspace |
app.list-pinned | — | array of {bundleId, name} | Reads the global pinned list |
bar.toggle-visible | — | — | Hides if visible, shows if hidden |
bar.set-visible | {visible: Bool} | — | Forces a state |
action.toggle-dock-suppressor | — | — | Mirrors Settings → Bar → Hide macOS Dock |
action.toggle-window-minder | — | — | Mirrors Settings → Bar → Adjust Windows Around Bar |
settings.open | {tab} | {tab} | Tabs: bar, permissions, agents, assistant, commandPalette, workspaces, about |
palette.show | — | — | Opens the command palette |
palette.run | {query} | {name, kind} | Local fuzzy match; runs the top hit |
URL scheme
apexdock://<resource>/<verb>?<args>. Args are URL query parameters.
apexdock://workspace/list
apexdock://workspace/switch?name=Work
apexdock://workspace/switch?id=01234567-89AB-CDEF-0123-456789ABCDEF
apexdock://workspace/current
apexdock://app/focus?bundleId=com.apple.Safari
apexdock://app/list-pinned
apexdock://action/toggle-dock-suppressor
apexdock://action/toggle-window-minder
apexdock://bar/toggle
apexdock://bar/set?visible=false
apexdock://settings?tab=agents
apexdock://palette/show
apexdock://palette/run?q=switch%20to%20Work
q is sugar for query. Responses from URL invocations aren't surfaced — use the socket or CLI when you need the result.
Smoke tests
SOCK="$HOME/Library/Application Support/ApexDock/api/control.sock"
# socat — interactive, keeps connection open
printf '{"v":1,"cmd":"workspace.list"}\n' | socat - UNIX:"$SOCK"
# nc — one-shot
printf '{"v":1,"cmd":"workspace.current"}\n' | nc -U "$SOCK"
# CLI — same effect
apexdock workspace current --text
# URL scheme — fire-and-forget
open 'apexdock://workspace/switch?name=Work'
Recipes
Raycast
#!/bin/bash
# @raycast.title Switch ApexDock Workspace
# @raycast.argument1 { "type": "text", "placeholder": "name" }
apexdock workspace switch "$1" --text
Alfred
/usr/local/bin/apexdock workspace switch "{query}"
Shortcuts.app
Add an Open URL action with apexdock://workspace/switch?name=Work. Combine with Ask for Input to parameterise. Or use a Run Shell Script action with the CLI.
Keyboard Maestro
Use Execute Shell Script:
/usr/local/bin/apexdock action toggle-dock-suppressor
Bind to whatever keystroke. The CLI exits non-zero on failures, so KM's "If last action failed" branch can show a notification.
Status line
while sleep 5; do
apexdock workspace current --text
done
Limitations (v1)
palette.runruns the local fuzzy matcher only — it doesn't invoke the LLM router. Usepalette.showand type if you need AI routing.bar.toggle-visibleflips every bar panel together; per-display control isn't exposed.- Schema is
v: 1. Future breaking changes will bump tov: 2.