Plan overview
Customer Slack Signals
Read the internal and shared external Slack channels for an account, extract every meaningful signal with a permalink, and produce a health verdict with action items.
Best for
Customer success · Account management · Support
Runs with
Slack · Signal extraction · Action items
At a glance
04
Outputs
01
Capabilities
03
Teams
Core outputs
Signal log with permalinks
Executive verdict
Top themes
Action items
Capability requirements
What it solves
The real state of an account lives in scattered Slack threads across two channels, and no one has a repeatable way to read the relationship.
Turns noisy Slack chatter into a structured account health read.
Preserves a linkable source for every signal it surfaces.
Assigns owners and channels to each follow-up automatically.
Workflow
Fetch recent history from the internal and external account channels.
Extract signals — requests, escalations, sentiment — each with a permalink.
Summarize relationship health and derive owned action items.
Implementation
Review the underlying FML plan — the sessions, tools, and typed schemas that define this workflow — and see exactly how it is instructed for repeatable execution.
FML Plan
customer-chatter.fml
100 lines
parameter("internalChannelId", type=string) # Slack channel ID for the internal side channel parameter("externalChannelId", type=string) # Slack channel ID for the shared external customer channel require mcp Slack transformer("slimSlackMessages") { onFunctionOutput = "conversations_history" jmesPath = "messages[*].{text: text, user: user, ts: ts, thread_ts: thread_ts}" } components { schema("signal") { author?: string # Slack user who posted (display name or handle) relevance?: string # Why this signal matters (e.g. feature request, escalation, blocker) timestamp?: string # ISO datetime permalink: string # Constructed Slack permalink to the message summary: string # 1-sentence summary of the message or thread full_message: string # The exact, verbatim full text of the original Slack message channel: string # internal | external } schema("action_item") { due?: string # Stated due date, or empty if none mentioned permalink?: string # Slack permalink to the source message, if directly applicable item: string # Concise description of the follow-up owner: string # Who owns it — Barndoor name, customer name, or 'Unknown' source_channel?: string # internal | external } } session("fetch-signals") { call("conversations_history") -> internal_messages { channel = "{{ .params.internalChannelId }}" limit = 100 } call("conversations_history") -> external_messages { channel = "{{ .params.externalChannelId }}" limit = 100 } - You have two batches of Slack messages: 1. INTERNAL channel ({{ .params.internalChannelId }}) — side discussion: {{ .vars.internal_messages }} 2. EXTERNAL channel ({{ .params.externalChannelId }}) — shared channel: {{ .vars.external_messages }} Extract signals from each batch separately. A signal is any message or thread that carries one of: - a feature request, bug report, or complaint - a positive or negative sentiment expression - an escalation or blocker - an explicit action item or commitment from either side - a planned meeting, demo, or follow-up Map each signal to the `signal` schema and tag it with `channel`: - "internal" for items from the internal channel - "external" for items from the external channel Include the exact, verbatim text of the original message in the `full_message` field. Use the message's `ts` (Slack timestamp) converted to ISO for `timestamp`. Construct the `permalink` for each signal using the archives URL format: https://barndoorai.slack.com/archives/{CHANNEL_ID}/p{TS_WITHOUT_DOT} (Use {{ .params.internalChannelId }} or {{ .params.externalChannelId }} as the CHANNEL_ID depending on the source. Remove the decimal point from the `ts` value to create the `p...` suffix). If the message is a thread reply — i.e. it has a `thread_ts` that differs from its own `ts` — append the thread context so the link opens the thread panel: https://barndoorai.slack.com/archives/{CHANNEL_ID}/p{TS_WITHOUT_DOT}?thread_ts={THREAD_TS}&cid={CHANNEL_ID} (Use the raw `thread_ts` value, with the decimal point kept, for the thread_ts query param.) Discard pure status pings, bot messages, emoji-only reactions, and routine "+1"/"thanks" replies that carry no information. schema $signal[] } session("build-summary", after="fetch-signals") { context true - Using the signals extracted above, produce three outputs: `exec_summary` (1-2 sentences, CS-level): Plain-English verdict on the health of the relationship based on the Slack chatter. Name the most important theme and indicate whether the relationship is trending positive, neutral, or at risk. Reference specific signals where possible (e.g. "The customer raised a P1 on auth flow Tuesday and is awaiting a fix; otherwise traffic is steady"). `top_themes` (3-5 short strings): Recurring topics across both channels. Each theme should reference how many messages touched it and which channel(s) carried it. Examples: - "OAuth re-auth pain — 4 messages in external, 2 internal follow-ups" - "Upcoming QBR scheduled for next week" `action_items`: Specific follow-ups derived from the chatter. For each, include the owner (Barndoor name, customer-side name, or 'Unknown'), the source channel, a stated due date if one was mentioned in Slack, and optionally the `permalink` to the source signal it originated from (omit if this is an aggregated item). schema { exec_summary: string # 1-2 sentence CS-level verdict on customer health from Slack chatter top_themes: string[] # Short recurring-theme description with rough volume + channel action_items: $action_item[] } }