Webhooks
Pro+ adds signed HTTPS webhooks so your own systems can react the moment a meeting or D&D session is ready—without polling DiscMeet.
Typical uses:
- Zapier / Make — Start a workflow when a transcript finishes (Slack, Notion, CRM, email, databases).
- Custom backends — Enqueue jobs, update an internal app, or sync metadata to your data warehouse.
Webhooks are per Discord server (guild). Anyone who can manage this server in the DiscMeet dashboard can configure the webhook URL and signing secret in Integrations—the same rules as the rest of server settings: you need Manage Channels, Administrator, or to be the Discord server owner for that guild (see the Integrations page if you don’t have access).
What you need
- An active Pro+ subscription on the guild owner’s DiscMeet account (the account that owns billing for that server).
- A public HTTPS URL that accepts POST requests with a JSON body (Zapier “Catch Hook”, Make “Webhooks” custom hook, or your own server).
- (Recommended) A signing secret so you can verify requests really came from DiscMeet.
Configure in DiscMeet
- Sign in and open the dashboard .
- Select the server you want.
- In the sidebar, open Integrations.
- Paste your webhook URL (must start with
https://). - Click Rotate secret (or equivalent) once, copy the secret immediately, and store it somewhere safe. DiscMeet only shows the full secret at creation time; later you’ll only see the last four characters.
- Save the URL.
If Pro+ is not active for that server’s owner, the Integrations screen will prompt you to upgrade.
Events
For each recording, transcript.ready fires when notes are ready. audio.ready fires only when MP3 recording is turned on for that server in dashboard → Settings—that is what creates the full downloadable file. If MP3 recording is off, you will not get audio.ready for new meetings, but transcript.ready still works as usual.
When an event is sent, it includes the same meeting_id, plus the channel name and participant list, so you can match transcript and audio and label automations clearly.
transcript.ready
Fired when transcription has finished for a regular meeting or a D&D session. Each payload includes where the session lives in Discord, who spoke, and the notes and transcript text (plain strings—escape newlines and quotes as usual):
discord_channel_name— The name of the Discord text channel DiscMeet ties to this meeting (where notes would be posted).nullonly if the name could not be looked up at send time (for example, a brief Discord API issue).participant_names— Display names of people who spoke, in order of first appearance. JSON array of strings; use[]when no speakers were detected.notes— A summary of the conversation generated from the transcript. Supports markdown formatting.""if there is nothing to send.transcript— The conversation text with speaker + timestamp lines (who said what, and when). Supports markdown formatting.""if there is nothing to send.audio_url— HTTPS URL for the merged full-session MP3 (stable path on DiscMeet’s recording host). The file exists only after merged audio is available (MP3 recording must be on for the server); until then the URL may not download successfully.
{
"event": "transcript.ready",
"meeting_id": "550e8400-e29b-41d4-a716-446655440000",
"guild_discord_id": "987654321098765432",
"recording_type": "meeting",
"discord_channel_name": "meeting-transcripts",
"participant_names": ["Alex", "Jamie"],
"notes": "## Meeting — Mar 28, 2026\n\n**Alex** (00:01): Quick sync on the roadmap.\n\n**Jamie** (00:45): Sounds good.",
"transcript": "## Meeting — Mar 28, 2026\n\n**Alex** (00:01): Quick sync on the roadmap.\n\n**Jamie** (00:45): Sounds good.",
"audio_url": "https://rails.discmeet.com/recordings/550e8400-e29b-41d4-a716-446655440000/audio.mp3",
"occurred_at": "2026-03-28T12:34:56Z"
}{
"event": "transcript.ready",
"meeting_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"guild_discord_id": "987654321098765432",
"recording_type": "session",
"discord_channel_name": "the-broken-seal",
"participant_names": ["DM", "Asha", "Rook"],
"notes": "## D&D Session — The Broken Seal\n\n**DM** (00:02): You enter the hall...",
"transcript": "## D&D Session — The Broken Seal\n\n**DM** (00:02): You enter the hall...",
"audio_url": "https://rails.discmeet.com/recordings/6ba7b810-9dad-11d1-80b4-00c04fd430c8/audio.mp3",
"occurred_at": "2026-03-28T12:35:10Z"
}| Field | Type | Meaning |
|---|---|---|
event | string | Always transcript.ready for this event. |
meeting_id | string (UUID) | Stable public id for this recording. Use it to correlate with audio.ready and your own databases. |
guild_discord_id | string | Discord snowflake of the server. |
recording_type | string | "meeting" or "session" (D&D). |
discord_channel_name | string or null | Name of the Discord text channel for this meeting’s notes. |
participant_names | array of strings | Who spoke, first-seen order; empty array if unknown. |
notes | string | Conversation summary notes generated from the transcript. Markdown-friendly text string. |
transcript | string | Speaker + timestamp transcript (who said what, and when). Markdown-friendly text string. |
audio_url | string | HTTPS URL for the merged MP3 (…/recordings/{meeting_id}/audio.mp3). File is available only after merged audio exists (MP3 recording on). |
occurred_at | string | ISO 8601 timestamp in UTC when the event was emitted. |
notes vs transcript: both are plain strings in the webhook. Use transcript when you need the timestamped speaker-by-speaker conversation. Use notes when you want a concise summary of the conversation based on that transcript.
You can send notes or transcript straight into Notion, Google Docs, email bodies, or your database, and use discord_channel_name and participant_names to file or filter rows. Webhooks give you downstream copies without scraping Discord.
When MP3 recording is on, you might receive transcript.ready before or after audio.ready depending on whether the MP3 finishes before or after transcription. Always use meeting_id to match the two.
audio.ready
Sent only when MP3 recording is enabled for the server (see dashboard → Settings → MP3 recording). DiscMeet waits until transcription is finished and a full-session MP3 link exists. If MP3 recording is off, or there is no audio file to share, this event is not sent.
The same discord_channel_name and participant_names fields appear here as in transcript.ready, so you can label downloads or automations without waiting for the transcript event.
{
"event": "audio.ready",
"meeting_id": "550e8400-e29b-41d4-a716-446655440000",
"guild_discord_id": "987654321098765432",
"recording_type": "meeting",
"discord_channel_name": "meeting-transcripts",
"participant_names": ["Alex", "Jamie"],
"audio_url": "https://rails.discmeet.com/recordings/550e8400-e29b-41d4-a716-446655440000/audio.mp3",
"occurred_at": "2026-03-28T12:35:02Z"
}| Field | Type | Meaning |
|---|---|---|
event | string | Always audio.ready. |
meeting_id | string (UUID) | Same id as in transcript.ready for that recording. |
guild_discord_id | string | Discord server snowflake. |
recording_type | string | "meeting" or "session". |
discord_channel_name | string or null | Same meaning as in transcript.ready. |
participant_names | array of strings | Same meaning as in transcript.ready. |
audio_url | string | HTTPS URL to the merged MP3—usually the same stable public path as transcript.ready (…/recordings/{meeting_id}/audio.mp3). If that is not available, a time-limited presigned URL; download presigned links promptly before they expire. |
occurred_at | string | ISO 8601 UTC timestamp. |
How requests are sent
| Item | Value |
|---|---|
| Method | POST |
| URL | The HTTPS URL you configured |
Content-Type | application/json |
User-Agent | DiscMeet-Webhooks/1.0 |
| Body | UTF-8 JSON object (see Events) |
X-Discmeet-Signature | Present when a secret is configured — see Verifying signatures |
Timeouts and retries:
- Each attempt uses a 15 second HTTP timeout.
- If the call fails with a retryable error (network failure, HTTP 429, or 5xx), DiscMeet schedules up to 5 retries, 30 minutes apart.
- 4xx responses (except 429) are treated as non-retryable: the payload is not retried automatically.
transcript.ready bodies can be large—both notes and transcript are strings and may contain long markdown or other text. Ensure your endpoint, Zapier/Make limits, and timeouts can handle that.
Zapier (first-time friendly)
- Create a Zap. Trigger: Webhooks by Zapier → Catch Hook.
- Zapier shows you a unique HTTPS URL. Copy it.
- In DiscMeet, open the dashboard → Integrations, paste that URL as the webhook URL, and save.
- Run a real meeting (or wait for the next one). Zapier’s test screen should show a POST with JSON body.
- Map fields like
event,meeting_id,guild_discord_id,recording_type,discord_channel_name,participant_names,notes,transcript,audio_urlontranscript.ready, andaudio_urlagain onaudio.readyif you use both events.
Tips:
- Use a Filter (or Paths) so you only continue when
eventequalstranscript.readyoraudio.ready. - If
audio.readynever appears, check that MP3 recording is on in dashboard Settings for that server. - A follow-up step might GET
audio_urlto download the MP3. Stable public URLs do not expire like presigned links; if the payload uses a presigned URL, fetch it before expiry. - Signature verification in Zapier is limited; many teams filter on
guild_discord_idand keep the hook URL private. For stricter checks, point the webhook at your API, verify the signature there, then forward to Zapier.
Make (Integromat)
- Create a scenario. Add Webhooks → Custom webhook (or Custom trigger).
- Create a new hook and copy the HTTPS address.
- Paste it into DiscMeet Integrations (from the dashboard ) and save.
- Run a meeting; Make receives the JSON payload and you can map each property to later modules (Google Sheets, Slack, HTTP, etc.).
Same guidance as Zapier for filters on event. Treat audio_url as stable when it matches the …/recordings/{meeting_id}/audio.mp3 pattern; otherwise download presigned URLs promptly. Remember audio.ready only runs when MP3 recording is enabled for the server.
Verifying signatures
When a signing secret is set, DiscMeet sends:
X-Discmeet-Signature: sha256=<lowercase_hex>The HMAC uses SHA-256, the raw JSON body bytes exactly as sent (the same string DiscMeet POSTs—not pretty-printed, not re-serialized), and your shared secret.
Algorithm (conceptually):
expected_hex = HMAC_SHA256(secret, raw_request_body).hexdigest()
header_value = "sha256=" + expected_hexChecklist:
- Read the raw body as bytes/string before parsing JSON.
- Compute HMAC-SHA256 with your stored secret.
- Compare to the header value after the
sha256=prefix, using a constant-time string compare if possible.
If the secret is not configured, the header is omitted (webhooks still deliver). That is easier for quick Zapier tests but not recommended for production.
For HMAC snippets in several languages, see Signature verification examples below.
Signature verification examples
rawBody must be the exact bytes of the POST body (UTF-8). In some frameworks that is a Buffer, bytes, []byte, or byte[] before JSON parsing.
Node.js
import crypto from 'crypto';
function verifyDiscmeetWebhook(rawBody, header, secret) {
if (!header || !header.startsWith('sha256=') || !secret) return false;
const theirs = header.slice('sha256='.length);
const hmac = crypto.createHmac('sha256', secret).update(rawBody, 'utf8').digest('hex');
return crypto.timingSafeEqual(Buffer.from(theirs, 'utf8'), Buffer.from(hmac, 'utf8'));
}Why use webhooks
- Push — You get your notes automatically as soon as it’s ready.
- Easy matching —
meeting_idlets you pairtranscript.readyandaudio.readyfor the same recording (when you use MP3 recording). - Rich payload — Events include
notes,transcript, the Discord channel name, and who spoke, so you can automate recaps, full records, and tidy routing. - Optional security — Turn on signature verification with HMAC if you want stricter validation in production.
Related product docs
- API — List transcripts over HTTP with a Bearer token (pagination).
- Getting started — Billing and browser settings
- Commands overview