# Claude routines push notifications: know when a cloud run needs you

> Claude routines run in Anthropic's cloud on a schedule, an API call or a GitHub event, and tell nobody when they finish. Here is how to get a phone push when a run is done, fails or finds something.

*18.10.2026*

<!-- source: https://pocketalert.app/blog/claude-routines-notifications -->

Claude routines are the part of Claude Code that runs without you. You save a prompt and a repository once, and Anthropic's cloud starts a fresh Claude Code session every night, on every new pull request, or whenever your monitoring calls an endpoint. Your laptop can be closed.

The catch is that a routine reports to nobody. The result sits in a session on claude.ai until you go and look, and the run list is not much help either: a green status only means the session started and exited without an infrastructure error, not that the work succeeded. A nightly dependency check that found a broken build looks exactly like one that found nothing.

This guide makes the routine send a push to your phone through Pocket Alert: a quiet one when all is well, a loud one when something needs you.

## What Claude routines are, briefly

Anthropic now calls them **scheduled tasks**. "Routines" is the earlier name and still shows up in the web address (`claude.ai/code/routines`), the `/routines` CLI alias and the API. It is the same feature, currently in research preview.

A routine is a saved Claude Code configuration: a prompt, one or more GitHub repositories, a cloud environment and a set of connectors. Each run is a full cloud session that can run shell commands, edit code, push to a `claude/` branch and call connectors. Runs start from triggers, and one routine can have several:

- **Schedule**: hourly, daily, weekly presets, a custom cron expression (no more often than once an hour), or a one-off run at a set time.
- **API**: a POST to the routine's own `/fire` endpoint with a bearer token. You can pass a `text` field, such as an alert body, that the run receives as context.
- **GitHub**: pull request, release or issue events on a repository with the Claude GitHub App installed, with pull request filters on author, branch, labels, draft state and more.

You create routines at [claude.ai/code/routines](https://claude.ai/code/routines) with **New task**, in the Desktop app, or with `/schedule` in the Claude Code CLI, for example `/schedule daily PR review at 9am`. They need a Pro, Max, Team or Enterprise plan, and runs draw down your subscription usage like interactive sessions do.

## What you need

- A Pocket Alert account, an API key from [API Keys](/account/apikeys), and the app on your iPhone or Android phone. The Free plan works.
- A routine, or a plan for one.

There are two ways for a run to reach your phone: `curl` from the session to the Pocket Alert API, or the [Pocket Alert MCP server](/tools/mcp) added as a connector. Start with curl. It works on every plan that has routines, and the key never enters the session.

## Setup: curl with a network secret

A routine's cloud environment has **Trusted** network access by default: package registries, GitHub and cloud provider APIs, nothing else. `api.pocketalert.app` is not on that list, so a plain `curl` would fail with `403` and `x-deny-reason: host_not_allowed`.

A **network secret** solves both the network and the key problem. You store the key on the environment, and Anthropic's proxy adds it to requests for the hosts you list after they leave the session's VM. Hosts on a network secret are reachable whatever the access level, and Claude never sees the key.

1. Open the routine at [claude.ai/code/routines](https://claude.ai/code/routines), choose **Edit**, then **Advanced settings > General > Cloud environment**. Hover over the environment and click its settings icon.
2. In **Network secrets**, click **Add secret** and keep the type **Bearer**.
3. **Name**: `Pocket Alert`. **Allowed websites**: `api.pocketalert.app`.
4. Under **Custom headers**, change the header name from `Authorization` to `Token`, clear the `Bearer` prefix, and paste your API key as the value.
5. Click **Add**. The secret saves immediately and can't be viewed again.

Now the request needs no key at all. Add this to the end of the routine's prompt:

```text
When you are done, send exactly one push notification with curl:

curl -sS https://api.pocketalert.app/v1/messages \
  -H 'Content-Type: application/json' \
  --data-binary @- <<'EOF'
{"title": "<routine name>: <one-line result>", "message": "<2-3 sentence summary>", "level": "<level>"}
EOF

Do not add any auth header; the environment adds it. Build valid JSON.
Pick the level:
- "low" if the run finished and nothing needs my attention.
- "default" if you opened a pull request or found something worth reading today.
- "high" only if the work failed, tests are broken, or you found a real problem.
Include the session link from:
echo "https://claude.ai/code/${CLAUDE_CODE_REMOTE_SESSION_ID/#cse_/session_}"
```

The last line is from Anthropic's docs: each cloud session can read its own ID from `CLAUDE_CODE_REMOTE_SESSION_ID`, and that command turns it into the transcript URL. With it in the message you go from the push to the run in one tap.

Click **Run now** on the routine's page to test it before the first scheduled run.

If your environment dialog has no **Custom headers** row (on a Team plan without an admin role the form asks only for a host and a value), set **Network access** to **Custom**, add `api.pocketalert.app` to **Allowed domains** with the default list kept, and put the key in an environment variable such as `POCKETALERT_KEY`, sent as `-H "Token: $POCKETALERT_KEY"`. Note that environment variables are readable by anyone who uses the environment, which is why the secret is the better option.

## Or attach the MCP server as a connector

Routines can use your claude.ai connectors, and connector traffic goes through Anthropic's servers, so it needs no allowlist change at all. Then the prompt just says "call the pocketalert `create_message` tool" with a title, message and level.

The limit is authentication. Pocket Alert's MCP server takes the API key as `Authorization: Bearer KEY`, and on claude.ai a custom connector can only send a fixed key through the **Request headers** section of the **Add custom connector** dialog. Anthropic marks that section as a beta available to a limited set of organizations. If you see it (on Team and Enterprise plans an Owner adds the connector under **Organization settings > Connectors** instead):

1. Go to [claude.ai/customize/connectors](https://claude.ai/customize/connectors) and click **Add custom connector**.
2. URL: `https://mcp.pocketalert.app/mcp`. Authentication: **No sign-in**.
3. In **Request headers**, pick `authorization` and enter `Bearer YOUR_API_KEY`.

New routines include all your connectors by default, so check the **Connectors** tab of the routine and keep the ones it needs. The Claude Code CLI's `claude mcp add` does not help here: servers added that way live on your machine and don't show up in routines. If you use Claude Code locally too, that setup is in [Claude Code push notifications](/blog/claude-code-push-notifications); for other MCP options see [Best MCP notification servers](/blog/best-mcp-notification-servers).

## Second level: a push that doesn't depend on the model

Both routes above rely on Claude following the last paragraph of your prompt. Usually it does. But a run that hits a rate limit or an API error never gets to that paragraph, and that is the run you most want to hear about.

Hooks run in routines too: a cloud session with one repository reads the repository's `.claude/settings.json`. A `Stop` hook fires when Claude finishes, and `StopFailure` fires instead when the turn ends on an API error such as `rate_limit` or `server_error`. Commit this as `.claude/settings.json`:

```json
{
  "hooks": {
    "Stop": [
      { "hooks": [{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/notify.sh done" }] }
    ],
    "StopFailure": [
      { "hooks": [{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/notify.sh failed" }] }
    ]
  }
}
```

And the script, `.claude/hooks/notify.sh`, made executable:

```bash
#!/usr/bin/env bash
[ "$CLAUDE_CODE_REMOTE" = "true" ] || exit 0

payload=$(cat)
last=$(jq -r '.last_assistant_message // ""' <<<"$payload")
link="https://claude.ai/code/${CLAUDE_CODE_REMOTE_SESSION_ID/#cse_/session_}"

if [ "$1" = "failed" ]; then
  title="Claude run failed: $(jq -r '.error // "unknown"' <<<"$payload")"
  level=high
else
  title="Claude run finished · $(basename "$CLAUDE_PROJECT_DIR")"
  level=low
fi

jq -n --arg t "$title" --arg m "${last:0:300}
$link" --arg l "$level" '{title: $t, message: $m, level: $l}' |
  curl -sS --max-time 10 https://api.pocketalert.app/v1/messages \
    -H 'Content-Type: application/json' --data-binary @- >/dev/null 2>&1 || true
```

The `CLAUDE_CODE_REMOTE` check keeps the hook silent on your laptop, where that variable is never `true`. `jq` is preinstalled in cloud sessions, and the network secret from the first section supplies the key. Keep the prompt-based push for the content ("3 outdated packages, PR opened") and the hook as the guaranteed "it ended" signal, or use only one.

The hook fires for every cloud session on that repository, not only routine runs, so expect a push when you use Claude Code on the web there too.

## Levels and buttons

The prompt above already uses the part that matters most: levels. `low` arrives quietly and stays in the history, `high` comes through as time-sensitive on iPhone and gets past Focus. The rule "high only for problems" is what keeps you from muting the whole thing after a week. `critical` exists too and breaks through Do Not Disturb on paid plans, but a nightly routine rarely deserves it. See [priority levels](https://info.pocketalert.app/features/priority-levels.html) for how each level behaves on iOS and Android.

On paid plans a message can carry up to three [action buttons](https://info.pocketalert.app/features/action-buttons.html). The useful one here is a `view` button with the session URL, added to the JSON as:

```json
"actions": [{"type": "view", "label": "Open run", "value": "https://claude.ai/code/session_..."}]
```

Every push also lands in the app's history and the web dashboard, next to alerts from your servers and CI, so a week of nightly runs is one scroll.

## Honest limits

- **The prompt is a request, not a guarantee.** Claude usually sends the push, but it can skip it, send two, or write the JSON wrong. Use the hook if you need certainty.
- **A run can stop and wait.** Routines start in auto mode, but an action that matches an ask rule, or repeated blocks, makes the run wait for an answer in its session. Neither the prompt nor `Stop` will tell you about that wait. If a routine usually pings you at 7:10 and doesn't, go look.
- **Usage limits apply.** Runs count against your subscription. Without usage credits, extra runs are rejected once you hit the limit, and a run that never starts sends nothing. There are also hourly caps: 100 scheduled runs per account, 30 Run now or API fires per routine.
- **Research preview.** Anthropic says behavior, limits and the API may still change.
- **Free plan.** 50 messages a day on one device. An hourly routine sending one push per run fits; one push per file it touched does not.

## If nothing arrives

1. **Open the run.** Every run in the routine's **History** opens as a full session. Search the transcript for `api.pocketalert.app` and read curl's output.
2. **`403` with `host_not_allowed`.** The secret isn't on the environment this routine uses, or the host is misspelled. Check **Allowed websites**.
3. **`401`.** "No token provided" means no key reached the API: the secret isn't attached or the header name is misspelled. "token contains an invalid number of segments" means the header is still `Authorization`; change it to `Token`. "Invalid token" means the `Bearer` prefix is still there or the key was revoked.
4. **`400`.** The body isn't valid JSON, usually an unescaped quote from the summary. The hook version builds the JSON with `jq`, which avoids that.
5. **Hook never ran.** Check that `.claude/settings.json` is committed on the default branch, the script is executable, and the routine has only one repository.

## FAQ

### Are Claude routines and scheduled tasks the same thing?

Yes. Anthropic renamed routines to scheduled tasks; the web address, the `/routines` alias and the API keep the old name.

### Does the routine see my Pocket Alert API key?

Not with a network secret. The proxy adds the header after the request leaves the session, so the key is not in any environment variable or file Claude can read. With an environment variable, it is readable inside the session.

### Can a routine wake me up at night?

With `"level": "critical"` on a paid plan, yes. Reserve it for routines that watch production, like an API-triggered routine your monitoring fires on an error spike.

### What about local scheduling with /loop or Desktop scheduled tasks?

Those run on your machine, so local Claude Code hooks and MCP servers work as usual. The setup is in [Claude Code push notifications](/blog/claude-code-push-notifications). For more agent patterns, see [AI agent alerts](/use-cases/ai-agent-alerts).

[Create a free account](/signup), add the secret, and let tomorrow morning's routine tell you how it went.
