Monitor Temporal workflows: activity heartbeat and LastPing completion ping
In the @workflow.run method, wrap
execute_activity in try/except: on success execute a
dedicated ping activity that GETs the LastPing URL; on exception
execute a fail-ping activity that POSTs to /fail. Use
activity.heartbeat() to prevent Temporal from timing out
long activities. Absence detection covers a down worker. Free, no
monitor cap.
The snippets
Create a monitor in
the console, set the
schedule to match your Temporal cron_schedule plus a grace
window, and add the ping activities below.
Python SDK — ping as a dedicated activity
Temporal replays workflow code deterministically, so HTTP calls
must go inside activities, not directly in the workflow body.
A small ping_lastping activity is the correct
pattern:
Registering the workflow with a cron schedule
Pass cron_schedule to start_workflow so
Temporal re-runs the workflow on the given cron expression. Each
run is independent — the workflow function starts fresh each time:
activity.heartbeat() vs. LastPing ping — two different things
activity.heartbeat() is an internal keepalive that
prevents Temporal from declaring the activity timed out.
A LastPing ping is an external signal to your team that the
workflow completed on schedule. Both are needed for long-running
workflows: heartbeats keep the activity alive; LastPing tells you
when it's done. See also the
Airflow guide for a DAG-level
comparison and the LastPing MCP server for
AI-agent provisioning.
Grace window alignment
Set the LastPing grace window to at least
schedule_to_close_timeout + retry_budget so
Temporal's built-in retries don't open a spurious incident. Only
the final failure (after Temporal exhausts retries) should trigger
the /fail ping from the workflow's except block.
What this catches
The activities report what they can; the silence reports the rest.
- Workflow exception after retries — the except block executes the fail-ping activity, opening an incident immediately with the error text.
- Successful workflow completion — the ping activity fires after the last business activity, resetting the absence timer.
- Worker is down — no activity is polled; the workflow stays scheduled but never runs; the missing ping opens the incident after the grace window.
- Activity timeout — Temporal marks the activity as timed out and the workflow's except block fires the fail-ping activity.
- Recovery — the next successful workflow run fires the success ping activity, closing the incident automatically.
Questions people ask
Front-loaded answers — the most important fact first.
-
Can I call fetch/httpx directly in the workflow body?
No. Temporal replays workflow code deterministically; side effects including HTTP calls must go inside activities. Wrap the ping in a dedicated
@activity.defnfunction and call it withworkflow.execute_activity. -
What is the difference between activity.heartbeat and a LastPing ping?
activity.heartbeat()is an internal keepalive to Temporal's server — it tells Temporal the activity is still running. A LastPing ping is an external completion signal to your team. Both are useful for long-running workflows: one prevents Temporal timeouts, the other prevents silence going unnoticed. -
What if the Temporal worker process is killed mid-activity?
Temporal schedules the activity for retry on another worker. If no worker is available (all workers are down), the activity stays in the task queue and no success ping fires. Absence detection opens the incident after the grace window.
-
Is this free?
Yes. LastPing is free and fully hosted with no monitor cap and no paid tier. See how it compares to Healthchecks.io and Cronitor.
Temporal retries silently. LastPing tells you when they're exhausted.
One ping activity now beats debugging a failed nightly workflow later. First alert wired up in about a minute — and LastPing monitors itself the same way.