Klient wiersza poleceń
Wysyłaj powiadomienia, steruj wydarzeniami na żywo, aktualizuj widżety i czekaj na odpowiedzi ze skryptu powłoki, zadania crona lub CI za pomocą klienta wiersza poleceń pushward.
pushward is a client for the same REST API, authenticated with your hlk_ integration key. It covers every operation in the public API, and anything newer is reachable through pushward api. Source and releases: mac-lucky/pushward-cli.
Install
brew install mac-lucky/tap/pushwardHomebrew also installs the shell completions (pushward completion bash|zsh|fish|powershell prints them otherwise). With Go:
go install github.com/mac-lucky/pushward-cli/cmd/pushward@latestOr grab an archive from Releases. The macOS binaries in those archives are not signed, so a copy downloaded with a browser needs xattr -d com.apple.quarantine pushward first; Homebrew and go install are not affected. There is also a container image, ghcr.io/mac-lucky/pushward-cli:
docker run --rm -e PUSHWARD_API_TOKEN ghcr.io/mac-lucky/pushward-cli notify --title Hi --body ThereAgent skill
Coding agents such as Claude Code, Codex and Cursor can drive the CLI for you. The pushward skill teaches them which command fits "ping me when the tests pass", "ask me before you push" or "show the migration on my lock screen", to end every Live Activity they start, and to keep your key out of the chat:
npx skills add mac-lucky/pushward-cli --skill pushwardThe agent uses the key the CLI already has, so run pushward auth login yourself first. A key limited to agent-* slugs (see Creating scoped keys) keeps it away from your other activities. If your agent talks to the MCP server instead, use that page's skill.
Key
Copy an integration key from the app, then either export it or store it once:
export PUSHWARD_API_TOKEN=hlk_...
pushward auth login # prompts, checks the key, writes ~/.config/pushward/config.json (0600)
pushward auth statusThe environment variable wins over the stored key. There is no --token flag on purpose, so the key stays out of shell history and ps. pushward auth login --with-token reads the key from stdin, for piping it out of a password manager.
A key only does what its permissions allow: starting a Live Activity needs activities at manage, and notifications, widgets and email each need their own level. See Creating scoped keys.
Notifications
pushward notify --title "Backup done" --body "412 GB in 38m"
make 2>&1 | tail -20 | pushward notify --title "Build log" --body -
pushward notify --title "Disk" --body "/data at 91%" --level time-sensitive --url https://grafana.example.com--body - reads the body from stdin. Add buttons with --action id=Title (repeatable), and --wait blocks until one is tapped:
answer=$(pushward notify --title "Deploy to prod?" --body v2.4.0 \
--action deploy=Deploy --action skip=Skip --wait 15m --jq .action_id)
[ "$answer" = deploy ] && ./deploy.shNobody answering in time exits 7. pushward notification answer <id> --wait 5m picks up the same answer later. How answers are recorded is covered in Answers.
Ta sekcja opisuje funkcję z aktualizacji aplikacji oczekującej na weryfikację w App Store. Odblokuje się tutaj automatycznie, gdy tylko aktualizacja będzie dostępna.
Repeat until acknowledged
From CLI 1.4.0, --ack on notify and schedule create sends the notification again until someone acknowledges it on one of their devices or it expires: every minute for an hour, unless --ack-repeat (30s to 1h) and --ack-expire (1m to 3h) say otherwise. Repeats are free. --tag and --callback-url go with it, notify --ack --wait blocks until it is acknowledged, and pushward receipt secret prints the secret callbacks are signed with.
pushward notify --title "db-1 down" --body "Primary unreachable" --level time-sensitive --ack --tag db-1
pushward receipt 4242 --wait 10m # exit 7 if it expires, or 10m pass, first
pushward receipt cancel --tag db-1 # stops what this key sent with the tagDetails, limits and callback verification: Acknowledged Alerts.
End-to-end encryption
From CLI 1.4.0, with an encryption key from PUSHWARD_E2E_KEY or the config file, notify, notification send and schedule create seal the title, subtitle, body and url before the request leaves your machine. --no-encrypt sends one in the clear, --encrypt fails when no key is set, and pushward api never encrypts. e2e import --force replaces a different stored key.
pbpaste | pushward e2e import # the key from the app's Settings > End-to-End Encryption
pushward e2e key-id # compare with the Key ID in the app
pushward e2e generate --save # or create one here and add it in the appWhat is encrypted, the envelope format and its limits: End-to-End Encryption.
Live Activities
pushward activity start deploy --name "Deploy api" --template steps --step 1/3 --step-labels Build,Test,Ship --text Building
pushward activity update deploy --step 2/3 --text Testing
pushward activity end deploy --status successactivity start creates the activity if needed and sets it ongoing with its first content; starting a slug that already exists restarts it.
activity end --status success|failure|cancelled shows a final frame first (green check, red cross or grey stop, with the progress filled on success), holds it for --display-time (4s by default) and then ends the card, so the Lock Screen shows the outcome instead of a card that just vanishes. --text, --icon and --color override the preset.
All ten templates work. For the ones without a dedicated flag, set content fields directly:
pushward activity update ci -F content.progress=0.4 -f content.state="Compiling"
pushward activity update board --data @board.jsonApproval
An approval card waits for a decision the same way a notification does. activity wait gives up after --timeout (10 minutes by default) and exits 7:
pushward activity start release --template approval --text "Ship 2.4?" --option ship=Ship --option hold=Hold
pushward activity wait release --timeout 30m --jq .content.answer.optionactivity start makes two calls, a create and then the first update, and both count against your Live Activity quota (see Limits). The API has no single call that creates an activity with a name and shows it.
Widgets, schedules, email
pushward widget create cpu --template gauge --min 0 --max 100 --unit % --value 12
pushward widget update cpu --value 57
pushward schedule create --in 2h --title "Stand up" --body Stretch
pushward schedule create --cron "0 9 * * 1-5" --tz Europe/Warsaw --title Standup --body "In 5 minutes"
pushward schedule list
pushward schedule cancel 1234 --purge
pushward email send --to [email protected] --subject "Nightly report" --text-file report.txtSee Widgets, Scheduled notifications and Email for what each field does. Email only goes to recipients you have verified in the app.
With a key from an organization, notify, schedule create, activity create, activity start and activity update take --target-groups, --target-tags and --target-members (CLI 1.3.0 and later), and activity update --no-target clears the target.
Request bodies
Every write command builds its body from four layers, later ones winning:
--data(-d): a JSON literal,@file, or-for stdin.- The command's own flags.
-f key=value: always a string.-F key=value: typed, so numbers,true/false/null, JSON literals and@file.
Keys are dotted paths; key[] appends to an array and key[2] sets an index.
pushward notify --title T --body B -F push=false -f metadata.host=web-1
pushward activity update rack -F content.template=board \
-F 'content.tiles[]={"label":"CPU","value":"12","unit":"%"}' -F 'content.tiles[]={"label":"Fans","value":"on"}'For endpoints or fields the other commands do not cover, pushward api calls any path with the same key and the same -f/-F/--data handling:
pushward api /activities?state=ongoing
pushward api -X PATCH /activities/build -F content.progress=0.5Output
On a terminal you get a short summary or a table. Piped, or with --json, you get the API response as JSON; --jq filters it and -q prints nothing.
pushward activity list --state ongoing --jq '.items[].slug'
pushward me --jq .live_activity_updates_usedExit codes
Exit codes are stable, so scripts can branch on them:
| Code | Meaning |
|---|---|
0 | OK |
1 | Any other error, such as a 422 validation failure |
2 | Bad usage |
3 | Missing, invalid or unauthorized key |
4 | Not found |
5 | Rate limited or out of quota |
6 | Server error or network failure |
7 | --wait (or activity wait) ran out |
Rate limits (429) and 503s are retried for up to a minute, honoring Retry-After. Other server errors and network failures are only retried for reads and deletes: retrying a POST could send a notification twice.
GitHub Actions
The same binary runs the mac-lucky/pushward-action GitHub Action, which turns these commands into workflow steps with the run's context filled in. See GitHub Actions.