> ## Documentation Index
> Fetch the complete documentation index at: https://miniti.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# webhook recipes

> ready-to-paste examples for zapier, make, n8n, slack, and custom endpoints

miniti fires a `POST` with the full meeting json when a meeting is saved or updated. see [webhooks](/docs/integrations/webhooks) for the payload shape.

this page collects concrete recipes you can drop in.

<Warning>
  webhooks are not signed and not retried. treat incoming payloads as untrusted until you add your own auth (shared secret in a header, ip allowlist, etc.) on your endpoint.
</Warning>

## zapier

1. create a new zap with **webhooks by zapier** -> **catch hook**
2. copy the zapier webhook url
3. paste into miniti: settings -> integrations -> webhooks -> url
4. save a meeting in miniti to send a test payload
5. zapier will parse the json and expose all fields (including nested `meeting.meddpicc`, `meeting.training`, `meeting.action_items[]`)

common follow-up actions:

* **gmail: send email** - email yourself the summary + action items after every meeting
* **notion: create database item** - one row per meeting with title, summary, action items as a checklist
* **google sheets: create row** - log meetings for reporting
* **linear / jira: create issue** - one issue per action item (loop over `meeting.action_items`)

## make (integromat)

1. new scenario -> **webhooks** -> **custom webhook**
2. copy the webhook url into miniti settings
3. run once, save a miniti meeting, let make learn the structure
4. chain modules: iterator on `meeting.action_items` -> your task tracker, or router on `event` (saved vs updated)

## n8n

1. add a **webhook** node, method `POST`, path of your choice
2. copy the production url (not the test url) into miniti
3. downstream nodes can reference `{{$json["meeting"]["summary"]}}`, `{{$json["meeting"]["meddpicc"]["champion"]}}`, etc.

example: split action items into separate rows

```
webhook -> item lists (split out, field: meeting.action_items) -> your tool
```

## slack (via incoming webhook)

slack's native incoming webhooks don't accept miniti's payload directly - you need a small middleman. two easy options:

**option 1: zapier/make** - catch hook -> format a slack message -> post to channel.

**option 2: a tiny relay** (cloudflare worker, vercel function, fly, etc.):

```js theme={null}
export default {
  async fetch(request) {
    const body = await request.json()
    const m = body.meeting
    const slack = {
      text: `*${m.title ?? 'untitled meeting'}*\n${m.summary ?? ''}`,
      blocks: [
        { type: 'header', text: { type: 'plain_text', text: m.title ?? 'meeting' } },
        { type: 'section', text: { type: 'mrkdwn', text: m.summary ?? '' } },
        ...(m.action_items?.length
          ? [{ type: 'section', text: { type: 'mrkdwn', text: '*action items*\n' + m.action_items.map(a => `- ${a}`).join('\n') } }]
          : [])
      ],
    }
    await fetch(SLACK_WEBHOOK_URL, {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify(slack),
    })
    return new Response('ok')
  }
}
```

point miniti at your worker url.

## custom endpoint (node / bun / deno)

bare-bones receiver with a shared-secret header for auth:

```js theme={null}
import express from 'express'
const app = express()
app.use(express.json({ limit: '5mb' }))

app.post('/miniti', (req, res) => {
  if (req.header('x-webhook-secret') !== process.env.WEBHOOK_SECRET) {
    return res.status(401).end()
  }
  const { event, meeting } = req.body
  console.log(event, meeting.title, meeting.action_items?.length ?? 0)
  // save to your db, queue a job, etc.
  res.status(200).end()
})

app.listen(3000)
```

miniti doesn't support custom headers today, so the shared secret has to travel another way (subdomain-as-token, signed path, etc.) or skip auth and rely on an obscured url + ip allowlist.

## python (flask)

```python theme={null}
from flask import Flask, request, abort
app = Flask(__name__)

@app.post('/miniti')
def miniti():
    data = request.get_json()
    event = data.get('event')
    meeting = data.get('meeting', {})
    # do your thing
    return '', 200
```

## filtering by mode

the payload includes `meeting.training` (local metrics), `meeting.meddpicc` (only in meddpicc mode), and `meeting.questions` (only when questions mode has run). if you only care about sales calls, check `meeting.meddpicc` is non-null before acting.

## idempotency

each saved meeting has a stable `meeting.id` (uuid). use it as the idempotency key in your downstream system so that `meeting.updated` events replace rather than duplicate.

## see also

* [webhooks](/docs/integrations/webhooks) - payload shape + delivery rules
