Approving unattended agent actions: your options compared
An agent that's running without anyone watching still sometimes needs a human to say yes. There are several real ways to wire that up — not just pendnt's. This page compares five of them honestly, including the two cases where you're better off not using pendnt at all.
On this page
Comparison at a glance
| DIY: ntfy/Pushover + hooks | Remote Control + push | Pushary | OpenClaw ask_user |
pendnt | |
|---|---|---|---|---|---|
| Cost | Free (ntfy) or a one-time ~$5/platform app license (Pushover); the send API is free either way | Free, built into Claude Code | $9.99/mo (Agent), up to $19.99–59.99/mo for more agents/seats; 3-day trial, no free tier | Free, built into OpenClaw | Free (Trial or 100/mo Free) or $9/mo Pro, unlimited |
| Server you have to run | None to send; something has to receive the tap-back if you want real approve/deny, not just a one-way ping | None | None | None beyond the OpenClaw Gateway you already run | None |
| Works unattended (cron/SDK/headless) | Yes — it's an HTTP call from a hook script, fires from anywhere | No — interactive sessions only | Not documented — built around a live, blocking hook in a session you started | No — ask_user needs a live chat session; cron jobs get none |
Yes — this is the case it's built for |
| Durable, answer-it-later state | No — nothing persists past the hook call/process that made it | Session-scoped, tied to the live session | Bounded — a configurable hook timeout (docs example: 120s) with an auto-fallback, not a durable store you come back to days later | No | Yes — 7 days (Free) / 90 days (Pro), poll or long-poll a request_id whenever |
| Answer channels | Whatever you wire up: ntfy app (with action buttons), Pushover app (emergency-priority ack callback for a rough approximation of "waiting for an answer") | Claude's mobile/web app push | iOS/Android app (QR setup, no app store), Mac notch app, Slack | Whatever chat surface the session is attached to | Email, Telegram, outbound webhook (Slack/Discord-shaped payload), web inbox |
| Setup | Write the hook script and the receiver yourself | Built in | npx @pushary/agent-hooks setup |
Built in | MCP tool call or REST call; plugin for Claude Code, snippet for the Agent SDK, skill for OpenClaw |
| Self-hostable | Yes — ntfy can run on your own box; Pushover can't | No — Anthropic-hosted | No — hosted service | Yes — it's your Gateway | No — hosted service (Cloudflare Workers/D1) |
DIY: ntfy/Pushover + Claude Code hooks
What it is: a PreToolUse or Notification hook (see
the hooks guide) that shells out to curl
against ntfy or Pushover's send
API. Both are genuinely free to send from: ntfy's public server takes anonymous POSTs to a topic
with no account, or you self-host it; Pushover needs a one-time, roughly $5-per-platform app
purchase after a 30-day trial, but the API used to send messages from your script is free within
a generous per-app monthly quota.
# ntfy — a one-way ping, no answer expected
curl -d "Deploy release/1.4.0 to prod?" ntfy.sh/your-topic-here
# ntfy — an actual approve/deny button, via its action-button feature
curl -H "Actions: http, Approve, https://your-host/approve?id=42, method=POST" \
-d "Deploy release/1.4.0 to prod?" ntfy.sh/your-topic-here
That second example is the honest nuance most comparisons skip: ntfy's
action buttons can fire an HTTP request
when tapped, and Pushover's emergency-priority messages support a callback URL that fires when
someone acknowledges — so "DIY" isn't limited to one-way pings, it can genuinely do approve/deny.
The catch is that both of those need https://your-host/approve to exist: something
that receives the tap, marks the request answered, and hands that back to the still-running (or
re-invoked) hook. Writing and hosting that receiver, and the correlation logic between "which
request is this tap for," is the DIY glue — no one ships it for you, and nothing about it survives
past whatever you built to hold it (a file, a variable, an in-memory map) once that process exits.
Pushover's emergency-priority retries and callback stretch that window to hours; nothing here gets
you to days.
Good fit when: a single machine, a hobby project or personal automation, and
you're fine owning a small bash script and a webhook receiver in exchange for paying nobody and
depending on nothing but two well-established free/cheap notification services.
Not a fit when: you want this working across more than one machine or agent
without re-deriving the glue each time, need an answer to survive a process restart, or don't want
to be the one who finds and fixes the bug in your own correlation logic at 3am.
Claude Code Remote Control + push
What it is: Anthropic's own answer for a Claude Code session you started yourself, from your terminal — Remote Control lets you step away and keep approving permission prompts and steering the session from your phone via push notification, free, built into Claude Code and the Claude apps with no separate account or setup.
The entire mechanism assumes a live session on the other end. A claude -p run
kicked off by cron, a headless Agent SDK daemon, or any script with no interactive session open in
the first place has nothing for Remote Control to attach to — there's no session for your phone to
remote into. That's not a shortcoming so much as a difference in what problem it's solving: Remote
Control is "let me leave my desk without losing my running session," not "let this unattended job
ask someone something."
Good fit when: you're driving Claude Code interactively — you ran it, you're
watching it (loosely), and you just want to step away without disconnecting.
Not a fit when: nobody started an interactive session — cron, CI, a daemon, or any
agent that runs whether or not you're at a keyboard.
Pushary
What it is (as of its public docs): a
control-panel product built primarily around Claude Code (it also lists Codex, Cursor, Windsurf,
Gemini CLI, and other MCP-compatible agents), installed with npx @pushary/agent-hooks
setup. It wires a PreToolUse hook into Claude Code's settings that intercepts
matching tool calls (its docs show a matcher of Bash|Write|Edit, i.e. selective, not
every tool) and routes them to a phone app (iOS/Android, no app-store install — QR-code setup), a
Mac notch app, or Slack, where you can approve, deny, pick from a short list, or type a free-text
reply. Pricing is $9.99/mo for the Agent plan (one agent, 5,000 notifications/month, 30-day
history) after a 3-day free trial, up through $19.99/mo (Agent Pro, unlimited notifications,
multiple agents) and $59.99/mo (Team, up to 10 members) — there's no free tier, only the trial.
The hook example in its own docs sets a "timeout": 120 and a fallback policy
(auto-deny for Bash, auto-approve for reads, or handing control back to the terminal) for what
happens if you don't answer in time — which describes a live session with a blocking wait, not a
durable store you come back to hours or days later. Its documentation doesn't mention
claude -p, cron, or headless/unattended usage, so it's fair to read that as
undocumented rather than supported — it's built for a session where the hook can actually block
and someone is expected to be near their phone.
Good fit when: you live in Claude Code interactively, want a polished native
app (and Mac notch, and Slack) rather than something you assembled yourself, and the cost of a
monthly subscription is worth it for that UX.
Not a fit when: the thing that needs approving is a cron job or an SDK daemon with
no live session for the hook to block inside of, or the request needs to survive longer than a
short blocking timeout — its own docs don't describe either case.
OpenClaw's ask_user
What it is: the built-in, free way to pause an OpenClaw turn and put a question
in front of whoever's in the live chat session — no separate service, no signup, it's just part of
OpenClaw. The catch, covered in full in the OpenClaw cron
approvals guide: a job created with openclaw automations create ... --session
isolated runs from the Gateway process in a fresh cron:<jobId> session
with no chat surface attached and nobody on the other end — ask_user there has nothing
to render into. That's not a bug so much as the same category error as Claude Code's -p
mode having no TTY: the tool assumes an audience a scheduled run structurally doesn't have.
Good fit when: the OpenClaw session is a live chat someone's actually watching.
Not a fit when: the job runs on cron/every with an
isolated session — which is exactly when you need the poll-by-id pattern the guide above walks
through.
pendnt
What it is: an MCP server + REST API built specifically for the case none of
the above cover — cron, SDK daemons, and any framework, not just Claude Code. See the FAQ for how it sits next to Remote Control specifically. A
Trial key (curl https://api.pendnt.dev/try, no signup, 20 requests over 72 hours) or a
Free account (100 approvals/notifications a month, no card) both work for real; Pro is $9/mo,
unlimited, no sales call. request_approval returns a request_id
immediately and lets the agent long-poll or walk away and check back later — answers and pending
requests are retained 7 days on Free, 90 on Pro, which is the concrete difference from a
session-scoped or short-timeout approach: an agent that crashes, redeploys, or just goes quiet for
a day can still pick the same request back up by id. Answers can land by email, Telegram, an
outbound webhook shaped for Slack or Discord's incoming-webhook format, or the web inbox with zero
setup; see channels for the connection steps each one needs. It's a
hosted, multi-tenant service (Cloudflare Workers, Durable Objects, D1) — no self-host build, see
the FAQ for why.
Good fit when: the thing asking the question has no terminal and no live
session — a cron'd claude -p, a headless Agent SDK daemon, a LangGraph node, a plain
script — and you want an answer that's still good if it takes longer than a few minutes to arrive.
Not a fit when: see the next section.
When not to use pendnt
This page exists to be useful even if you end up choosing something else, so here's where that's the right call:
- You only ever run Claude Code interactively, from your own terminal. Remote Control is free, already built in, and solves exactly that case — there's no reason to add a service on top of it. pendnt's whole reason to exist is the sessions Remote Control doesn't reach.
- You're running one box, for yourself, and don't need answers to outlive the process
that asked. ntfy is free, and a hook script that
curls it — optionally with an action button — is genuinely less to maintain than adopting a hosted service for a single-machine side project, provided you're fine owning the glue and don't need a request to survive past the hook's own timeout. - You need everything self-hosted, no third party ever sees a request title. A
self-hosted ntfy server, or OpenClaw's own self-hosted Gateway with
ask_userinside a live chat, are the only options on this page that don't send anything to a hosted service — pendnt, Pushary, and Remote Control are all hosted (Remote Control by Anthropic). - You're all-in on OpenClaw and every job that needs a human runs in a live chat.
ask_useralready does that for free; you'd only reach for pendnt's OpenClaw skill once you have jobs oncron/everywith isolated sessions. - You specifically want a polished native phone/Mac-notch app and don't run anything headless. That's a real, different product shape than pendnt's — see the Pushary section above — and worth the subscription if that UX is what you're optimizing for.
Picking one
The fork that matters most is whether the thing asking the question has a live session behind
it. If yes — you're at a terminal, or in an OpenClaw chat — reach for what's built in and free
first: Remote Control or ask_user. If no — cron, a daemon, CI, anything unattended —
none of the free built-ins reach it, and the choice narrows to how much glue you want to own
yourself (ntfy/Pushover) versus how durable and framework-agnostic you need the answer to be
(pendnt), or whether the Claude-Code-specific native-app experience (Pushary) is worth a
subscription for a session that's still, per its own docs, a live one.