Veeramanikandan Shanmugam
08/12/2025, 10:20 AMMarvin
08/12/2025, 10:20 AMMarvin
08/12/2025, 10:23 AM{
"source_flow_run_gri": "{{ event.resource.id }}",
"source_flow_run_id": "{{ event.resource.id | replace('prefect.flow-run.', '') }}",
"source_flow_run_name": "{{ event.payload.flow_run_name }}",
"source_flow_name": "{{ event.payload.flow_name }}",
"source_state": "{{ event.payload.state }}",
"source_state_type": "{{ event.payload.state_type }}",
"source_tags": {{ event.payload.tags | tojson }}
}
Tip: If you don’t want to guess fields, just pass the whole event and parse it in your tracking flow:
{ "trigger_event": {{ event | tojson }} }
Then in your tracking flow:
from typing import Any, Dict
from prefect import flow
@flow
def track_scheduler(
source_flow_run_id: str | None = None,
source_flow_run_name: str | None = None,
source_flow_name: str | None = None,
source_state: str | None = None,
source_state_type: str | None = None,
source_tags: list[str] | None = None,
trigger_event: Dict[str, Any] | None = None,
):
# do your tracking here
...
Notes
- event.resource.id
is the GRI (e.g., prefect.flow-run.<uuid>
). Strip the prefix if you want just the UUID (as shown with replace
).
- Use the Automation “Test”/“Preview” to verify the templated values.
- This assumes Prefect 3.x. If you’re on 2.x, the approach is the same, but you should really consider upgrading.
If you want, tell me which specific fields you need and I’ll give you a minimal parameter set.Veeramanikandan Shanmugam
08/12/2025, 10:35 AMVeeramanikandan Shanmugam
08/12/2025, 10:35 AMMarvin
08/12/2025, 10:35 AMMarvin
08/12/2025, 10:38 AMprefect.tags(...)
) are applied at runtime, after the flow run is already created. That’s why your auto-scheduled runs don’t show them while they’re Scheduled/Pending.
To have tags on every flow run at creation time (including scheduled ones), set tags on the deployment or on the run request:
Options
- Deployment-level tags (recommended)
- In Python when creating the deployment:
from prefect import flow
@flow
def my_flow():
...
my_flow.from_source(
"<https://github.com/org/repo>",
entrypoint="flows/my_flow.py:my_flow",
).deploy(
name="prod",
work_pool_name="my-pool",
tags=["scheduler", "prod"]
)
- Or in prefect.yaml:
deployments:
- name: prod
flow_name: my_flow
entrypoint: flows/my_flow.py:my_flow
work_pool: { name: my-pool }
tags: ["scheduler", "prod"]
- When triggering runs programmatically:
from prefect.deployments import run_deployment
run_deployment("my-flow/prod", tags=["scheduler"])
- In Automations “Run deployment” action:
- Set the “Tags” field to ["scheduler"]
(you can also template it if you like).
Summary: put the tag on the deployment (or the run request), not just in-flow code, and it will appear on all flow runs, including auto-scheduled ones. If you share your current deployment config, I’ll point to exactly where to add tags
.Veeramanikandan Shanmugam
08/12/2025, 11:12 AMMarvin
08/12/2025, 11:12 AMMarvin
08/12/2025, 11:14 AMprefect.tag
(ids like prefect.tag.scheduler
). If you’re hitting the API directly, use those resource ids.
- If your UI doesn’t show an “Any of” operator, use a Composite trigger with two sub-triggers (one for each tag) and set it to “Any”/OR. Or, in a pinch, create two identical automations—one per tag.
- Make sure your runs actually have the tags at creation time (set tags on the deployment or in the “Run deployment” action), otherwise you’ll be filtering on vibes.Veeramanikandan Shanmugam
08/13/2025, 5:28 AMMarvin
08/13/2025, 5:28 AMMarvin
08/13/2025, 5:34 AM{
"name": "Run deployment when tag is scheduler or alerts",
"description": "Listens for any flow-run event that has tags scheduler or alerts and runs a helper deployment.",
"enabled": true,
"posture": "Reactive",
"trigger": {
"type": "event",
"match": {
"all": [
{
"resource": {
"role": "flow-run"
}
},
{
"related": {
"any": [
{
"id": "prefect.tag.scheduler"
},
{
"id": "prefect.tag.alerts"
}
]
}
}
]
}
},
"actions": [
{
"type": "run-deployment",
"deployment_id": "<YOUR_DEPLOYMENT_ID>",
"parameters": {
"trigger_event": "{{ event }}",
"trigger_event_name": "{{ event.event }}",
"triggered_flow_run_resource_id": "{{ event.resource.id }}"
}
}
]
}
Notes
- The trigger filters:
- resource.role = flow-run scops this to flow-run events.
- related.any for prefect.tag.scheduler or prefect.tag.alerts matches runs that carry either tag.
- The action parameters use templating:
- {{ event }}
injects the full event object as JSON into your downstream flow’s parameters.
- You can also cherry-pick fields, e.g. {{ event.resource.id }}
or {{ event.event }}
.
- If you want to restrict to specific event names (e.g., only state changes), add an EventName filter into match (commonly a prefix match like prefect.flow-run.)—but the above is enough for “any flow-run event with those tags”.
cURL request (Cloud example)
curl -X POST \
"<https://api.prefect.cloud/api/accounts/<ACCOUNT_ID>/workspaces/<WORKSPACE_ID>/automations/automations>" \
-H "Authorization: Bearer <PREFECT_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"name": "Run deployment when tag is scheduler or alerts",
"description": "Listens for any flow-run event that has tags scheduler or alerts and runs a helper deployment.",
"enabled": true,
"posture": "Reactive",
"trigger": {
"type": "event",
"match": {
"all": [
{ "resource": { "role": "flow-run" } },
{ "related": { "any": [
{ "id": "prefect.tag.scheduler" },
{ "id": "prefect.tag.alerts" }
]}}
]
}
},
"actions": [
{
"type": "run-deployment",
"deployment_id": "<YOUR_DEPLOYMENT_ID>",
"parameters": {
"trigger_event": "{{ event }}",
"trigger_event_name": "{{ event.event }}",
"triggered_flow_run_resource_id": "{{ event.resource.id }}"
}
}
]
}'
Downstream flow example (accept the event as a parameter)
from prefect import flow, get_run_logger
@flow
def handle_trigger(trigger_event: dict, trigger_event_name: str = "", triggered_flow_run_resource_id: str = ""):
logger = get_run_logger()
<http://logger.info|logger.info>(f"Triggered by event: {trigger_event_name}")
<http://logger.info|logger.info>(f"Source resource: {triggered_flow_run_resource_id}")
# Full event is available if you passed `trigger_event`
<http://logger.info|logger.info>(f"Full event keys: {list(trigger_event.keys())}")
Common variationsMarvin
08/13/2025, 5:34 AM"deployment_name": "flow_name/deployment_name"
in place of deployment_id
. If your API rejects that, stick to deployment_id.
- If you’d rather filter tags via the flow-run’s labels directly, you can use a resource-attribute filter; however, treating tags as related resources (prefect.tag.<name>) is the most robust.
Want me to fill your actual deployment_id and post it for you, or are you on Cloud vs. Server? I can tailor the exact request if you share those. I’ll try not to sound too excited.Veeramanikandan Shanmugam
08/13/2025, 5:45 AMMarvin
08/13/2025, 5:46 AMMarvin
08/13/2025, 5:47 AM{
"name": "<NAME>",
"description": "<DESCRIPTION>",
"enabled": true,
"posture": "Reactive",
"trigger": {
"type": "event",
"match": {
"prefect.resource.id": "prefect.flow-run.*"
},
"match_related": {
"prefect.resource.role": "tag",
"prefect.resource.id": [
"prefect.tag.scheduler",
"prefect.tag.alerts"
]
},
"expect": [
"prefect.flow-run.*"
],
"for_each": [
"prefect.resource.id"
],
"threshold": 1,
"within": 0
},
"actions": [
{
"type": "run-deployment",
"source": "selected",
"deployment_id": "<DEPLOYMENT_ID>",
"parameters": {
"trigger_event_name": "{{ event.event }}",
"triggered_flow_run_id": "{{ event.resource.id }}",
"trigger_event_json": "{{ event }}"
},
"job_variables": null
}
]
}
Downstream flow options
- If you only need a few fields:
from prefect import flow, get_run_logger
@flow
def downstream(trigger_event_name: str, triggered_flow_run_id: str):
logger = get_run_logger()
<http://logger.info|logger.info>(f"Triggered by: {trigger_event_name} from {triggered_flow_run_id}")
- If you want the full event:
import json
from prefect import flow, get_run_logger
@flow
def downstream(trigger_event_json: str):
logger = get_run_logger()
evt = json.loads(trigger_event_json)
<http://logger.info|logger.info>(f"Event name: {evt.get('event')}")
<http://logger.info|logger.info>(f"Source resource: {evt.get('resource', {}).get('id')}")
Why your previous run didn’t start
- If your deployment’s flow does not accept a parameter named trigger_event (or expects a dict, not a str), the action validation/flow-run creation will fail and the automation won’t launch a run. Pass strings or adjust the flow signature to match exactly what you send.
Sanity checks
- Put posture at the top-level (Reactive). The server expects posture on the automation, not inside the trigger.
- Confirm your deployment_id is correct and the worker/work pool can pick it up.
- Check the Automation Activity in the UI for any “action failed” messages—type/parameter validation errors will show there.
If you want, paste the exact error shown in the Automation activity/logs and your downstream flow signature. I’ll align the parameters precisely so it actually runs instead of silently sulking.Veeramanikandan Shanmugam
08/13/2025, 5:50 AMMarvin
08/13/2025, 5:50 AMMarvin
08/13/2025, 5:51 AM"actions": [
{
"type": "run-deployment",
"source": "selected",
"deployment_id": "<DEPLOYMENT_ID>",
"parameters": {
"trigger_event": "{{ event }}"
}
}
]
Downstream flow
from typing import Any, Dict
from prefect import flow, get_run_logger
@flow
def downstream(trigger_event: Dict[str, Any]):
logger = get_run_logger()
<http://logger.info|logger.info>(f"Event name: {trigger_event.get('event')}")
<http://logger.info|logger.info>(f"Source resource: {trigger_event.get('resource', {}).get('id')}")
B) Pass the event as a JSON string
- Prefect’s templating is not Jinja; there’s no tojson filter.
- If you absolutely need a string, accept a string in your flow and json.loads it. Prefect will usually still pass a dict when the placeholder is the entire value, so this is less reliable than A. The robust approach is to accept a dict and call json.dumps inside your flow if you need a string.
If you insist on a string param anyway:
"parameters": {
"trigger_event_json": "{{ event }}"
}
And in the flow:
import json
from typing import Any
from prefect import flow
@flow
def downstream(trigger_event_json: Any):
# If a dict arrives (likely), turn it into a string yourself when needed
if not isinstance(trigger_event_json, str):
trigger_event_json = json.dumps(trigger_event_json)
evt = json.loads(trigger_event_json)
...
Reminder: make sure your deployment’s parameter schema allows the new parameter name/type (redeploy if you changed the flow signature).
For your tag condition in match_related (OR for “scheduler” or “alerts”):
"match_related": {
"prefect.resource.role": "tag",
"prefect.resource.id": [
"prefect.tag.scheduler",
"prefect.tag.alerts"
]
}
If it still doesn’t run when parameters are present, share the Automation Activity error and your flow signature; it’s usually a schema/type mismatch sulking in the corner.Veeramanikandan Shanmugam
08/13/2025, 6:03 AMMarvin
08/13/2025, 6:03 AMMarvin
08/13/2025, 6:04 AM{{ event }}
is a Pydantic Event model. The templater can’t serialize it as JSON, so it falls back to str(event) and sends a giant string. Your flow expects a dict, so Pydantic complains.
Fixes
Option A — Pass only JSON-native pieces of the event
Use fields that are already JSON-serializable (strings, numbers, dicts). In particular:
- event.id
, event.event
, event.occurred
, event.received
are strings/ISO datetimes
- event.payload
is a plain dict
- event.resource.root
is a plain dict
- Avoid passing event
or event.resource
directly (they’re models → get stringified)
Automation payload snippet
"trigger": {
"type": "event",
"match": {"prefect.resource.id": "prefect.flow-run.*"},
"match_related": {
"prefect.resource.role": "tag",
"prefect.resource.id": [
"prefect.tag.scheduler",
"prefect.tag.alerts"
]
},
"expect": ["prefect.flow-run.*"],
"for_each": ["prefect.resource.id"],
"threshold": 1,
"within": 0
},
"actions": [
{
"type": "run-deployment",
"source": "selected",
"deployment_id": "<DEPLOYMENT_ID>",
"parameters": {
"trigger_event": {
"id": "{{ event.id }}",
"name": "{{ event.event }}",
"occurred": "{{ event.occurred }}",
"received": "{{ event.received }}",
"resource": "{{ event.resource.root }}",
"payload": "{{ event.payload }}"
},
"triggered_flow_run_id": "{{ event.resource.id }}"
}
}
]
Downstream flow
from typing import Any, Dict
from prefect import flow, get_run_logger
@flow
def scheduler_sync_flow(trigger_event: Dict[str, Any], triggered_flow_run_id: str):
logger = get_run_logger()
<http://logger.info|logger.info>(f"Event {trigger_event['id']} ({trigger_event['name']}) "
f"for flow-run {triggered_flow_run_id}")
# trigger_event["resource"] and trigger_event["payload"] are dicts
Option B — Pass only the event ID and fetch inside your flow
This avoids all templating serialization weirdness.
"parameters": {
"trigger_event_id": "{{ event.id }}"
}
Then in your flow, call the Events API to read the event by id and get the full JSON. It’s one extra hop, but far more robust than shoving complex objects through the template renderer.
Why your original failed
- The value for trigger_event
was a string (repr of the Event), not a dict. Pydantic in your flow correctly said “Input should be a valid dictionary”. Using event.payload
and event.resource.root
keeps everything JSON-native so Prefect can pass it as a real dict.
Extra: your tag OR condition in match_related
"match_related": {
"prefect.resource.role": "tag",
"prefect.resource.id": [
"prefect.tag.scheduler",
"prefect.tag.alerts"
]
}
If you still see coercion-to-string on those dict fields, tell me the exact Automation Activity “action failed” message and I’ll adjust the payload to the letter. I live for this. Well, “live” is a strong word.Veeramanikandan Shanmugam
08/13/2025, 6:07 AMMarvin
08/13/2025, 6:07 AMMarvin
08/13/2025, 6:09 AMMarvin
08/13/2025, 6:10 AM"parameters": {
"trigger_event": {
"id": "{{ event.id }}",
"name": "{{ event.event }}",
"occurred": "{{ event.occurred }}",
"received": "{{ event.received }}",
"resource": "{{ event.resource.root }}",
"payload": "{{ event.payload }}"
}
}
Flow signature
from typing import Any, Dict
from prefect import flow
@flow
def scheduler_sync_flow(trigger_event: Dict[str, Any]):
...
Option 2 — Pass event.id and fetch the full event inside your flow
- This avoids all templating serialization quirks.
Automation
"parameters": {
"trigger_event_id": "{{ event.id }}"
}
Flow snippet using the API to read the event by id
- In practice, most people instead pull the flow-run details via event.resource.id
(the flow run ID) because that endpoint is stable and first-class. But if you truly need the full event, fetch by id with your REST client of choice within the flow.
Option 3 — Use a Pydantic type in your flow (but only if you supply a proper dict)
- You can annotate your param as Prefect’s Event model:
- from prefect.events.schemas.events import Event
- @flow def f(trigger_event: Event): ...
- This only helps if the Automation sends a JSON object that matches the Event schema. {{ event }}
will not do that; it sends a string. If you build a dict from JSON-native fields (Option 1), Pydantic can parse it into Event
. You won’t be able to include non-JSON-native submodels like related
without manually constructing them.
Why you can’t send “the whole thing” as JSON
- Automations templating cannot call methods (model_dump
, dict
, etc.) and has no JSON filter. Prefect won’t automatically convert arbitrary Pydantic models in the template context to JSON. So either:
- Send fields that are already JSON-native, or
- Send an identifier and refetch.
Reminder: OR tag filter for your trigger
"match_related": {
"prefect.resource.role": "tag",
"prefect.resource.id": [
"prefect.tag.scheduler",
"prefect.tag.alerts"
]
}
If you share exactly which pieces of the event you need downstream (e.g., tags, state info, source deployment), I’ll give you a minimal parameter dict that keeps everything JSON-native so your flow parameter can stay typed as Dict[str, Any] or even as Event.Veeramanikandan Shanmugam
08/13/2025, 6:13 AMMarvin
08/13/2025, 6:13 AMMarvin
08/13/2025, 6:13 AMMarvin
08/13/2025, 6:14 AM"parameters": {
"trigger_event": {
"id": "{{ event.id }}",
"name": "{{ event.event }}",
"occurred": "{{ event.occurred }}",
"received": "{{ event.received }}",
"resource": "{{ event.resource.root }}",
"payload": "{{ event.payload }}"
}
}
Flow signature
from typing import Any, Dict
from prefect import flow
@flow
def scheduler_sync_flow(trigger_event: Dict[str, Any]):
...
Option 2 — Send just the event id and fetch the full event inside the flow
- Avoids all templating serialization quirks.
"parameters": { "trigger_event_id": "{{ event.id }}" }
Then in your flow, call your Prefect API to read the event by id and get the full JSON. If all you need is flow-run context, you can also just pass "{{ event.resource.id }}" (the flow-run id) and query the flow run instead.
Option 3 — Use Prefect’s Event model type in your flow, but only if you send a proper dict
- You can annotate your parameter as Prefect’s Event:
from prefect.events.schemas.events import Event
@flow
def scheduler_sync_flow(trigger_event: Event): ...
- This only helps if the automation actually sends a JSON object matching Event. Since you can’t dump the Pydantic model in the template, you’d still need to construct a JSON-native dict (Option 1). You cannot make the templater call model_dump()/tojson.
Bottom line
- That jinja/tojson construct is not valid in Automations action parameters.
- Either pass JSON-native pieces and let the flow receive a dict, or pass the event id and fetch the full event in the flow. If you truly need every last field, fetching by id is the robust path.Veeramanikandan Shanmugam
08/13/2025, 6:51 AMMarvin
08/13/2025, 6:51 AMMarvin
08/13/2025, 6:52 AM"trigger": {
"type": "event",
"match": { "prefect.resource.id": "prefect.flow-run.*" },
"match_related": {
"prefect.resource.role": "tag",
"prefect.resource.id": [
"prefect.tag.scheduler",
"prefect.tag.auto-scheduled",
"prefect.tag.alerts"
]
},
"expect": ["prefect.flow-run.*"],
"for_each": ["prefect.resource.id"],
"threshold": 1,
"within": 0
},
"posture": "Reactive"
B) Or scope by deployment/flow instead of tags
If your goal is “whenever this deployment’s runs happen,” skip tags and match the related deployment. This avoids surprises from system tags.
"trigger": {
"type": "event",
"match": { "prefect.resource.id": "prefect.flow-run.*" },
"match_related": {
"prefect.resource.role": "deployment",
"prefect.resource.id": "prefect.deployment.<YOUR_DEPLOYMENT_ID>"
},
"expect": ["prefect.flow-run.*"],
"for_each": ["prefect.resource.id"],
"threshold": 1,
"within": 0
}
You can also combine both criteria by AND-ing more fields in match_related if you really want tags plus deployment.
Extra: if you want every run to carry the scheduler tag regardless of how it’s started, add it in your flow:
from prefect import flow, tags
@flow
def my_flow(...):
with tags("scheduler"):
...
But the simplest path is to include auto-scheduled in your tag OR list or filter by deployment instead of tags.
If you paste your current trigger block, I’ll rewrite it precisely so scheduled and quick runs both fire the automation. I’ll even pretend to enjoy it.Veeramanikandan Shanmugam
08/13/2025, 6:52 AMVeeramanikandan Shanmugam
08/13/2025, 6:52 AMMarvin
08/13/2025, 6:52 AMMarvin
08/13/2025, 6:54 AM"posture": "Reactive",
"trigger": {
"type": "event",
"match": { "prefect.resource.id": "prefect.flow-run.*" },
"match_related": {
"any": [
{
"prefect.resource.role": "tag",
"prefect.resource.id": "prefect.tag.scheduler"
}
]
},
"expect": ["prefect.flow-run.*"],
"for_each": ["prefect.resource.id"],
"threshold": 1,
"within": 0
}
This matches any flow-run event that has a related tag resource equal to prefect.tag.scheduler, even when there are additional tags like auto-scheduled.
If you truly want “scheduler OR alerts”, just add the second entry:
"match_related": {
"any": [
{ "prefect.resource.role": "tag", "prefect.resource.id": "prefect.tag.scheduler" },
{ "prefect.resource.role": "tag", "prefect.resource.id": "prefect.tag.alerts" }
]
}
2) Ensure the scheduled runs actually include the scheduler tag
Quick runs can add a run tag on the fly. Scheduled runs use the deployment’s configuration. If the deployment doesn’t specify scheduler as a tag, the auto-scheduled run may not have it — and your automation won’t fire.
- Add scheduler to your deployment’s tags so every run (scheduled or manual) carries it.
- You can do this in the UI (Edit deployment → Tags) or via API by updating the deployment’s tags to include ["scheduler"].
- Alternatively, add the tag in your flow code:
from prefect import flow, tags
@flow
def my_flow(...):
with tags("scheduler"):
...
That guarantees the tag on all runs.
Bonus: fix your action parameters to be JSON-native to avoid sporadic action failures
You’re currently passing models:
- Use resource.root and payload (dict) instead of resource and related (models):
"parameters": {
"trigger_event": {
"name": "{{ event.event }}",
"occurred": "{{ event.occurred }}",
"received": "{{ event.received }}",
"resource": "{{ event.resource.root }}",
"payload": "{{ event.payload }}"
}
}
Passing event.related directly is a list of models; the templater can stringify it at the worst possible time. If you need specific related items (tags, deployment), pass them individually.
Sanity checks
- Put "posture": "Reactive" at the top level (not inside trigger).
- Inspect a sample auto-scheduled event in the UI Events view and confirm you actually see a related resource with id prefect.tag.scheduler. If it’s missing, the deployment doesn’t propagate that tag and the filter will (correctly) skip it.
If you share the deployment ID, I can give you the exact PATCH payload to set its tags to ["scheduler"]. Yes, I know, thrilling.Veeramanikandan Shanmugam
08/13/2025, 7:04 AMMarvin
08/13/2025, 7:04 AMMarvin
08/13/2025, 7:05 AM"posture": "Reactive",
"trigger": {
"type": "event",
"match": { "prefect.resource.id": "prefect.flow-run.*" },
"match_related": {
"prefect.resource.role": "tag",
"prefect.resource.id": "prefect.tag.scheduler"
},
"expect": ["prefect.flow-run.*"],
"for_each": ["prefect.resource.id"],
"threshold": 1,
"within": 0
}
Full payload example (with safer, JSON-native params)
{
"name": "<NAME>",
"description": "<DESCRIPTION>",
"enabled": true,
"posture": "Reactive",
"trigger": {
"type": "event",
"match": {
"prefect.resource.id": "prefect.flow-run.*"
},
"match_related": {
"prefect.resource.role": "tag",
"prefect.resource.id": "prefect.tag.scheduler"
},
"expect": [
"prefect.flow-run.*"
],
"for_each": [
"prefect.resource.id"
],
"threshold": 1,
"within": 0
},
"actions": [
{
"type": "run-deployment",
"source": "selected",
"deployment_id": "<DEPLOYMENT_ID>",
"parameters": {
"trigger_event": {
"id": "{{ event.id }}",
"name": "{{ event.event }}",
"occurred": "{{ event.occurred }}",
"resource": "{{ event.resource.root }}",
"payload": "{{ event.payload }}"
}
}
}
]
}
Notes
- Using a single string for prefect.resource.id under match_related means “must have this tag among its related resources.” Extra tags don’t matter.
- If you ever want OR for multiple tags, provide a list:
"prefect.resource.id": [
"prefect.tag.scheduler",
"prefect.tag.alerts"
]
- Don’t pass models in parameters. Use resource.root and payload; they’re plain dicts and won’t get stringified by the templater.
If this still doesn’t fire on scheduled runs, confirm the scheduled events actually include a related resource with id prefect.tag.scheduler. If not, add the tag to the deployment so all runs inherit it.Veeramanikandan Shanmugam
08/13/2025, 7:10 AMMarvin
08/13/2025, 7:10 AMMarvin
08/13/2025, 7:11 AM"posture": "Reactive",
"trigger": {
"type": "event",
"match": { "prefect.resource.id": "prefect.flow-run.*" },
"match_related": {
"any": [
{
"prefect.resource.role": "tag",
"prefect.resource.id": "prefect.tag.scheduler"
}
]
},
"expect": ["prefect.flow-run.*"],
"for_each": ["prefect.resource.id"],
"threshold": 1,
"within": 0
}
If you also want alerts to count, add another entry:
"match_related": {
"any": [
{ "prefect.resource.role": "tag", "prefect.resource.id": "prefect.tag.scheduler" },
{ "prefect.resource.role": "tag", "prefect.resource.id": "prefect.tag.alerts" }
]
}
Two more sanity checks while we’re here:
- Put posture at the top level (as shown). Some payloads put it inside trigger, which the API ignores politely.
- In your action parameters, avoid passing Pydantic models (e.g., event.resource, event.related). Use JSON-native fields instead:
"parameters": {
"trigger_event": {
"id": "{{ event.id }}",
"name": "{{ event.event }}",
"occurred": "{{ event.occurred }}",
"resource": "{{ event.resource.root }}",
"payload": "{{ event.payload }}"
}
}
If it still doesn’t fire on scheduled runs, open the event in the UI and confirm you actually see a related resource with id prefect.tag.scheduler. If that tag isn’t there, the filter won’t match no matter how sternly we look at it.Veeramanikandan Shanmugam
08/13/2025, 7:29 AMMarvin
08/13/2025, 7:29 AMMarvin
08/13/2025, 7:31 AM{
"name": "<NAME>",
"description": "<DESCRIPTION>",
"enabled": true,
"posture": "Reactive",
"trigger": {
"type": "event",
"match": {
"prefect.resource.id": "prefect.flow-run.*"
},
"match_related": {
"any": [
{
"prefect.resource.role": "tag",
"prefect.resource.id": "prefect.tag.scheduler"
}
]
},
"expect": [
"prefect.flow-run.*"
],
"for_each": [
"prefect.resource.id"
],
"threshold": 1,
"within": 0
},
"actions": [
{
"type": "run-deployment",
"source": "selected",
"deployment_id": "<DEPLOYMENT_ID>",
"parameters": {
"trigger_event": {
"name": "{{ event.event }}",
"occurred": "{{ event.occurred }}",
"received": "{{ event.received }}",
"resource": "{{ event.resource.root }}",
"payload": "{{ event.payload }}"
},
"flow_run_id": "{{ event.resource.id }}"
}
}
]
}
Why this fixes it
- match_related.any ensures the condition is true if any related resource matches (i.e., one of the tags is scheduler), regardless of additional tags like auto-scheduled or alerts.
- event.resource.root and event.payload are JSON-native. Passing event.resource or event.related can turn into strings and cause parameter validation failures that look like “the automation didn’t trigger.”
If your scheduled runs sometimes don’t include the scheduler tag at all, add it to the deployment’s tags so every run inherits it, or broaden the filter:
"match_related": {
"any": [
{ "prefect.resource.role": "tag", "prefect.resource.id": "prefect.tag.scheduler" },
{ "prefect.resource.role": "tag", "prefect.resource.id": "prefect.tag.auto-scheduled" }
]
}
If it still refuses to fire, check the Automation Activity for “action failed” errors—matching may be fine, but the parameters could be sulking.Veeramanikandan Shanmugam
08/13/2025, 7:32 AMMarvin
08/13/2025, 7:32 AMMarvin
08/13/2025, 7:33 AM"match_related": {
"prefect.resource.role": "tag",
"prefect.resource.id": "prefect.tag.scheduler"
}
- Explicit OR form (future‑proof, and what I’d use)
"match_related": {
"any": [
{
"prefect.resource.role": "tag",
"prefect.resource.id": "prefect.tag.scheduler"
}
]
}
- Multiple tags OR in one go (scheduler OR alerts)
"match_related": {
"any": [
{ "prefect.resource.role": "tag", "prefect.resource.id": "prefect.tag.scheduler" },
{ "prefect.resource.role": "tag", "prefect.resource.id": "prefect.tag.alerts" }
]
}
Note: Some servers will also accept a list for a field value (e.g., "prefect.resource.id": ["prefect.tag.scheduler", "prefect.tag.alerts"]) inside the object, but using the any wrapper avoids compatibility surprises.
And, as ever, keep posture at the top level:
"posture": "Reactive"