Jonah Duffin
11/12/2025, 2:26 PMMarvin
11/12/2025, 2:26 PMMarvin
11/12/2025, 2:34 PMcreated_by field with id, type, and display_value. In Prefect Cloud, display_value is typically the user’s email when a human manually starts a run from the UI. For runs started by schedules, automations, or service accounts, it will reflect those instead (not a user email).
Here’s how to get it inside your flow:
Async flow example
from prefect import flow
from prefect.runtime import flow_run as runtime_flow_run
from prefect.client.orchestration import get_client
@flow
async def my_flow():
# Prefer the root run’s creator in case this is a subflow
flow_run_id = runtime_flow_run.root_flow_run_id() or runtime_flow_run.id()
async with get_client() as client:
fr = await client.read_flow_run(flow_run_id)
creator = getattr(fr, "created_by", None)
email = None
if creator and getattr(creator, "display_value", None):
# For user-initiated runs in Cloud, this is usually the email
dv = creator.display_value
if "@" in dv:
email = dv
# Use `email` if present to send your summary
return {"creator": creator, "email": email}
Sync flow example
from prefect import flow
from prefect.runtime import flow_run as runtime_flow_run
from prefect.client.orchestration import SyncPrefectClient
@flow
def my_flow():
flow_run_id = runtime_flow_run.root_flow_run_id() or runtime_flow_run.id()
with SyncPrefectClient() as client:
fr = client.read_flow_run(flow_run_id)
creator = getattr(fr, "created_by", None)
email = None
if creator and getattr(creator, "display_value", None):
dv = creator.display_value
if "@" in dv:
email = dv
return {"creator": creator, "email": email}
Notes and limitations
- Manual runs: For a run started by a logged-in Cloud user via the UI, created_by.display_value is typically that user’s email.
- Non-user triggers: For schedules, automations, or service accounts, created_by.type will reflect those and display_value won’t be a user email.
- Subflows: A subflow’s own created_by may be “system”; use root_flow_run_id() to fetch the top-level run’s created_by as shown above.
- Robustness: Since display_value is not guaranteed to be an email, a more deterministic approach is to pass a notify_email parameter when manually triggering the run in the UI and use that inside the flow.
Helpful links
- Access runtime info: Access runtime info
- Python client usage: API client guide
- REST API: read a flow run (includes created_by): GET /api/flow-runs/{id}
If you confirm how your runs are typically triggered (manual vs schedule/automation), I can tailor a small snippet to populate recipients and send the email right in the flow.