A Hammerspoon Spoon that sends a webhook when you become active after being idle.
PresenceWebhook notifies external services (Slack, Discord, home automation, etc.) when you return to your computer after being away. Useful for:
- Letting your team know you're back
- Triggering home automation when you sit down to work
- Logging your work patterns
- Install Hammerspoon: https://www.hammerspoon.org
- Download the latest
PresenceWebhook.spoonzip from Releases: https://github.com/manuel-grondona/presence-webhook-spoon/releases/latest - Double-click the zip to unzip it.
- Double-click
PresenceWebhook.spoonto install it. If it does not show up in Hammerspoon, manually copy it into~/.hammerspoon/Spoons/(Finder -> Go -> Go to Folder...). - Click the Hammerspoon menu bar icon -> Open Config.
- Paste this into
~/.hammerspoon/init.lua, then save:
hs.loadSpoon("PresenceWebhook")
spoon.PresenceWebhook
:configure({
idleThresholdSeconds = 600,
cooldownSeconds = 600,
webhookURL = "https://your-service.com/webhook",
headers = { Authorization = "Bearer YOUR_API_KEY" },
messageTemplate = "User active after {{idle_seconds}}s at {{timestamp}}",
})
:start()- Update the webhook URL and API key in the config above.
- Click the Hammerspoon menu bar icon -> Reload Config.
- Grant Accessibility permissions to Hammerspoon when prompted.
Example values:
idleThresholdSeconds = 600means 10 minutescooldownSeconds = 600means 10 minutes
This Spoon only detects activity and does not log or record what you type.
If you already use SpoonInstall, you can install from a Releases zip URL:
hs.loadSpoon("SpoonInstall")
spoon.SpoonInstall:installSpoonFromZipURL("https://github.com/manuel-grondona/presence-webhook-spoon/releases/latest/download/PresenceWebhook.spoon.zip")Then reload Hammerspoon. Docs: https://www.hammerspoon.org/Spoons/SpoonInstall.html
- Listens for mouse/keyboard events via
hs.eventtap. - If you were idle for
idleThresholdSeconds, the next input triggers a webhook. - Cooldown prevents repeated notifications.
Log file: ~/.hammerspoon/presence-webhook.log
idleThresholdSeconds(default 600)cooldownSeconds(default 600)webhookURL(required)headers(optional)payloadFn(optional override function; receives the base event table withidle_seconds,timestamp,host,eventand returns the full payload table)messageTemplate(optional string template formessage)payloadTemplate(optional table of templated values, merged into the payload)logPath(optional, defaults to~/.hammerspoon/presence-webhook.log)
{
"event": "user_active_after_idle",
"idle_seconds": 3600,
"timestamp": "2026-01-14T20:10:35Z",
"host": "My-MacBook"
}payloadFn overrides the entire payload. Use it when you want full control:
payloadFn = function(event)
return {
message = "Back after " .. event.idle_seconds .. "s",
metadata = event,
}
endmessageTemplate adds a message field on top of the default payload:
messageTemplate = "Back after {{idle_seconds}}s at {{timestamp}}"payloadTemplate adds custom fields (templated strings are rendered):
payloadTemplate = {
message = "Back after {{idle_seconds}}s",
metadata = {
idle_seconds = "{{idle_seconds}}",
timestamp = "{{timestamp}}",
host = "{{host}}",
},
}Use {{...}} placeholders in messageTemplate or payloadTemplate.
Available values:
idle_secondstimestamphostevent
Notes:
- Templates apply only when
payloadFnis not set, becausepayloadFnreturns the entire payload. payloadTemplatecan include nested tables; string values are rendered.
spoon.PresenceWebhook:start()– Start listeningspoon.PresenceWebhook:stop()– Stop listeningspoon.PresenceWebhook:toggle()– Toggle on/off
- If no events fire, make sure Hammerspoon has Accessibility permissions.
- Confirm the log file at
~/.hammerspoon/presence-webhook.logis being written. - Use the menu bar item to reopen permissions or toggle the listener.
The Spoon shows a menu bar item:
- Toggle enable/disable
- Open log file
- Open permissions settings
- Quit (disables listener)
MIT