<@ULVA73B9P> I tried get using `/api/users/{id}` ...
# ask-marvin
l
@Marvin I tried get using
/api/users/{id}
, but this api only return the api key user information only if i give any other user id it returns 404 not found error
m
thought for 117.8 seconds
Thanks for reporting this — what you’re seeing is expected. In Prefect Cloud, the Users API is scoped to the authenticated principal. A GET to
/api/users/{id}
will return your own user record if
{id}
matches the user tied to your API key; requests for any other user ID are not exposed and will return 404. This is by design for privacy/permissions. If your goal is to see who’s in an account or workspace (and their roles), use the memberships endpoints instead (e.g., list account/workspace members). Those return the set of users you’re authorized to view along with role info. Can you share: - Which environment you’re calling (base URL)? - What you’re trying to accomplish (e.g., list all users in a workspace, fetch a specific teammate’s email/role, etc.)? With that, I can point you to the exact endpoint and a curl example for your case.
l
@Marvin Our use case, we want to see the user email address who runs the flow using flow_run_info created_by.id or user handle? Could you give me a complete Python script for this?
m
thought for 216.8 seconds
Great question. In Prefect 3.x, a flow run’s creator is exposed on the flow run object as
created_by
with fields like
type
,
id
, and
display_value
. For user-triggered runs,
display_value
typically includes the user’s name and/or email. For scheduled/automation/service-account runs, there may not be a user email. Below is a complete script that: - Resolves a flow run ID from a CLI flag, env var, or the runtime context (if run inside a flow) - Fetches the flow run - Prints the best-available creator info and extracts an email from
display_value
when present Notes: - For Prefect Cloud, set
PREFECT_API_URL
and
PREFECT_API_KEY
in your environment before running. - For Prefect Server, set
PREFECT_API_URL
only.
Copy code
import argparse
import asyncio
import os
import re
import sys

from prefect.client.orchestration import get_client

# We will try to use the runtime flow run id if the script runs inside a flow
def get_runtime_flow_run_id():
    try:
        from prefect.runtime import flow_run as runtime_flow_run
        return runtime_flow_run.id()
    except Exception:
        return None

EMAIL_RE = re.compile(r'[\w\.-]+@[\w\.-]+\.\w+')

async def get_creator_info(flow_run_id: str):
    async with get_client() as client:
        flow_run = await client.read_flow_run(flow_run_id)

    created_by = getattr(flow_run, "created_by", None)
    if not created_by:
        return {
            "flow_run_id": flow_run_id,
            "creator_type": None,
            "creator_id": None,
            "display_value": None,
            "email": None,
        }

    display_value = getattr(created_by, "display_value", None)
    email = None
    if display_value:
        m = EMAIL_RE.search(display_value)
        if m:
            email = m.group(0)

    return {
        "flow_run_id": flow_run_id,
        "creator_type": getattr(created_by, "type", None),
        "creator_id": str(getattr(created_by, "id", None)),
        "display_value": display_value,
        "email": email,
    }

def main():
    parser = argparse.ArgumentParser(description="Show who created a Prefect flow run.")
    parser.add_argument("--flow-run-id", help="Flow run ID to inspect (optional).")
    args = parser.parse_args()

    flow_run_id = (
        args.flow_run_id
        or os.environ.get("FLOW_RUN_ID")
        or get_runtime_flow_run_id()
    )

    if not flow_run_id:
        print(
            "No flow run ID found. Provide one with:\n"
            "  --flow-run-id <ID>\n"
            "or set FLOW_RUN_ID env var, or run this inside a Prefect flow."
        )
        sys.exit(2)

    info = asyncio.run(get_creator_info(flow_run_id))

    print("Flow Run:", info["flow_run_id"])
    print("Created By Type:", info["creator_type"])
    print("Created By ID:", info["creator_id"])
    print("Display Value:", info["display_value"])
    print("Email (best effort):", info["email"])

if __name__ == "__main__":
    main()
What to expect: - If a human triggered the run,
display_value
usually contains their name/email and the regex will extract the email. - If a service account, automation, or the system created the run, there may be no email — the script will show the type and display value. - Directly querying another user’s full profile from their ID is not exposed via the public API for privacy reasons; relying on
created_by.display_value
is the supported path.