<@ULVA73B9P> I am creating an automation that send...
# ask-marvin
b
@Marvin I am creating an automation that sends an MS Teams notification upon flow run completion. I want the Teams message body to include a table with 2 columns for a key : value output. Give me an example of that
m
thought for 200.2 seconds
Here are two straightforward ways to get a 2‑column “key : value” look in a Microsoft Teams notification sent by a Prefect 3 automation. Option A — Simple “table” look using pipes in the body Paste this into your Automation action body. It renders as a readable two‑column list in most Teams clients. Note: Teams Adaptive Cards don’t support true Markdown tables; this will show as plain text with pipes.
Copy code
Flow run completed ✔️

| Key       | Value |
|---------- |-------|
| Flow      | {{ flow.name }}
| Run Name  | {{ flow_run.name }}
| Run ID    | {{ flow_run.id }}
| State     | {{ flow_run.state.name }}
| Message   | {{ flow_run.state.message }}
| Start     | {{ flow_run.start_time }}
| End       | {{ flow_run.end_time }}
| Link      | {{ flow_run|ui_url }}
Option B — Clean key:value list (most reliable) This is the most consistent across Teams clients:
Copy code
Flow run completed ✔️

• *Flow:* {{ flow.name }}
• *Run Name:* {{ flow_run.name }}
• *Run ID:* {{ flow_run.id }}
• *State:* {{ flow_run.state.name }}
• *Message:* {{ flow_run.state.message }}
• *Start:* {{ flow_run.start_time }}
• *End:* {{ flow_run.end_time }}
• *Link:* {{ flow_run|ui_url }}
If you truly need a native two‑column layout Teams’ Adaptive Cards support a FactSet (name/value) layout. Prefect’s built‑in Microsoft Teams notification block doesn’t expose custom card templates, but if you’re sending to a Power Automate (Flow) URL, you can have your Flow construct an Adaptive Card with a FactSet from the incoming payload. Here’s a minimal Adaptive Card body your Flow could send to Teams:
Copy code
{
  "type": "message",
  "attachments": [
    {
      "contentType": "application/vnd.microsoft.card.adaptive",
      "content": {
        "$schema": "<http://adaptivecards.io/schemas/adaptive-card.json>",
        "type": "AdaptiveCard",
        "version": "1.4",
        "body": [
          {
            "type": "TextBlock",
            "text": "Flow Run Completed",
            "weight": "Bolder",
            "size": "Large"
          },
          {
            "type": "FactSet",
            "facts": [
              {
                "name": "Flow",
                "value": "{{ flow.name }}"
              },
              {
                "name": "Run Name",
                "value": "{{ flow_run.name }}"
              },
              {
                "name": "Run ID",
                "value": "{{ flow_run.id }}"
              },
              {
                "name": "State",
                "value": "{{ flow_run.state.name }}"
              },
              {
                "name": "Start",
                "value": "{{ flow_run.start_time }}"
              },
              {
                "name": "End",
                "value": "{{ flow_run.end_time }}"
              },
              {
                "name": "Link",
                "value": "{{ flow_run|ui_url }}"
              }
            ]
          }
        ]
      }
    }
  ]
}
If you want, share how you’re wiring the notification (Prefect block vs. Power Automate customization), and I can tailor the snippet exactly to your setup.
b
@Marvin create a body that has a table for my flow run results. It should include the following: Flow run name Flow name Deployment Name Deployment Version Flow run URL State Name State Message Error List
m
thought for 47.5 seconds
Here’s a ready-to-paste Automation body that renders as a simple two-column “table” in Teams. Note: Teams doesn’t support true Markdown tables in Adaptive Cards, but this gives a clean key:value look.
Copy code
Flow Run Results

| Field               | Value |
|---------------------|-------|
| Flow run name       | {{ flow_run.name }} |
| Flow name           | {{ flow.name }} |
| Deployment Name     | {{ deployment.name if deployment else "—" }} |
| Deployment Version  | {{ deployment.version if deployment else "—" }} |
| Flow run URL        | {{ flow_run|ui_url }} |
| State Name          | {{ flow_run.state.name }} |
| State Message       | {{ flow_run.state.message or "—" }} |
| Error List          | {% if flow_run.state.name in ["Failed", "Crashed"] %}{{ flow_run.state.message }}{% else %}None{% endif %} |
If you prefer a native two-column layout (FactSet) via Power Automate, say the word and I’ll share an Adaptive Card JSON snippet you can drop into your Flow.
b
@Marvin, make adjustments to the table so that the flow run url displays as "Open Prefect Flow Run". Is it possible to include a row that has a value to the GitHub repository for the version? Also include a row for how the flow run was executed. Meaning indicate whether it was schedule or manually executed. Also include a row that is a list of tags associated to the deployment in the table.
m
thought for 153.0 seconds
Got it — here’s an updated body that: - Shows the Flow Run URL as “Open Prefect Flow Run” - Adds a GitHub repository row (uses a workspace variable
REPO_URL
if available) - Indicates whether the run was scheduled or manual - Lists deployment tags Paste this into your Automation action body:
Copy code
Flow Run Results

| Field                       | Value |
|---------------------------- |-------|
| Flow run name               | {{ flow_run.name }} |
| Flow name                   | {{ flow.name }} |
| Deployment Name             | {{ deployment.name if deployment else "—" }} |
| Deployment Version          | {{ deployment.version if deployment else "—" }} |
| Flow run URL                | <{{ flow_run|ui_url }}|Open Prefect Flow Run> |
| State Name                  | {{ flow_run.state.name }} |
| State Message               | {{ flow_run.state.message or "—" }} |
| Error List                  | {% if flow_run.state.name in ["Failed", "Crashed"] %}{{ flow_run.state.message }}{% else %}None{% endif %} |
| Executed Via                | {% if flow_run.auto_scheduled %}Scheduled{% else %}Manual{% endif %} |
| Deployment Tags             | {% if deployment and deployment.tags %}{{ deployment.tags | join(", ") }}{% else %}—{% endif %} |
| GitHub Repository (version) | {% if variables is defined and variables.REPO_URL is defined and variables.REPO_URL %}{% if deployment and deployment.version %}<{{ variables.REPO_URL }}/tree/{{ deployment.version }}|{{ variables.REPO_URL | replace('https://', '') }} @ {{ deployment.version }}>{% else %}<{{ variables.REPO_URL }}|{{ variables.REPO_URL | replace('https://', '') }}>{% endif %}{% else %}—{% endif %} |
Notes: - To populate the GitHub row, set a workspace variable named
REPO_URL
(for example `https://github.com/org/repo`). If a deployment version is present, the link will point to
/tree/{{ deployment.version }}
. - “Executed Via” uses
flow_run.auto_scheduled
to distinguish Scheduled vs Manual.
b
@Marvin most of this is working. Examine the attached screenshot of the results. There is a formatting issue with the Flow run URL where it's not showing as a hyperlink and is split into a 3 column. Please fix this. The GitHub repository is not showing. Is this possible to show in the table?
m
thought for 337.4 seconds
Thanks for the screenshot context — that helps. Why the link broke inside your “table” - Teams Adaptive Cards don’t support Markdown tables. When you use pipes
|
to fake a table, Teams often stops parsing Markdown links on those lines and treats the pipes as column separators. That’s why your “<…|Open Prefect Flow Run>” didn’t render as a hyperlink and looked like multiple columns. Two reliable fixes - If you must keep the 2‑column look in a single Prefect message, keep the “table” for visual alignment but put actual clickable links underneath as a short “Links” section. - If you truly need a native 2‑column layout with clickable links inside the rows, use a Power Automate flow to post an Adaptive Card with a FactSet (or buttons). That’s the only way to get a real table-like layout with fully working links. Updated Prefect Automation body (keeps the visual table, adds working links below) This version: - Keeps the 2‑column look with pipes - Uses “Open Prefect Flow Run” text in the Value cell, then adds a clickable link below in a Links section - Tries to populate the GitHub row from a workspace variable
REPO_URL
, falling back to scanning
deployment.pull_steps
for a repository URL - Shows Scheduled vs Manual from
flow_run.auto_scheduled
- Lists deployment tags
Copy code
{# --- Try to find a repository URL --- #}
{% set repo_url = None %}
{% if variables is defined and variables.REPO_URL %}
  {% set repo_url = variables.REPO_URL %}
{% elif deployment and deployment.pull_steps %}
  {% for s in deployment.pull_steps %}
    {% if s.get('type') in ['git', 'git_clone', 'git-repo', 'pull', 'checkout'] %}
      {% set repo_url = s.get('repository') or s.get('url') or s.get('repo') %}
    {% endif %}
  {% endfor %}
{% endif %}

{# --- Optional: compute a repo link that points at the version if available --- #}
{% if repo_url and deployment and deployment.version %}
  {% set repo_link_url = repo_url.rstrip('/') ~ '/tree/' ~ deployment.version %}
  {% set repo_link_text = (repo_url | replace('https://','') | replace('http://','')) ~ ' @ ' ~ deployment.version %}
{% elif repo_url %}
  {% set repo_link_url = repo_url %}
  {% set repo_link_text = (repo_url | replace('https://','') | replace('http://','')) %}
{% else %}
  {% set repo_link_url = None %}
  {% set repo_link_text = None %}
{% endif %}

Flow Run Results

| Field                       | Value |
|---------------------------- |-------|
| Flow run name               | {{ flow_run.name }} |
| Flow name                   | {{ flow.name }} |
| Deployment Name             | {{ deployment.name if deployment else "—" }} |
| Deployment Version          | {{ deployment.version if deployment else "—" }} |
| Flow run URL                | Open Prefect Flow Run |
| State Name                  | {{ flow_run.state.name }} |
| State Message               | {{ flow_run.state.message or "—" }} |
| Error List                  | {% if flow_run.state.name in ["Failed", "Crashed"] %}{{ flow_run.state.message or "—" }}{% else %}—{% endif %} |
| Executed Via                | {% if flow_run.auto_scheduled %}Scheduled{% else %}Manual{% endif %} |
| Deployment Tags             | {% if deployment and deployment.tags %}{{ deployment.tags | join(", ") }}{% else %}—{% endif %} |
| GitHub Repository (version) | {% if repo_link_text %}{{ repo_link_text }}{% else %}—{% endif %} |

Links:
- <{{ flow_run|ui_url }}|Open Prefect Flow Run>
{% if repo_link_url %}- <{{ repo_link_url }}|View Repository>{% endif %}
If you want a true two‑column layout with clickable links inside the row Use a Power Automate flow to post an Adaptive Card to Teams. In your flow, construct a card with a FactSet and include buttons (Action.OpenUrl) for reliable links. Example card body: ``` { "type": "message", "attachments": [ { "contentType": "application/vnd.microsoft.card.adaptive", "content": { "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "type": "AdaptiveCard",
"version": "1.4", "body": [ { "type": "TextBlock", "text": "Flow Run Results", "weight": "Bolder", "size": "Large" }, { "type": "FactSet", "facts": [ { "name": "Flow run name", "value": "{{ flow_run.name }}" }, { "name": "Flow name", "value": "{{ flow.name }}" }, { "name": "Deployment Name", "value": "{{ deployment.name if deployment else '—' }}" }, { "name": "Deployment Version", "value": "{{ deployment.version if deployment else '—' }}" }, { "name": "State Name", "value": "{{ flow_run.state.name }}" }, { "name": "State Message", "value": "{{ flow_run.state.message or '—' }}" }, { "name": "Executed Via", "value": "{% if flow_run.auto_scheduled %}Scheduled{% else %}Manual{% endif %}" }, { "name": "Deployment Tags", "value": "{% if deployment and deployment.tags %}{{ deployment.tags | join(', ') }}{% else %}—{% endif %}" }, { "name": "Repository", "value": "{% if repo_link_text %}{{ repo_link_text }}{% else %}—{% endif %}" } ] } ], "actions": [ { "type": "Action.OpenUrl", "title": "Open Prefect Flow Run", "url": "{{ flow_run|ui_url }}" } {% if repo_link_url %},{ "type": "Action.OpenUrl", "title": "View Repository", "url": "{{ repo_link_url }}" }{% endif %} ] } } ] } ``` About the GitHub repository not showing - In the first template, it will only show if either: - You define a workspace variable
REPO_URL
(e.g., https://github.com/org/repo), or - Your deployment was created with source pull steps that include a Git repository; the template scans
deployment.pull_steps
for a
repository
or
url
. - If neither is present, there’s nothing to display and it will show “—”. If you can share how the deployment was created (e.g.,
flow.from_source(...)
vs local path), I can tailor the repo extraction to your setup. If you’d like to stick with the table look, I recommend the first snippet (table + “Links” section). If you want fully native two-column with clickable links inside rows, go with the Power Automate Adaptive Card approach.
b
@Marvin, in the automation, is it possible to dynamically set the Notify Type which is present in the block being used and indicates failure, warning, success, etc.? The goal would be to use 1 block and 1 automation to generate the notification in teams where the Notify Type for the Teams notification is set based on the run status of the flow run.
m
thought for 76.1 seconds
Short answer: not with a stock block. In Prefect 3 automations, the send-notification action can template the subject and body, but it cannot override a block’s notify_type at runtime. The MicrosoftTeamsWebhook block’s notify_type is fixed by the block configuration and is what gets passed to Teams on every call. What you can do: - Easiest: Create a few Teams blocks (e.g., one each for success, warning, failure) and in a single automation use conditional actions or multiple actions to route based on the flow run state. Example triggers: Failed → failure block; Completed → success block; Canceled/Paused → warning block. - More flexible: Implement a small custom notification block that chooses notify_type dynamically in its notify() method based on the flow_run state in the body/subject/context you pass in. - Full control: Use CustomWebhookNotificationBlock to send a payload to your own endpoint (or Power Automate) that constructs the Teams card and “status” styling from the flow run state. If you want, I can sketch: - an Automation config using multiple Teams blocks keyed by state, or - a minimal custom block that maps state → notify_type.
b
@Marvin, the GitHub URL logic didn't work. The GitHub URL should come from the from the deployment -> Versions tab which contains a URL link to the version in GitHub. The link at the bottom of the message to the prefect flow run didn't work. Remove that and just make the Flow Run URL the full URL to the Prefect Flow Run.
m
🔄 Researching your answer... this may take a while 🔧 Using tool:
research_prefect_topic
📊 Tools used: •
research_prefect_topic
(1x)
b
@Marvin are you still working?
m
thought for 3.5 seconds
Yep—I’m here and ready to help. What can I do for you?
b
@Marvin, the GitHub URL logic didn't work. The GitHub URL should come from the from the deployment -> Versions tab which contains a URL link to the version in GitHub. The link at the bottom of the message to the prefect flow run didn't work. Remove that and just make the Flow Run URL the full URL to the Prefect Flow Run.
m
thought for 160.1 seconds
Got it—thanks for the specifics. Here’s a corrected approach that does both: - Uses the exact URL from the Deployment → Versions tab (via
deployment.version_info.url
) - Sets the Flow Run URL to the full UI URL (via
prefect.runtime.flow_run.ui_url()
) - Removes any extra/bottom link Drop-in snippet:
Copy code
from prefect.context import FlowRunContext
from prefect.runtime import flow_run as runtime_flow_run
from prefect.client.orchestration import get_client

async def get_links_for_slack():
    # Full URL to the Prefect Flow Run
    flow_run_url = runtime_flow_run.ui_url()

    # URL to the exact code version from the Deployment -> Versions tab
    source_url = None
    ctx = FlowRunContext.get()
    if ctx and ctx.flow_run and ctx.flow_run.deployment_id:
        async with get_client() as client:
            deployment = await client.read_deployment(ctx.flow_run.deployment_id)
        if deployment.version_info and getattr(deployment.version_info, "url", None):
            # Use the URL recorded by Prefect for this deployment version (e.g., GitHub link)
            source_url = deployment.version_info.url

    return flow_run_url, source_url
Example Slack blocks without the extra/bottom link:
Copy code
flow_run_url, source_url = await get_links_for_slack()

blocks = [
  {
    "type": "section",
    "text": {
      "type": "mrkdwn",
      "text": f"*Flow Run URL:* <{flow_run_url}|{flow_run_url}>"
    }
  }
]

if source_url:
  blocks.append({
    "type": "section",
    "text": {
      "type": "mrkdwn",
      "text": f"*Code Version:* <{source_url}|View on GitHub>"
    }
  })
Notes: - We no longer construct the GitHub URL manually. We take it directly from
deployment.version_info.url
, which matches what you see in the UI’s Deployment → Versions tab. - The flow run link uses
prefect.runtime.flow_run.ui_url()
to ensure it’s the full, direct URL to the run. If you paste a snippet of your current Slack message builder, I can wire these changes directly into it.
b
@Marvin, how do I reference that in the body of an automation that sends a Microsoft Teams message?
m
thought for 232.3 seconds
Great question. In Prefect 3 automations, the Microsoft Teams “body” supports Jinja. You can reference: - Full Flow Run URL via the
ui_url
filter:
{{ flow_run | ui_url }}
- The Deployment’s code version URL (the same link you see in Deployment → Versions) via:
{{ deployment.version_info.url }}
Example Teams message body:
Copy code
Flow run {{ flow_run.name }} entered state {{ flow_run.state.name }}.

View in Prefect: {{ flow_run | ui_url }}

{% if deployment and deployment.version_info and deployment.version_info.url %}
Code version: {{ deployment.version_info.url }}
{% else %}
Code version: Not available
{% endif %}
Optional subject:
Copy code
Flow {{ flow_run.name }} {{ flow_run.state.name }}
Notes: -
ui_url
is a built-in Jinja filter that returns the full Prefect UI URL for the object. -
deployment.version_info.url
is only present if the deployment was created from source with versioning info. Use the conditional as shown to handle missing data. Docs for reference: - Automations templating overview: Automations (templating) - Jinja filters (includes
ui_url
): Jinja filters for Automations - Microsoft Teams notification block: Notification blocks API and Blocks concepts
b
@Marvin that has been fixed. I have 3 blocks for teams. One for warning messages, another for info, and another for failures. Can you give me an example of an automation that chooses which block to use based on the Flow Run State Name?
m
thought for 89.4 seconds
Short answer: you can’t dynamically choose the Teams block inside a single automation. The
send-notification
action’s
block_document_id
is not templatable, so it must be a fixed block per action. Practical pattern: create multiple automations, each targeting the states you care about, and point each one at the appropriate Teams block. Example setup (3 automations): - Automation 1: Failures → teams-fail block - Automation 2: Warnings/Info (e.g., Running/Scheduled) → teams-info block - Automation 3: Completed → teams-warning block YAML-style examples:
Copy code
# 1) Failures -> Teams "fail" block
- name: "Notify Teams - Failed runs"
  trigger:
    type: flow-run-state-change
    match:
      flow_run.state.type: ["FAILED"]
  actions:
    - type: send-notification
      block_document_id: "<UUID_TEAMS_FAIL_BLOCK>"
      subject: "🚨 Flow failed: {{ flow_run.name }}"
      body: |
        Flow: {{ flow_run.name }}
        State: {{ flow_run.state.name }}
        {{ flow_run.state.message }}
        View: {{ flow_run | ui_url }}
        {% if deployment and deployment.version_info and deployment.version_info.url %}
        Code: {{ deployment.version_info.url }}
        {% endif %}

# 2) Info (Running/Scheduled) -> Teams "info" block
- name: "Notify Teams - Info updates"
  trigger:
    type: flow-run-state-change
    match:
      flow_run.state.type: ["RUNNING", "SCHEDULED"]
  actions:
    - type: send-notification
      block_document_id: "<UUID_TEAMS_INFO_BLOCK>"
      subject: "ℹ️ Flow update: {{ flow_run.name }} is {{ flow_run.state.name }}"
      body: |
        Flow: {{ flow_run.name }}
        State: {{ flow_run.state.name }}
        View: {{ flow_run | ui_url }}

# 3) Completed -> Teams "warning" block (as you defined)
- name: "Notify Teams - Completed runs"
  trigger:
    type: flow-run-state-change
    match:
      flow_run.state.type: ["COMPLETED"]
  actions:
    - type: send-notification
      block_document_id: "<UUID_TEAMS_WARNING_BLOCK>"
      subject: "✅ Flow completed: {{ flow_run.name }}"
      body: |
        Flow: {{ flow_run.name }}
        State: {{ flow_run.state.name }}
        View: {{ flow_run | ui_url }}
Notes: - Use the state TYPEs in
match
(COMPLETED, FAILED, RUNNING, SCHEDULED). These are reliable across names. - You can still tailor the subject/body with Jinja as shown. - If you also want to catch “Crashed”, add it to the first automation’s match list:
["FAILED", "CRASHED"]
. If you share your three Teams block IDs, I can plug them into a ready-to-paste JSON/YAML for you.