← Back to changelog
Mar 24, 2026

Stateless Live Activities: Stream Updates Without Managing activity_id

Stateless Live Activity showing CPU usage on iPhone

Live Activities now support a stateless stream mode.

I built this because while working on a new Live Activity type, I ran into a real limitation: lifecycle state management is fine in systems that already have state, but it becomes awkward in runtimes that wake up, do one thing, and exit.

GitHub Actions is a good example of where the original lifecycle still fits well. A workflow can start a Live Activity, keep the returned activity_id across steps, send updates, and end it when the run finishes.

A cron job is different. Each run can measure the current state of a process, but it does not naturally remember the activity_id from the previous run. You can bolt on a file, Redis key, or database row to persist it, but that means the integration is doing more state management than the job itself.

Stateless Live Activities remove that overhead.

What's new

  • New PUT /live-activity/stream/{stream_key} endpoint for stateless start-or-update behavior.
  • New DELETE /live-activity/stream/{stream_key} endpoint to end the tracked Live Activity when the process is over.
  • ActivitySmith manages the lifecycle behind a stable stream_key, so the caller does not need to persist activity_id.
  • This is a good fit for cron jobs, scheduled scripts, background workers, and polling loops.

How it works

Pick one stable stream_key for one thing you are tracking.

Examples:

  • prod-web-1
  • deployment-main
  • nightly-backup
  • ev-charging

Use one stream_key for one system, workflow, or process.

If no Live Activity exists yet for that key, ActivitySmith starts one. If one already exists, ActivitySmith updates it. If the current Live Activity needs to rotate, ActivitySmith handles that too.

The caller only sends the latest state.

Example: monitor PM2 process from cron

API Health progress Live Activity on iPhone showing PM2 CPU usage

In this example, a cron job runs on a VPS, reads the CPU usage of one PM2 process by ID, maps that value to a progress Live Activity, and sends it with the same stream_key every time.

The important part is what the script does not do:

  • it does not load an activity_id
  • it does not save lifecycle state anywhere
  • it just sends the latest data
#!/bin/bash
set -euo pipefail
API_BASE_URL="https://activitysmith.com/api"
API_KEY="YOUR_API_KEY"
STREAM_KEY="pm2-activitysmith-api-cpu"
PM2_PROCESS_ID="0"
cpu_raw=$(pm2 jlist | jq -r --arg id "$PM2_PROCESS_ID" '
map(select((.pm2_env.pm_id | tostring) == $id)) | .[0].monit.cpu
')
if [ -z "$cpu_raw" ] || [ "$cpu_raw" = "null" ]; then
exit 1
fi
cpu=$(printf "%.0f" "$cpu_raw")
if [ "$cpu" -gt 100 ]; then
cpu=100
fi
if [ "$cpu" -ge 80 ]; then
color="red"
elif [ "$cpu" -ge 40 ]; then
color="yellow"
else
color="green"
fi
payload=$(cat <<EOF
{
"content_state": {
"title": "API Health",
"subtitle": "PM2 activitysmith-api CPU",
"type": "progress",
"percentage": $cpu,
"color": "$color"
}
}
EOF
)
curl --fail --silent --show-error \
-X PUT "$API_BASE_URL/live-activity/stream/$STREAM_KEY" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d "$payload"

Then let cron run it every minute:

* * * * * /opt/monitoring/pm2-cpu-live-activity.sh

One run might send a green update at normal CPU usage.

API Health progress Live Activity on iPhone showing PM2 CPU usage spike

A later run can spike the same Live Activity to yellow.

API Health progress Live Activity on iPhone showing PM2 CPU usage after it settles

That is the whole point of stream mode: each execution is stateless, but the Live Activity stays continuous on the device.

When to use which model

Use stream mode when the caller should only send the latest state and move on.

Good fits:

  • cron jobs
  • scheduled health checks
  • queue depth samplers
  • background workers
  • scripts that run independently and exit

Keep the original start/update/end lifecycle when the caller already has a natural place to keep state.

Good fits:

Ending the stream

When the tracked process is over and you no longer want the Live Activity on devices, end the stream explicitly:

curl --fail --silent --show-error \
-X DELETE "https://activitysmith.com/api/live-activity/stream/pm2-activitysmith-api-cpu" \
-H "Authorization: Bearer YOUR_API_KEY"

If you later send another PUT request with the same stream_key, ActivitySmith starts a new Live Activity for that stream again.

Availability

Available now across the API, SDKs, CLI, and GitHub Action.

This feature is available in the ActivitySmith iOS app version 1.5.0 and newer.