Customer Slack Signals — AI Workflow Plan | Diaphora
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
Outputs
- Signal log with permalinks
- Executive verdict
- Top themes
- Action items
What it solves
Account health lives in scattered Slack threads. No one has a repeatable way to read it — so nobody really does. A structured health read. Not noisy Slack chatter someone half-remembers. Every signal keeps its source — linked, not paraphrased from memory. Owners and channels assigned automatically, follow-up by follow-up.
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 blueprint — the sessions, tools, and typed schemas that define this workflow — and see exactly how it is instructed for repeatable execution.
FML Blueprint
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[]
}
}