Meeting notes to Slack

Combine voice and apps: transcribe a meeting recording, summarise it into notes and actions with flux-auto, then post the notes to a Slack channel.

This recipe turns a meeting recording into notes your team can read: transcribe the audio, summarise it into decisions and actions, then post the summary in a Slack channel. It uses three Flux abilities on one key.

StepAbilityStatus
Transcribe the recordingSpeech to textOver the API: Live. As a tool: Shipping
SummariseChat with flux-autoEvery key
Post to SlackAppsShipping

The Slack step needs Slack connected to your Flux account. Connected apps are shared by every key on the account, so any key you use here can post as you. See Who can see your connections.

Before you start

  • The recording. Any common audio format works (mp3, m4a, wav, ogg, webm, flac), up to 8 MB. Split a longer recording into parts.
  • A paid plan. Transcription is a paid ability. See Give your agent abilities.
  • Slack connected. Ask your agent to connect it with apps_connect, or run the connect step in Use apps.

From your agent

Add the Flux tools server to your agent, if you have not already:

Built into Murage, which decides which abilities it uses. Nothing to set up here. See murage.ai.

Then ask:

Transcribe https://files.example.com/standup-0928.m4a with the Flux transcribe tool. Summarise it as: decisions, actions (with owner), open questions. Then use the Flux apps tools to post the summary in the #team Slack channel. Find the Slack action with apps_find_actions first.

The agent makes three kinds of call: transcribe once, its own model to write the summary, and apps_find_actions then apps_execute once to post. The transcribe tool takes a public https link (audio_url) or the file inline as base64 (audio_base64 with filename).

If the transcribe tool is not switched on for your key yet, it answers ability_disabled. Transcribe from your own code instead, as below, and hand the text to your agent.

From your own code

Transcribe and summarise with the OpenAI-compatible client, then post through the tools server. The Slack action slug and argument names are an example; take the real ones from apps_find_actions.

python
import json
import requests
from openai import OpenAI

KEY = "sk-YOUR-FLUX-KEY"
client = OpenAI(base_url="https://api.fluxrouter.ai/v1", api_key=KEY)

# 1. Transcribe. flux-voice picks the model by clip length.
with open("standup-0928.m4a", "rb") as audio:
    transcript = client.audio.transcriptions.create(model="flux-voice", file=audio)

# 2. Summarise.
summary = client.chat.completions.create(
    model="flux-auto",
    messages=[
        {"role": "system", "content": "Summarise meeting transcripts as: Decisions, Actions (with owner), Open questions. Be brief."},
        {"role": "user", "content": transcript.text},
    ],
).choices[0].message.content

# 3. Post to Slack through the Flux tools server.
body = {
    "jsonrpc": "2.0", "id": 1, "method": "tools/call",
    "params": {"name": "apps_execute", "arguments": {
        "action": "SLACK_SEND_MESSAGE",
        "arguments": {"channel": "#team", "text": summary},
    }},
}
res = requests.post(
    "https://api.fluxrouter.ai/mcp-tools/",
    headers={
        "Authorization": f"Bearer {KEY}",
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
    },
    json=body,
    timeout=320,
)
res.raise_for_status()
data = next(line[len("data:"):] for line in res.text.splitlines() if line.startswith("data:"))
result = json.loads(data)["result"]["structuredContent"]
print(result.get("error") or result["ok"])

The same in TypeScript:

ts
import fs from 'node:fs';
import OpenAI from 'openai';

const KEY = 'sk-YOUR-FLUX-KEY';
const client = new OpenAI({ baseURL: 'https://api.fluxrouter.ai/v1', apiKey: KEY });

// 1. Transcribe.
const transcript = await client.audio.transcriptions.create({
  model: 'flux-voice',
  file: fs.createReadStream('standup-0928.m4a'),
});

// 2. Summarise.
const completion = await client.chat.completions.create({
  model: 'flux-auto',
  messages: [
    { role: 'system', content: 'Summarise meeting transcripts as: Decisions, Actions (with owner), Open questions. Be brief.' },
    { role: 'user', content: transcript.text },
  ],
});
const summary = completion.choices[0]?.message.content ?? '';

// 3. Post to Slack through the Flux tools server.
const res = await fetch('https://api.fluxrouter.ai/mcp-tools/', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${KEY}`,
    'Content-Type': 'application/json',
    Accept: 'application/json, text/event-stream',
  },
  body: JSON.stringify({
    jsonrpc: '2.0',
    id: 1,
    method: 'tools/call',
    params: {
      name: 'apps_execute',
      arguments: { action: 'SLACK_SEND_MESSAGE', arguments: { channel: '#team', text: summary } },
    },
  }),
});
const line = (await res.text()).split('\n').find((l) => l.startsWith('data:'));
const result = JSON.parse(line!.slice('data:'.length)).result.structuredContent;
console.log(result.error ?? result.ok);

Good to know

  • Nothing is rolled back. If the post fails, you still have the transcript and the summary, and you were billed for the transcription. Post again with the same text; do not transcribe again.
  • Check before posting twice. If the job might run twice for one meeting, put the recording's name in the message and ask the agent to search the channel for it first.
  • Transcripts can hold private details. Read the summary before it goes to a wide channel, or post it to yourself first.

What it costs

  • Transcription is billed per second of audio, rounded up, with a minimum. The response's x-flux-billed-seconds and x-flux-cost-usd headers show what it cost. Price: $0.10 per minute heard.
  • The summary is a normal chat call on flux-auto.
  • The Slack post is one app action run.
  • App actions: priced per call.

If something goes wrong

  • The recording is refused as too large or the wrong format: see Speech to text problems.
  • The Slack step says payment_required, rate_limited or invalid_request: see App problems.