Developers · Go
A meeting bot in Go: join a meeting with coder/websocket
A Go program that joins a meeting and reports who is in it, with what it printed when it was run.
What it does
A Go program that dials the meeting's socket, introduces itself with its key, and prints who is in the room. It uses github.com/coder/websocket and its wsjson helpers.
It needs a key and a meeting code in MEETFUL_KEY and MEETING. Two curl requests make both. MEETFUL_URL is optional and defaults to https://meetful.com.
Go 1.22 or later. The socket programs use github.com/coder/websocket (go get github.com/coder/websocket@v1.8.12); the transcript program uses only the standard library.
The code
// Join a Meetful meeting as a bot from Go. go get github.com/coder/websocket
// MEETFUL_KEY=tmk_… MEETING=abc-defg-hij go run .
package main
import (
"context"
"fmt"
"log"
"os"
"strings"
"time"
"github.com/coder/websocket"
"github.com/coder/websocket/wsjson"
)
type member struct{ Name, Kind string }
type message struct {
Type string
Meeting string
Why string
You struct{ Name, Emoji string }
Members []member
Can []string
}
func main() {
base := os.Getenv("MEETFUL_URL")
if base == "" {
base = "https://meetful.com"
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
conn, _, err := websocket.Dial(ctx, "ws"+strings.TrimPrefix(base, "http"), nil)
if err != nil {
log.Fatal(err)
}
defer conn.Close(websocket.StatusNormalClosure, "done")
conn.SetReadLimit(4 << 20) // the room's state can be larger than the 32 KB default
hello := map[string]string{"type": "agent-hello", "key": os.Getenv("MEETFUL_KEY"), "meeting": os.Getenv("MEETING")}
if err := wsjson.Write(ctx, conn, hello); err != nil {
log.Fatal(err)
}
for {
var m message
if err := wsjson.Read(ctx, conn, &m); err != nil {
log.Fatal(err)
}
switch m.Type {
case "agent-ready":
var people []string
for _, x := range m.Members {
if x.Kind == "person" {
people = append(people, x.Name)
}
}
here := strings.Join(people, ", ")
if here == "" {
here = "nobody yet"
}
fmt.Printf("In %s as %s %s. People here: %s.\n", m.Meeting, m.You.Emoji, m.You.Name, here)
fmt.Printf("It may: %s\n", strings.Join(m.Can, ", "))
return
case "agent-refused":
log.Fatal("Refused: " + m.Why)
}
}
}Run it
MEETFUL_KEY=tmk_… MEETING=abc-defg-hij go run .
What it printed
In gik-fhvv-dcu as 🤖 go bot. People here: Dana, Sam.
It may: say, chat, draw, note
Run on 10 November 2026 against a Meetful server built from the same code as meetful.com, with two people — Dana and Sam — in the meeting saying three lines. Nothing above was edited.
How it works
coder/websocket refuses any message bigger than 32 KB unless you raise the limit. The room sends its whole state as one message, and that grows with a long meeting, so the program raises the limit to 4 MB right after dialling.
Decoding into a small struct with only the fields you use is enough: encoding/json ignores the rest, and matches field names without caring about case, so Type reads "type".
What can go wrong
Refused: no such key — mint one with POST /api/keysThe key is wrong, or was never minted on this server. A key from one Meetful server does not work on another.
Refused: no such meetingThe meeting code is not one that is open. A meeting that has ended, or a code with a typo, gives this.
read limited at … bytesThe library's default limit — 32768 bytes, set in its read.go — was left in place and a message from the room was bigger. Call conn.SetReadLimit after Dial.
Next
In Go: live transcript · speak · get the transcript
The whole API · Every socket message and webhook · All guides
Try Meetful
Meetings that take their own notes
Free video meetings in the browser: every line of the transcript under the name of who said it, and the summary and action items when you hang up. Nothing to install — guests just open the link.