# Meetingize documentation (complete)
# Documentation Meetingize
Nouveau
## Interrogez votre IA sur vos réunions
Le **serveur MCP Meetingize** connecte vos réunions à Claude, ChatGPT, Cursor ou tout client MCP en deux minutes :
collez une URL, connectez-vous, autorisez. Demandez ensuite *« Quels engagements ai-je pris avec le client Dupont ce
trimestre ? »* et obtenez une réponse citée depuis vos transcriptions, avec les horodatages.
## Construire avec Meetingize
Créez des réunions à partir d'un audio, lisez les transcriptions diarisées (JSON, Markdown, SRT, VTT), les
comptes rendus IA et les exports prêts pour les LLM, générez des documents. Clés API ou OAuth 2.1.
[Présentation de l'API](api/overview/) et [explorateur d'API](api/explorer/)
De l'envoi du fichier à la transcription, au compte rendu et aux documents : statuts, polling ou webhooks, et ce
que vous recevez à chaque étape. [Lire le guide](api/create-meeting/)
Soyez notifié quand une transcription, un compte rendu ou un document est prêt. Signatures HMAC-SHA256, renvois
automatiques. [Webhooks](api/webhooks/)
Un seul fichier Python, sans dépendance : lister les réunions, exporter les transcriptions, générer des
documents depuis votre terminal. [Installer la CLI](cli/meetingize/)
## Utiliser Meetingize
Le [guide produit](guide/overview/) couvre l'enregistrement dans le navigateur ou sur iPhone, l'import de fichiers,
la transcription avec diarisation des locuteurs, les comptes rendus IA, les modèles par métier, la génération de
documents, la preuve de conseil, le partage et les forfaits.
## Pour les agents IA
Cette documentation est disponible en texte brut pour les modèles de langage : [llms.txt](/llms.txt) (index) et
[llms-full.txt](/llms-full.txt) (tout). Chaque page existe aussi en Markdown brut : ajoutez `.md` à son URL sans
le slash final, par exemple [api/webhooks.md](/api/webhooks.md). La spécification OpenAPI se trouve à
[openapi.json](/openapi.json).
---
# Meetingize for developers
Meetingize turns real conversations (in person, by phone or video) into diarized, timestamped transcripts, AI
meeting reports and professional documents. The developer platform exposes the same data and actions to your code
and to AI assistants, always within the secure context of one Meetingize account.
## What can I build?
- **Sync meetings into your tools**: push transcripts, reports and action items into a CRM, a case management
system or a data warehouse as soon as they are ready (webhooks + REST API).
- **Transcribe from your own app**: upload an audio or video file, or give a URL, and get back a diarized transcript
and an AI report (`POST /v1/meetings`).
- **Generate documents**: produce minutes, follow-up emails, suitability reports or any custom document from a
meeting, with Meetingize templates or your own prompt (`POST /v1/meetings/{id}/documents`).
- **Give your AI assistant a memory of your meetings**: connect Claude, ChatGPT, Cursor or any MCP client to the
Meetingize MCP server and ask questions across all your meetings.
## The building blocks
| Block | What it is | Page |
|---|---|---|
| REST API v1 | JSON over HTTPS, cursor pagination, RFC 9457 errors, idempotent POSTs | [API overview](../../api/overview/) |
| Authentication | API keys (`mtgz_...`) or OAuth 2.1 access tokens, both with scopes | [Authentication](../../api/authentication/) |
| Asynchronous jobs | Meeting processings and document generations run as jobs you can poll | [Jobs](../../api/jobs/) |
| Webhooks | Signed event notifications with retries | [Webhooks](../../api/webhooks/) |
| Usage billing | Monthly API allowance included in your plan, then optional on-demand billing | [Billing](../../api/billing/) |
| MCP server | Remote MCP server (Streamable HTTP + OAuth 2.1) for AI assistants | [MCP overview](../../mcp/overview/) |
| CLI | `meetingize` command line, built on the API | [CLI](../../cli/meetingize/) |
## Principles
- **Your data only.** Every credential belongs to one Meetingize user and only sees that user's meetings and
documents. Other accounts' resources answer `404`, never `403`.
- **Least privilege.** Keys and OAuth connections carry scopes (`read:meetings`, `read:transcripts`,
`write:documents`...). Give each integration only what it needs.
- **Pay for what you use.** API usage is metered from real consumption (audio minutes, LLM tokens) and
first debited from the allowance included in your plan. It never eats your web app quotas.
Next: the [quickstart](../quickstart/).
---
# Quickstart
This quickstart takes you from zero to your first API calls. You need a Meetingize account with at least one
processed meeting.
## 1. Create an API key
1. Sign in to the Meetingize web app.
2. Open the user menu, then **Developers** (Settings > Developers).
3. Click **Create a key**, give it a name and choose its scopes. For this quickstart keep `read:meetings`,
`read:transcripts`, `read:documents`, `write:meetings` and `write:documents`.
4. Copy the key now: it is shown only once. It looks like
`mtgz_live_k7Hq2x9a_...`.
```bash
export MEETINGIZE_API_KEY="mtgz_live_..."
export MEETINGIZE_API="https://app.meetingize.com/api/v1"
```
## 2. Check who you are
```bash
curl -s "$MEETINGIZE_API/me" -H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
The response tells you the account, the plan, the credential type (`api_key` or `oauth`), its scopes and your rate
limit.
## 3. List and search meetings
```bash
curl -s "$MEETINGIZE_API/meetings?limit=5" -H "Authorization: Bearer $MEETINGIZE_API_KEY"
curl -s "$MEETINGIZE_API/meetings?q=budget&created_after=2026-07-01T00:00:00Z" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
`q` searches titles, AI reports and transcripts (case-insensitive). Results are paginated with a cursor:
pass `next_cursor` back as `cursor` while `has_more` is `true`.
## 4. Read a transcript and the AI report
```bash
MEETING_ID="1b9e..." # an id from the previous call
curl -s "$MEETINGIZE_API/meetings/$MEETING_ID/transcript?format=md" -H "Authorization: Bearer $MEETINGIZE_API_KEY"
curl -s "$MEETINGIZE_API/meetings/$MEETING_ID/summary?format=md" -H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
Transcripts come as structured JSON segments (`start`, `end`, `speaker`, `speaker_name`, `text`) by default, or as
`md`, `txt`, `srt` and `vtt`. Need everything for an LLM? Use the [LLM-ready export](../../api/meetings-transcripts/#llm-ready-export).
## 5. Send a recording
```bash
curl -s -X POST "$MEETINGIZE_API/meetings" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-F file=@my-meeting.m4a -F title="Weekly sync" -F language=en
```
You get `202 Accepted` with the new meeting (`status: "queued"`) and its processing job. The full walkthrough
(statuses, polling or webhooks, what you receive at each step) is in
[Create a meeting and get the results](../../api/create-meeting/). Your key needs the `write:meetings` scope.
## 6. Generate a document
```bash
curl -s -X POST "$MEETINGIZE_API/meetings/$MEETING_ID/documents" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"prompt": "Write a client follow-up email with the decisions and next steps", "title": "Follow-up"}'
```
The answer is `202 Accepted` with a job. Poll it until `status` is `completed`, then read the document:
```bash
curl -s "$MEETINGIZE_API/jobs/job_..." -H "Authorization: Bearer $MEETINGIZE_API_KEY"
curl -s "$MEETINGIZE_API/documents/doc_...?format=md" -H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
Document generation consumes LLM tokens, billed on your [API allowance](../../api/billing/).
## 7. Get notified instead of polling
Register a webhook endpoint in **Settings > Developers** (or with `POST /v1/webhook_endpoints` and the `webhooks`
scope), subscribe to `document.generated`, and [verify the signature](../../api/webhooks/#verify-the-signature) of
each delivery.
## Prefer Python, Node or your AI assistant?
- The [API explorer](../../api/explorer/) generates code in many languages and lets you try calls with your key.
- The [`meetingize` CLI](../../cli/meetingize/) wraps all of the above in one command line.
- To use your meetings from Claude or ChatGPT without writing code, [connect the MCP server](../../mcp/overview/).
---
# What is Meetingize
Meetingize is an app for recording or importing real meetings (in person, phone, video), turning them into a transcript with speaker diarization, and generating an AI meeting report plus professional documents from templates adapted to your job. Documents can be sealed as a "proof of advice" record. It runs on the web and on iOS.
## Typical workflow
1. Record a meeting in your browser or on your iPhone, or import an existing audio/video file.
2. Meetingize transcribes the recording and identifies who said what (speaker diarization).
3. It generates an AI meeting report: summary, minutes table by speaker, key takeaways, action items.
4. From that report, generate a professional document (for example an advisory engagement letter or a suitability report) using a template adapted to your profession.
5. Edit the document, export it (PDF or Word), and optionally seal it to create a proof-of-advice record.
6. Share the meeting or the document with colleagues or clients, with read or write access.
## Who is Meetingize for
Meetingize is built for professionals who have client conversations and need a reliable record of what was discussed and advised: insurance brokers, wealth management advisors, lawyers, consultants, and other advisors who meet clients regularly. It is particularly useful for professionals subject to advice-duty regulations such as the Insurance Distribution Directive (IDD/DDA) or MiFID II, who must be able to document the demands, needs and advice given to each client.
## Learn more
- [Create an account and sign in](../account/)
- [Record a meeting](../recording/)
- [Import an audio or video file](../importing/)
- [Transcription and speaker diarization](../transcription/)
- [The AI meeting report](../meeting-report/)
- [Professional personas and document templates](../templates-personas/)
- [Generating and exporting documents](../documents/)
- [Proof of advice and sealing](../proof-of-advice/)
- [Sharing a meeting](../sharing/)
- [Clients, tags and search](../organizing/)
- [The iOS app](../mobile/)
- [Plans and quotas](../plans-quotas/)
- [Privacy and security](../privacy-security/)
- [Frequently asked questions](../faq/)
---
# Account and sign in
Your Meetingize account holds your meetings, documents and settings. You sign in with a magic link sent to your email, with Google, or with your email and password.
## How do I sign up or sign in?
- **Magic link by email**: enter your email address, Meetingize sends you a sign-in link, and clicking it logs you in (web and iOS use separate magic links).
- **Sign in with Google**: authenticate with your Google account instead of email.
- **Email and password**: the sign-in page also has a **Password** tab if you prefer a classic password.
## What happens at onboarding?
The first time you sign in, Meetingize asks you to choose a **professional persona** (for example insurance broker, wealth management advisor, lawyer, consultant) and your default language. The persona determines which document templates are suggested to you when you generate a document (see [Professional personas and document templates](../templates-personas/)). You can describe your responsibilities in a short free-text field.
## Profile and settings
Your profile includes your nickname, phone number, job title and a short description of your responsibilities. You can review and change your default language, your professional persona, and the applications connected to your account at any time from your account settings.
## Connected applications
If you have granted an AI assistant (through the Meetingize MCP server) access to your account, you can see and revoke that access from **Settings > Connected applications** (French UI: *Paramètres > Applications connectées*). The connected application only accesses your own data, within the permissions you granted.
## Can I link my mobile app to my web account?
Yes. See [The iOS app](../mobile/) for linking the iOS app to your existing web account.
## How do I delete my account?
You can request account deletion from your account settings. Meetingize keeps an internal deletion record for compliance purposes, but your personal data and content are removed.
## Good to know
- There is no "Sign in with Apple" option; the only sign-in methods are the email magic link and Google.
---
# Recording a meeting
Meetingize can record a meeting live, either in your web browser or with the iOS app, so it can transcribe it afterwards.
## Recording in the browser
Start a recording from the web app with your device's microphone. Your browser will ask for microphone permission the first time; you need to allow it for the recording to start. You can play back and review the recorded audio by segments once the recording is finished.
## Recording on iPhone
The iOS app includes a dedicated recorder that:
- Works offline: it keeps recording even without a network connection and uploads the audio once you're back online.
- Records in segments, so if the app is interrupted (a call comes in, the app is closed, the phone restarts), it can recover the in-progress recording and resume instead of losing it.
## Consent notice
Before recording, Meetingize shows a consent notice. In the browser, it is shown before any recording, solo or meeting. On the iPhone app, it is shown before a **meeting** recording only (a solo recording skips it); your acknowledgement is recorded on your device with a timestamp. This device-side consent trace is implemented today.
Coming soon Attaching this consent acknowledgement to the meeting record on the server side (so it travels with the meeting data itself) is planned but not yet implemented.
## Solo vs meeting recording
When you start a recording, you choose whether it is a **solo** recording (just you, for example personal notes or dictation) or a **meeting** recording (with other participants). This choice affects the consent notice shown and how the recording is categorized.
## Good to know
- Microphone access must be allowed in your browser or in iOS settings for Meetingize.
- Once a recording is uploaded, it goes through the same transcription pipeline as an imported file. See [Transcription and speaker diarization](../transcription/).
---
# Importing a file or text
Instead of recording live, you can import an audio or video file you already have, or paste text directly.
## How do I import an audio or video file?
Upload the file from the web app or the iOS app. Meetingize recognizes the following audio formats: `opus`, `webm`, `wav`, `aac`, `m4a`, `flac`, `mp3`, `ogg`, `oga`.
## What happens after I upload a file?
The file is stored on your account and queued for processing: transcription, then speaker diarization (see [Transcription and speaker diarization](../transcription/)). You can track the progress from the meeting list.
## Can I import by URL?
Coming soon Importing directly from a URL (for example a link to an online recording) is planned but not available yet. Download the file first, then import it.
## Can I paste text instead of audio?
Yes. You can enter text directly as the content input instead of uploading audio, for example to generate a document from notes you already took.
## Good to know
- There is no enforced file size or duration limit shown to you in the app; very large files are split internally for processing, but this is a technical detail and not a quota you manage.
- Images can be attached to a meeting; they are automatically resized if large.
---
# Transcription and speaker diarization
Once a recording or imported file is uploaded, Meetingize transcribes the audio to text and splits it by speaker (speaker diarization) automatically, without you having to specify how many people are speaking.
## How does transcription work?
Transcription uses speech recognition based on Whisper, run either on Meetingize's own infrastructure or, for scale and quality, via a cloud provider. Speaker diarization (figuring out who speaks when) runs as a separate step on top of the transcript.
## Which languages are supported?
Meetingize supports the same set of languages offered in the language selector in the app (dozens of languages), consistent with Whisper's multilingual support. You can set your default language in your account settings and override it per meeting.
## How do I identify or rename speakers?
The transcript is initially split into generic speaker labels (for example "Speaker 1"). The AI suggests a confidence level (low, medium, high) for who each speaker might be when it can tell from context. You can rename each speaker label to the actual person's name directly in the app.
## Can I edit the transcript?
Yes. You can edit the transcript text directly. Once you've manually edited it, your edit is kept and is not overwritten by later automatic re-processing.
## Good to know
- There is no fixed maximum number of speakers; diarization adapts automatically to the recording.
- Processing time depends on the length of the recording and current load; there is no fixed guaranteed delay shown in the app.
---
# The AI meeting report
Once a meeting is transcribed, Meetingize generates an AI meeting report: a structured summary of what was discussed, built automatically from the transcript.
## What does the meeting report contain?
- A **title** and **description** for the meeting.
- A **short summary** and a **long summary**.
- A **minutes table**, organized by speaker and timestamp, so you can see who said what and when.
- **Key takeaways** from the meeting.
- **Action items**, grouped by the speaker responsible for each one.
- **Speaker identification**: suggested names for each speaker label, with a confidence level.
## How do I edit the report?
You can edit the generated text directly in the app. Manual edits are preserved and are not overwritten by later automatic processing.
## Can I regenerate the report?
Regenerating the AI report yourself from the app is not currently available; edits you make directly in the report are the way to correct it after fixing the transcript.
## Good to know
- The report is generated by an AI model and should be reviewed before being relied on as a final record, especially for regulated advice documentation (see [Proof of advice](../proof-of-advice/)).
- The report feeds directly into document generation: see [Professional personas and document templates](../templates-personas/) and [Generating and exporting documents](../documents/).
---
# Professional personas and document templates
Meetingize adapts the documents it can generate to your profession through a **professional persona**. Each persona comes with a list of document types (templates) relevant to that job.
## What is a professional persona?
You choose your persona at onboarding (see [Account and sign in](../account/)) and can change it later in your settings. It determines which document types are suggested when you generate a document from a meeting.
## Which personas are available?
- Attorney/Legal Professional
- Physician/Medical Professional
- Therapist
- Financial Advisor/Wealth Manager
- Executive/C-Suite
- Teacher
- Corporate Trainer
- Real Estate Agent
- HR Consultant
- Engineer
- Software Developer
- Product Manager
- Project Manager
- Consultant
- Sales Professional
- Marketing Professional
- Researcher
- Designer
- Craftsperson/Artisan
- Sports Coach
- Building Expert/Inspector
- Chef/Restaurant Manager
- Accountant/CPA
- Insurance & Credit Broker
- Wealth Management Advisor (CGP)
## Document types for regulated advisors
Two personas are built specifically for advice-duty regulation (IDD/DDA and MiFID II):
**Insurance & Credit Broker**: Statement of Demands and Needs, Insurance Advice Record, Credit Advice Record, Advisory Engagement Letter, Insurance Portfolio Review, Claim Meeting Record.
**Wealth Management Advisor (CGP)**: Client Knowledge Questionnaire, Patrimonial Assessment, Suitability Report, Advisory Engagement Letter, Statement of Demands and Needs, Periodic Suitability Review.
Other personas have their own set of document types (for example a Financial Advisor can generate a Financial Plan, Investment Proposal, Portfolio Review, Retirement Plan, Risk Assessment, and more).
## Can I use a custom document type?
Yes. When generating a document, you can choose "Something else: go custom" instead of a predefined type, and enter a free-text title and description. Meetingize uses that as the basis for the document instead of a fixed template.
## Private documents
A document can be marked private, meaning it is visible only to you until you choose to share it with recipients.
## Good to know
- The list of document types per persona is managed by Meetingize and may evolve over time.
---
# Generating and exporting documents
From a transcribed meeting, you can generate a professional document using one of the templates for your persona (see [Professional personas and document templates](../templates-personas/)).
## How do I generate a document?
Open a meeting, choose a document type for your persona (or "Something else: go custom" for a free-text type), and launch generation. Generation runs asynchronously: Meetingize processes it in the background and you can track its progress, then open the finished document once ready.
## Can I edit the document?
Yes. The document content is markdown text that you can edit directly in the app's editor.
## Diagrams in documents
You can include Mermaid diagrams in a document by writing a ` ```mermaid ` code block in the markdown. Meetingize renders the diagram as an image in the exported PDF or Word file.
## How do I export a document?
- **PDF** and **Word**: generated by Meetingize and downloadable from the document.
- **Markdown**: exported directly as plain text from the app.
- Coming soon PowerPoint (PPTX) export is not available.
## Read-only export restriction
If a document was shared with you with read-only access, you can only export it as PDF; Word export is reserved to the document owner and to recipients with write access.
## Good to know
- Document generation is asynchronous; large or complex meetings may take a little time to produce a document.
- See [Proof of advice](../proof-of-advice/) to seal a document once it is finalized.
---
# Proof of advice and sealing
Meetingize can "seal" a document to create a time-stamped proof that it existed, in a given state, at a given moment. This is designed to support the documentation duties of regulated advisors (for example under the Insurance Distribution Directive or MiFID II).
## What is a seal, exactly?
When you seal a document, Meetingize computes a SHA-256 hash of its content and records that hash together with the sealing date and time in its own database. There is no blockchain and no third-party timestamping authority involved: the proof is a hash plus a timestamp, kept by Meetingize. Sealing a document that is already sealed does not create a duplicate seal.
## How do I seal a document?
Open the document and use the seal action. You can also seal directly from the meeting's generated output, without first exporting it as a separate document.
## QR code and attestation page
Once sealed, Meetingize generates a QR code for the seal and an attestation page titled "Preuve de conseil - Meetingize" (shown in French even in other locales) that shows the document name, the sealing date, a truncated version of the hash, and the QR code, without showing the document's content.
## Public verification
Anyone with the link (a client, or a regulator such as the ACPR or the AMF in France) can open the public verification page at `/verify/` without needing a Meetingize account. That page only confirms that a document with that hash existed as of that date; it never displays the document's content.
## Use cases
This is intended for advisors who must be able to show, after the fact, that a piece of advice or a document (such as a Statement of Demands and Needs, a Suitability Report, or an Advisory Engagement Letter) existed as given, unmodified, at a specific date.
## Good to know
- Sealing helps you **document** that advice was given and recorded at a point in time. It does not, by itself, certify that the advice given was compliant with any specific regulation; that assessment remains your professional responsibility.
---
# Sharing a meeting
You can share a meeting and its generated documents with other people, either by inviting them individually or with a shareable link.
## How do I share a meeting with someone?
Add a recipient by email address and choose whether they get **read** or **write** access. A recipient can also be marked as a participant of the meeting.
## Public sharing link
You can generate a public sharing link for a meeting instead of (or in addition to) inviting people by email. Anyone with the link can access the shared content according to the permissions attached to the link.
## What can a read-only recipient do?
A recipient with read-only access can view the meeting, its transcript and its documents, and export documents as PDF, but cannot edit content and cannot export as Word (see the read-only export restriction in [Generating and exporting documents](../documents/)).
## Shared with me
Meetings and documents that others have shared with you appear in a dedicated "shared with me" view, separate from your own meetings.
## Good to know
- Sharing rights (read/write) are set per recipient, so different people can have different levels of access to the same meeting.
---
# Clients, tags and search
As your number of meetings grows, Meetingize gives you a few ways to keep them organized: client folders, tags, and filters.
## Client folders
You can create **clients** (client folders) and attach a meeting to a client, so all the meetings and documents related to that person or company are grouped together. A client can be **archived** once you no longer need to see it in your active list.
## Tags
You can create tags and apply them to meetings. Filtering the meetings list by multiple tags returns meetings that match any of the selected tags.
## Searching and filtering the meetings list
The meetings list can be filtered by:
- One or more **tags**.
- A specific **client**.
- A **client name search** (matching on the client's name).
## Good to know
- There is no full-text search across transcripts or document content; filtering works through tags, client and client name only.
---
# The iOS app
Meetingize has a dedicated iOS app for recording meetings on the go.
## What can the iOS app do?
- Record a meeting with a segmented recorder that works offline and uploads the audio once you're back online.
- Recover an in-progress recording automatically if the app is interrupted (incoming call, app closed, phone restart).
- Show the consent notice before a meeting recording (not before a solo recording) and keep a device-side record of your acknowledgement (see [Recording a meeting](../recording/)).
- Sign in and sync with your Meetingize account.
## How do I link the app to my web account?
Sign in to the iOS app with its own magic-link email flow, or link it to an existing web account using a one-time code / magic link exchange between the mobile app and the web app.
## Where can I get the app?
The Meetingize app is available on the App Store for iOS.
Coming soon An Android app is not available yet.
## Good to know
- Audio recorded offline on iPhone is uploaded automatically once the device regains network access.
---
# Plans and quotas
Meetingize offers a Free plan and two paid plans, Standard and Pro.
## Plans and pricing
- **Free**: limited usage, no subscription required.
- **Standard** and **Pro**: paid monthly subscriptions with higher quotas. Check the current prices in the app or on the pricing page, as they may change.
## What do the quotas cover?
Paid plans include quotas on things such as:
- Audio hours processed per week and per month.
- Number of documents generated per billing period.
- How many items can process in parallel.
- How long audio and content are retained.
The exact numbers for the Standard and Pro plans are set in the app and may change; check your current limits and usage on the **Manage Plans** page (user menu), which shows your usage against your plan's maximums for the current period.
## Free plan limits
The Free plan has lifetime caps (not renewed weekly or monthly): **4 hours** of audio processed in total, and **10 documents** generated in total.
## What happens when I reach a quota?
Once you reach your audio-hours or document quota, further processing or document generation is blocked until the next period (for paid plans) or until you upgrade (for the Free plan's lifetime caps). The app tells you which limit was reached.
## Managing your subscription
- On the web, subscriptions are managed through Stripe, including upgrading, downgrading and the billing portal.
- On iOS, subscriptions go through the App Store (in-app purchase), including restoring a previous purchase.
## API usage
If you use the Meetingize API or MCP server outside the app, usage-based billing for that applies separately. See the [API billing page](../../api/billing/) in the developer documentation for details.
## Good to know
- The Free plan's 4-hour / 5-document caps are caps for the lifetime of the account, not reset periodically.
---
# Privacy and security
Meetingize stores your meetings, transcripts and documents under your own account, isolated from other users.
## Data ownership and isolation
Each account only has access to its own meetings, documents and clients, unless you explicitly share a meeting with someone else (see [Sharing a meeting](../sharing/)). A public sharing link or a recipient added to a meeting only grants access to that specific meeting, not to the rest of your account.
## Connected applications
If you connect an AI assistant to your Meetingize account (through the MCP server), it only accesses your own data, within the permissions you granted. You can review and disconnect any connected application at any time from **Settings > Connected applications**.
## Public verification pages
The one exception to "only you can see your data" is the proof-of-advice verification page (`/verify/`): it is intentionally public so a third party can confirm a document's seal, but it only ever shows the document's existence, date and hash, never its content. See [Proof of advice and sealing](../proof-of-advice/).
## Account deletion
You can request deletion of your account from your settings. Meetingize keeps an internal deletion log for compliance purposes, but your personal data and content are removed.
## Good to know
- Read-only recipients cannot edit shared content and cannot export it as Word, only as PDF.
---
# Frequently asked questions
## Can I record a phone call?
Meetingize does not record phone calls directly from the phone network. You can record audio with your microphone (in the browser or on iPhone) during an in-person or speakerphone conversation, or import an audio file of a call you already have (see [Importing a file or text](../importing/)).
## How many speakers can Meetingize identify?
There is no fixed maximum; speaker diarization detects the number of speakers automatically from the audio.
## Which languages does Meetingize support?
Dozens of languages are available in the language selector, consistent with the multilingual speech recognition used for transcription. See [Transcription and speaker diarization](../transcription/).
## Can I edit the transcript and the report?
Yes, both the transcript text and the AI meeting report can be edited directly in the app. Your manual edits are preserved.
## Who can see my meetings?
Only you, unless you explicitly share a meeting with someone by email or with a public link. See [Sharing a meeting](../sharing/) and [Privacy and security](../privacy-security/).
## Can my AI assistant read my meetings?
Yes, if you connect it through the Meetingize MCP server, which gives AI assistants such as Claude or ChatGPT access to your own data within the permissions you grant. See the [MCP server overview](../../mcp/overview/). You can disconnect it at any time from Settings > Connected applications.
## What is "proof of advice" / sealing?
Sealing a document records a hash and a timestamp so you can later prove it existed, unmodified, at a given date. See [Proof of advice and sealing](../proof-of-advice/).
## Can I export a document as PowerPoint?
Coming soon Not currently. Documents export to PDF, Word or Markdown.
## Can I import a meeting from a URL?
Coming soon Not yet; you can upload a file directly instead.
## Is there an Android app?
Coming soon Not yet; Meetingize is currently available on iOS and on the web.
## What happens if I reach my plan's quota?
Processing or document generation is blocked until the next billing period, or until you upgrade for the Free plan's lifetime caps. See [Plans and quotas](../plans-quotas/).
## Can someone I share a meeting with edit it?
Only if you gave them write access; read-only recipients can view and export as PDF, but not edit or export as Word.
## Does Meetingize use blockchain for proof of advice?
No. A seal is a SHA-256 hash and a timestamp stored by Meetingize, not a blockchain record or a third-party timestamping service.
## Can I search inside my transcripts?
Not with full-text search today; you can filter your meetings list by tag, client or client name. See [Clients, tags and search](../organizing/).
## How do I choose which document templates I see?
By choosing your professional persona in your account settings. See [Professional personas and document templates](../templates-personas/).
---
# API overview
The Meetingize REST API v1 gives programmatic access to your meetings, diarized transcripts, AI reports and
documents, lets you create meetings from recordings and generate documents, and manages webhooks. It is a JSON API over
HTTPS described by an [OpenAPI 3.1 specification](/openapi.json).
## Base URL
All endpoints live under:
```
https://app.meetingize.com/api/v1
```
Requests must use HTTPS and carry a bearer credential (see [Authentication](../authentication/)).
## Versioning
The API version is `2026-10-01`. It is returned in the `Meetingize-Version` response header and in the
`api_version` field of webhook events. Backward compatible additions (new fields, new endpoints, new event types)
can happen at any time: ignore unknown fields. Breaking changes will come with a new version.
## Conventions
- JSON in UTF-8, dates in ISO 8601 UTC (`2026-10-11T14:03:22Z`).
- Amounts are EUR **decimal strings** (`"0.0123"`), never floats.
- Every response carries an `X-Request-Id` header, also present in error bodies. Give it to support when you
report a problem.
- Errors follow RFC 9457 problem details with a stable `code`: see [Errors and limits](../errors/).
## Identifiers
| Object | Format | Example |
|---|---|---|
| Meeting | UUID | `1b9e5c4a-...` |
| Document | `doc_` | `doc_1234` |
| Template | `tpl_` | `tpl_42` |
| Job | `job_` | `job_9b0c2d...` |
| Webhook endpoint | `we_` | `we_3f...` |
| Webhook event / delivery | `evt_` / `whd_` | |
| Ledger entry | `led_` | |
## Pagination
List endpoints use cursor pagination:
```http
GET /v1/meetings?limit=20&cursor=eyJ...
```
```json
{ "data": [ ... ], "has_more": true, "next_cursor": "eyJ..." }
```
`limit` defaults to 20 (max 100 on most lists). Keep calling with `cursor=next_cursor` while `has_more` is `true`.
An altered cursor returns `400 invalid_cursor`.
## Idempotency
All `POST` requests accept an `Idempotency-Key` header (any unique string, for example a UUID), remembered for
24 hours:
- same key and same body: the original response is replayed, with `Idempotent-Replayed: true`;
- same key, different body: `422 idempotency_key_reused`;
- same key while the first request is still running: `409 idempotency_key_in_use`.
Always send one when you create jobs, so that a network retry never starts the same transcription twice.
## Endpoints at a glance
| Area | Endpoints | Scope |
|---|---|---|
| Account | `GET /me` | any |
| Meetings | `GET /meetings`, `GET /meetings/{id}`, `/summary`, `/participants`, `/export` | `read:meetings` |
| Transcripts | `GET /meetings/{id}/transcript` | `read:transcripts` |
| Templates | `GET /templates`, `GET /templates/{id}`, `GET /personas` | `read:meetings` or `write:documents` |
| Documents | `POST /meetings/{id}/documents`, `GET /documents`, `GET /documents/{id}` | `write:documents` / `read:documents` |
| Create, update, delete meetings | `POST /meetings`, `PATCH /meetings/{id}`, `DELETE /meetings/{id}` | `write:meetings` |
| Feedback | `POST /feedback` | any |
| Jobs | `GET /jobs`, `GET /jobs/{id}` | any |
| Usage | `GET /usage`, `GET /usage/ledger` | `read:usage` |
| Webhooks | `/webhook_endpoints` and sub-resources | `webhooks` |
Full details in the [reference](../reference/meetings/) and the interactive [API explorer](../explorer/).
---
# Authentication
Every API request must carry a bearer credential in the `Authorization` header. Two kinds of credentials exist,
with the same scopes and the same per-user isolation:
- **API keys** for your own scripts, servers and integrations;
- **OAuth 2.1 access tokens** for applications acting on behalf of other Meetingize users (AI assistants via the
MCP server, third-party apps).
```http
Authorization: Bearer mtgz_live_k7Hq2x9a_3bV...
```
## API keys
Create keys in the web app, **Settings > Developers > Create a key**. Keys are never created or revoked through
the API itself.
- Format: `mtgz_live__`.
- The full key is displayed **once**. Meetingize only stores a SHA-256 hash of it, plus the prefix
(`mtgz_live_k7Hq2x9a`) and the last 4 characters for display.
- Each key has a name, scopes, an optional expiration date, a "last used" date and IP, and can be revoked at any
time. Revocation is immediate.
- Treat keys like passwords: keep them server-side, in a secret manager or environment variable, never in a
browser or a mobile app.
## Scopes
| Scope | Grants |
|---|---|
| `read:meetings` | list and search meetings, details, AI report, participants, templates, personas |
| `read:transcripts` | transcripts (`json`, `md`, `txt`, `srt`, `vtt`); with `read:meetings`, the LLM-ready export |
| `read:documents` | list and download generated documents (`json`, `md`, `pdf`, `docx`) |
| `write:documents` | generate documents (asynchronous jobs, billed on usage) |
| `write:meetings` | create meetings from audio or video files or URLs (billed on usage), update their title, date, tags and participants, delete them |
| `webhooks` | manage webhook endpoints through the API, send tests, read the delivery log |
| `read:usage` | current consumption, wallet and ledger |
`GET /me` and `GET /jobs` work with any valid credential (a job is only visible to its owner). A call without the
required scope answers `403 insufficient_scope`.
## OAuth 2.1
Meetingize is an OAuth 2.1 authorization server. It is what AI assistants use when you connect the
[MCP server](../../mcp/overview/), and any application can use it to act on behalf of a Meetingize user with
their explicit consent.
| Item | Value |
|---|---|
| Discovery (RFC 8414) | `https://app.meetingize.com/.well-known/oauth-authorization-server` |
| Protected resource metadata (RFC 9728) | `https://app.meetingize.com/.well-known/oauth-protected-resource/api/mcp` |
| Authorization endpoint | `https://app.meetingize.com/api/oauth/authorize` |
| Token endpoint | `https://app.meetingize.com/api/oauth/token` |
| Dynamic client registration (RFC 7591) | `https://app.meetingize.com/api/oauth/register` |
| Revocation (RFC 7009) | `https://app.meetingize.com/api/oauth/revoke` |
| Grant types | `authorization_code` (with PKCE), `refresh_token` |
| PKCE | required, `S256` only |
| Client authentication | `none` (public clients), `client_secret_post`, `client_secret_basic` |
| Client ID Metadata Documents | supported: an `https://` URL can be used as `client_id` |
| Access token lifetime | 1 hour, refresh tokens 30 days with rotation |
### Flow
1. **Register** your client once (or use a Client ID Metadata Document):
```bash
curl -s -X POST https://app.meetingize.com/api/oauth/register \
-H "Content-Type: application/json" \
-d '{"client_name": "My CRM sync", "redirect_uris": ["https://crm.example.com/oauth/callback"],
"grant_types": ["authorization_code", "refresh_token"], "token_endpoint_auth_method": "none"}'
```
Redirect URIs must be `https`, `http` on a loopback address (`localhost`, `127.0.0.1`) for native apps, or a
private-use scheme (`myapp://...`).
2. **Redirect the user** to the authorization endpoint with `response_type=code`, your `client_id`,
`redirect_uri`, `scope`, `state`, `code_challenge` (S256) and optionally `resource`. The user signs in to
Meetingize if needed, sees a consent screen listing the requested permissions, and allows or denies.
3. **Exchange the code** at the token endpoint with the `code_verifier`:
```bash
curl -s -X POST https://app.meetingize.com/api/oauth/token \
-d grant_type=authorization_code -d code=... -d redirect_uri=https://crm.example.com/oauth/callback \
-d client_id=... -d code_verifier=...
```
4. **Call the API** with `Authorization: Bearer ` and refresh it with `grant_type=refresh_token`
before it expires.
The `resource` parameter (RFC 8707) is accepted for the MCP server URL (`https://app.meetingize.com/api/mcp`) and the
API base URL; any other value is refused with `invalid_target`.
### What the user controls
Users see every connected application in **Settings > Connected applications**, with the permissions granted and
the last use, and can disconnect any of them. Disconnection revokes access and refresh tokens immediately.
## Errors
| Status | `code` | Meaning |
|---|---|---|
| 401 | `missing_credentials` | no `Authorization` header |
| 401 | `invalid_api_key`, `invalid_token` | unknown, malformed or expired credential |
| 401 | `api_key_revoked`, `api_key_expired` | the key was revoked or reached its expiration date |
| 403 | `insufficient_scope` | the credential lacks the scope required by the endpoint |
---
# Create a meeting and get the results
This guide walks through the full life of a meeting created through the API: you send a recording, Meetingize
transcribes it with speaker diarization, writes the AI meeting report and, if you ask for it, generates documents.
At each step you see what to call and what you get back.
You need an API key with the `write:meetings` scope (to create) and `read:meetings` + `read:transcripts`
(+ `read:documents` if you generate documents) to read the results.
## Step 1: send the recording
`POST /v1/meetings` **creates a meeting from an audio or video file and starts its processing.** Send either the
file itself (multipart) or a public `https` URL that Meetingize downloads.
```bash
# With a file (multipart/form-data)
curl -s -X POST "https://app.meetingize.com/api/v1/meetings" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-F file=@rdv-dupont.m4a \
-F title="Annual review - Mr Dupont" \
-F recorded_at=2026-10-14T09:30:00Z \
-F language=fr \
-F 'participants=[{"name": "Paul Dupont", "email": "paul.dupont@example.com"}]' \
-F 'tags=["client", "dupont"]'
```
```bash
# With a URL (application/json)
curl -s -X POST "https://app.meetingize.com/api/v1/meetings" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"audio_url": "https://files.example.com/calls/2026-10-14-dupont.mp3",
"title": "Annual review - Mr Dupont",
"recorded_at": "2026-10-14T09:30:00Z",
"language": "fr",
"document_templates": ["tpl_42"],
"metadata": {"crm_id": "C-1042"}
}'
```
### Options
| Field | What it does |
|---|---|
| `file` or `audio_url` | the recording, exactly one of them. Files up to 2 GB (mp3, m4a, wav, flac, ogg, webm, mp4...). URLs must be public `https` addresses |
| `title` | meeting title (max 300 characters). Without it, the AI report names the meeting |
| `recorded_at` | when the meeting took place (ISO 8601). Default: now |
| `language` | ISO 639-1 code (`fr`, `en`...) or `auto` (default) |
| `recording_type` | `meeting` (several speakers, diarization, default) or `solo` (dictation) |
| `participants` | list of `{name, email}` (max 50). Speaker attribution can be added later |
| `tags` | tag names (max 20) |
| `client_id` or `client` | file the meeting in a client folder: an existing `cli_` id (`GET /v1/clients`), or `client` = `{"name", "email", "phone"}` found by name or created |
| `transcribe_only` | `true`: transcript only, no AI report and no documents (faster and cheaper) |
| `document_templates` | up to 5 template ids (`GET /v1/templates`) to generate **after** the AI report |
| `persona` | a persona id (`GET /v1/personas`): without `document_templates`, generates that persona's templates (max 5) |
| `metadata` | your own JSON object (max 4 KB), returned in jobs and webhook events |
In multipart requests, `participants`, `tags` and `document_templates` are JSON strings (tags and templates also
accept a comma-separated list).
### What you get back
`202 Accepted` with the new meeting and its processing **job**. Keep `id` (the meeting) and `job.id`.
```json
{
"id": "6f0c2a4e-1b7d-4c55-9a51-3e2d8f0b7c11",
"object": "meeting",
"title": "Annual review - Mr Dupont",
"status": "queued",
"recorded_at": "2026-10-14T09:30:00Z",
"created_at": "2026-10-14T10:02:11Z",
"language": "fr",
"source": "api",
"transcribe_only": false,
"tags": ["client", "dupont"],
"has_transcript": false,
"has_summary": false,
"web_url": "https://app.meetingize.com/meeting/detail?uuid=6f0c2a4e-1b7d-4c55-9a51-3e2d8f0b7c11",
"summary": null,
"participants": [
{ "id": "par_311", "name": "Paul Dupont", "email": "paul.dupont@example.com", "speaker_labels": [] }
],
"documents": [],
"job": {
"id": "job_9b0c2d1e",
"object": "job",
"kind": "meeting_processing",
"status": "queued",
"stage": "queued",
"meeting_id": "6f0c2a4e-1b7d-4c55-9a51-3e2d8f0b7c11",
"document_templates": ["tpl_42"],
"document_job_ids": [],
"metadata": { "crm_id": "C-1042" },
"created_at": "2026-10-14T10:02:11Z"
}
}
```
Errors you may meet at this step: `400 validation_error` (a field is wrong, see `errors[]`), `413 file_too_large`,
`402 insufficient_balance` or `spend_cap_reached` ([billing](../billing/)), `429 parallel_processing_limit`
(your plan's simultaneous processings are all busy, see [limits](../limits/)).
## Step 2: follow the processing
The job goes through these states:
| `status` | `stage` | Meaning |
|---|---|---|
| `queued` | `queued` | waiting for a worker |
| `processing` | `downloading` | `audio_url` only: Meetingize is downloading the file |
| `processing` | `transcribing` | transcription and speaker diarization |
| `processing` | `summarizing` | the transcript is ready, the AI report is being written |
| `completed` | `done` | transcript and AI report ready (transcript only with `transcribe_only`) |
| `failed` | | see `error.code` and `error.message` |
The meeting's own `status` follows the same vocabulary (`queued`, `processing`, `completed`, `failed`).
Count roughly a quarter of the audio duration for the transcript, plus a minute or two for the report.
### Option A: polling
```bash
curl -s "https://app.meetingize.com/api/v1/jobs/job_9b0c2d1e" -H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
```json
{ "id": "job_9b0c2d1e", "object": "job", "kind": "meeting_processing",
"status": "processing", "stage": "summarizing", "meeting_id": "6f0c2a4e-...", "progress": null }
```
Poll every 10 to 30 seconds, and stop when `status` is `completed` or `failed`. Polling does not count against your
API allowance, only against the [rate limit](../limits/).
### Option B: webhooks (recommended)
Register an endpoint once ([webhooks](../webhooks/)) and Meetingize calls you:
| Event | When | What you can read now |
|---|---|---|
| `transcription.completed` | the diarized transcript is ready (stage `summarizing`) | the transcript |
| `processing.completed` | the AI report is ready, job `completed` | transcript, report, participants |
| `document.generated` | each requested document is ready | the document |
| `transcription.failed`, `processing.failed`, `document.failed` | a step failed | `error` in the event |
```json
{
"id": "evt_7d1f...",
"object": "event",
"type": "processing.completed",
"created": "2026-10-14T10:09:42Z",
"api_version": "2026-10-01",
"data": {
"object": {
"id": "job_9b0c2d1e", "object": "job", "kind": "meeting_processing", "status": "completed", "stage": "done",
"meeting_id": "6f0c2a4e-...", "document_job_ids": ["job_a41f..."], "metadata": { "crm_id": "C-1042" },
"meeting": { "id": "6f0c2a4e-...", "object": "meeting", "title": "Annual review - Mr Dupont", "status": "completed" }
}
}
}
```
Your `metadata` comes back in every event, which makes it easy to match the event with your own record.
## Step 3: read the transcript
As soon as the stage reaches `summarizing` (event `transcription.completed`):
```bash
curl -s "https://app.meetingize.com/api/v1/meetings/6f0c2a4e-.../transcript" -H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
```json
{
"object": "transcript",
"meeting_id": "6f0c2a4e-...",
"language": "fr",
"duration_seconds": 2412,
"speakers": ["SPEAKER_00", "SPEAKER_01"],
"segments": [
{ "start": 14.2, "end": 21.9, "speaker": "SPEAKER_00", "speaker_name": null,
"text": "Je m'engage à vous envoyer une proposition d'allocation avant vendredi." }
]
}
```
Add `?format=md`, `txt`, `srt` or `vtt` for text formats. Before that point the endpoint answers
`409 transcript_not_ready`.
## Step 4: read the AI report
When the job is `completed` (event `processing.completed`):
```bash
curl -s "https://app.meetingize.com/api/v1/meetings/6f0c2a4e-.../summary?format=md" -H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
The JSON format returns `markdown` plus a `structured` object (short and long summary, minutes, key takeaways,
action items, speaker identification). `GET /v1/meetings/{id}` returns everything at once: metadata, report,
participants and documents. Not available with `transcribe_only`.
## Step 5: get the documents
Documents listed in `document_templates` (or the `persona` templates) are generated after the report. Each one is a
child job (`document_job_ids`, kind `document_generation`) that ends with a `document.generated` event:
```bash
curl -s "https://app.meetingize.com/api/v1/documents?meeting_id=6f0c2a4e-..." -H "Authorization: Bearer $MEETINGIZE_API_KEY"
curl -s "https://app.meetingize.com/api/v1/documents/doc_1234?format=md" -H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
Formats: `json` (metadata and Markdown), `md`, `pdf`, `docx`. You can also generate documents later at any time with
`POST /v1/meetings/{id}/documents` ([documents](../documents/)).
## Step 6 (optional): fix the metadata
`PATCH /v1/meetings/{id}` **updates the title, the date, the tags, the client folder and the participants of a
meeting**, for example
to say which speaker is who once you know it:
```bash
curl -s -X PATCH "https://app.meetingize.com/api/v1/meetings/6f0c2a4e-..." \
-H "Authorization: Bearer $MEETINGIZE_API_KEY" -H "Content-Type: application/json" \
-d '{"participants": {"update": [{"id": "par_311", "speaker_labels": ["SPEAKER_01"]}],
"add": [{"name": "Claire Martin", "speaker_labels": ["SPEAKER_00"]}]}}'
```
Participants changes are `add`, `update` (only the fields you send change; `speaker_labels` replaces the list) and
`remove` (a list of `par_` ids). No email is ever sent to participants by the API.
`DELETE /v1/meetings/{id}` **deletes the meeting** with its recording, transcript, report and documents; it cannot be
undone.
## Checklist
- Always send an [`Idempotency-Key`](../idempotency/) on `POST`: a retry never creates a second meeting.
- Prefer webhooks to polling for integrations; keep polling as a fallback.
- Use `transcribe_only` when you only need the text: faster and cheaper.
- Put your own identifiers in `metadata`.
- Respect `429` and `Retry-After` ([limits](../limits/)).
---
# Documents and templates
Meetingize generates documents from a meeting: minutes, follow-up emails, proposals, suitability reports and
any custom document. Generation runs as an asynchronous [job](../jobs/) and consumes LLM tokens
billed on your [API usage](../billing/).
## Which templates can I use?
```http
GET /v1/personas
GET /v1/templates?persona=INSURANCE_BROKER
GET /v1/templates/{template_id}
```
Templates (`tpl_`) are grouped by professional persona (`INSURANCE_BROKER`, `FINANCIAL_ADVISOR`, `ATTORNEY`, `PROJECT_MANAGER`...).
Each template has an `id`, `name`, `description`, `icon` and `persona`.
## How do I generate a document?
```http
POST /v1/meetings/{meeting_id}/documents
Idempotency-Key: 6f1d...
Content-Type: application/json
{ "template_id": "tpl_42", "title": "Rapport d'adéquation", "instructions": "Mention the client's risk profile." }
```
| Field | Description |
|---|---|
| `template_id` | a template id; mutually exclusive with `prompt` |
| `prompt` | your own generation instructions (up to 20,000 characters) instead of a template |
| `title` | document title (up to 300 characters) |
| `instructions` | extra instructions appended to the template (up to 5,000 characters) |
| `include_transcript` | include the full transcript in the context (default `true`) |
| `metadata` | your own JSON object, echoed in the job and webhook events |
Exactly one of `template_id` or `prompt` is required. The response is `202 Accepted` with a job of kind
`document_generation`. When the job is `completed` its `document_id` is set and a `document.generated` webhook is sent
(`document.failed` on error).
Documents generated through the API do not count against the document quota of your web app plan: they are
billed on API usage instead.
## Listing and downloading documents
```http
GET /v1/documents?meeting_id={meeting_id}
GET /v1/documents/{document_id}?format=json # metadata + markdown
GET /v1/documents/{document_id}?format=md # text/markdown
GET /v1/documents/{document_id}?format=pdf # application/pdf
GET /v1/documents/{document_id}?format=docx # Word
```
Scope `read:documents`. Documents marked as private by another participant of a shared meeting are not
listed.
---
# Meetings and transcripts
A **meeting** is one recording or import in Meetingize: its metadata, the diarized transcript, the AI meeting report
(called summary in the API) and the documents generated from it. Meetings are identified by a UUID.
To create a meeting from a recording, see [Create a meeting and get the results](../create-meeting/).
## How do I list or search meetings?
`GET /v1/meetings` **returns your meetings, most recent first, optionally filtered.**
```http
GET /v1/meetings?q=budget&client=Dupont&tag=rdv&recorded_after=2026-07-01T00:00:00Z&status=completed&limit=20
```
| Parameter | Description |
|---|---|
| `q` | case-insensitive text search in the title, the AI report and the transcript |
| `tag` | tag name, repeatable (`tag=a&tag=b`) |
| `client` | client folder name |
| `recorded_after`, `recorded_before` | date of the meeting (ISO 8601 date-times) |
| `created_after`, `created_before` | date it was added to Meetingize |
| `status` | `draft`, `queued`, `processing`, `completed`, `failed` |
| `limit`, `cursor` | [pagination](../overview/#pagination) |
Each meeting has `id`, `title`, `status`, `recorded_at`, `created_at`, `transcribe_only`, `duration_seconds`, `language`, `source`, `recording_type`, `tags`, `client`, `has_transcript`, `has_summary` and
`web_url` (link to open it in the web app).
### Meeting status
| Status | Meaning |
|---|---|
| `draft` | being recorded in the web or mobile app, no audio finalized yet |
| `queued` | waiting for processing |
| `processing` | transcription, then AI report (the transcript is readable as soon as `has_transcript` is `true`) |
| `completed` | transcript and AI report available (transcript only for `transcribe_only` meetings) |
| `failed` | processing failed |
## Meeting details
`GET /v1/meetings/{id}` **returns one meeting with everything about it**: its `summary` (AI report), its
`participants`, the list of generated `documents` and, for meetings created through the API, its latest processing
`job`.
`PATCH /v1/meetings/{id}` **updates the title, the date, the tags and the participants** (scope `write:meetings`),
and `DELETE /v1/meetings/{id}` **permanently deletes the meeting** with its recording and documents. Examples in
[step 6 of the guide](../create-meeting/#step-6-optional-fix-the-metadata).
## Transcript
`GET /v1/meetings/{id}/transcript` **returns the diarized, timestamped transcript.**
```http
GET /v1/meetings/{id}/transcript?format=json
```
Scope `read:transcripts`. The JSON format returns the language, duration, the list of speakers and the segments:
```json
{
"object": "transcript",
"meeting_id": "1b9e...",
"language": "fr",
"speakers": ["SPEAKER_00", "SPEAKER_01"],
"segments": [
{ "start": 4.5, "end": 9.0, "speaker": "SPEAKER_01", "speaker_name": "Mme Martin",
"text": "Merci. Le budget est validé pour le trimestre." }
]
}
```
`speaker` is the diarization label; `speaker_name` is filled when the speaker was identified in Meetingize.
Other formats: `md` (Markdown), `txt` (`[hh:mm:ss] Speaker: text` lines), `srt` and `vtt` (subtitles, the VTT
includes `` voice tags). While transcription is still running the endpoint answers
`409 transcript_not_ready`.
## AI meeting report
`GET /v1/meetings/{id}/summary` **returns the AI meeting report.**
```http
GET /v1/meetings/{id}/summary?format=json # or format=md
```
Returns the report in Markdown (`markdown`) and, when available, the `structured` result produced by the AI
(summary, minutes, key takeaways, action items, speaker identification). `409 summary_not_ready` while processing.
## Participants
`GET /v1/meetings/{id}/participants` **lists the people of the meeting** with their `id` (`par_`), `name`,
`email` and the `speaker_labels` attributed to them. Speakers of the transcript not yet attributed to anyone are
listed with `id: null`. Attribute them with `PATCH /v1/meetings/{id}`.
## LLM-ready export
`GET /v1/meetings/{id}/export` **bundles everything about a meeting into text ready to paste into an LLM.**
```http
GET /v1/meetings/{id}/export?format=md&max_tokens=24000&lang=en
```
Scopes `read:meetings` and `read:transcripts`. Bundles a priming prompt, the metadata, the participants, the AI
report and the diarized timestamped transcript, split into parts of at most `max_tokens` tokens (2,000 to
200,000, default 24,000) so each part fits in a model context window. `lang` (`fr` default, or `en`) sets the
language of the priming prompt. `format=json` returns `parts[]` with `index`, `total`, `tokens` and `content`;
`format=md` concatenates the parts with `--- Part i/n ---` separators.
This is the export to paste into ChatGPT or Claude by hand. To let your assistant fetch meetings itself, use the
[MCP server](../../mcp/overview/).
---
# Jobs
Everything slow in Meetingize runs as an asynchronous **job**: processing a new meeting (transcription, diarization,
AI report) and generating a document. The `POST` that starts the work answers immediately with the job; you then
follow it by polling or with [webhooks](../webhooks/).
## Kinds of jobs
| `kind` | Started by | Ends with |
|---|---|---|
| `meeting_processing` | `POST /v1/meetings` | the transcript and the AI report (transcript only with `transcribe_only`) |
| `document_generation` | `POST /v1/meetings/{id}/documents`, or `document_templates` at meeting creation | a document (`document_id`) |
## Statuses and stages
`status` is the coarse state, `stage` the detail:
| `status` | `stage` (meeting processing) | `stage` (document generation) |
|---|---|---|
| `queued` | `queued` | `queued` |
| `processing` | `downloading` (with `audio_url`), `transcribing`, `summarizing` | `generating` |
| `completed` | `done` | `done` |
| `failed` | the stage where it failed | |
| `canceled` | the processing was canceled (for example the meeting was deleted while it was running) | |
A meeting processing in stage `summarizing` already has its transcript; it becomes `completed` when the AI report is
ready.
## Get a job
`GET /v1/jobs/{job_id}` **returns the status, the stage, the ids of what was produced and the cost of one job.** It
is what you poll.
```json
{
"id": "job_9b0c2d1e",
"object": "job",
"kind": "meeting_processing",
"status": "completed",
"stage": "done",
"progress": null,
"meeting_id": "6f0c2a4e-1b7d-4c55-9a51-3e2d8f0b7c11",
"document_id": null,
"parent_job_id": null,
"transcribe_only": false,
"document_templates": ["tpl_42"],
"document_job_ids": ["job_a41f07c2"],
"error": null,
"usage": { "cost_eur": "0.0581", "wallet_eur": "0.0581", "on_demand_eur": "0.0000", "ledger_entry_ids": ["led_..."] },
"metadata": { "crm_id": "C-1042" },
"created_at": "2026-10-14T10:02:11Z",
"started_at": "2026-10-14T10:02:13Z",
"finished_at": "2026-10-14T10:09:42Z"
}
```
| Field | Meaning |
|---|---|
| `meeting_id` | the meeting concerned (known from the start) |
| `document_id` | `document_generation`: the document once completed |
| `document_job_ids` | `meeting_processing`: the document jobs started after the report (from `document_templates` or `persona`) |
| `parent_job_id` | `document_generation`: the meeting job that requested it at creation |
| `error` | `{code, message}` when the job failed |
| `usage` | cost of the job once metered, see [billing](../billing/) |
| `metadata` | the object you sent, unchanged |
## List jobs
`GET /v1/jobs` **returns your jobs, most recent first**, filtered with `status` (`queued`, `processing`,
`completed`, `failed`, `canceled`) and `kind`. Handy to see what occupies your
[parallel processing slots](../limits/#parallel-processing-quota):
```bash
curl -s "https://app.meetingize.com/api/v1/jobs?status=processing" -H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
## Poll or subscribe?
- **Polling**: call `GET /v1/jobs/{id}` every 10 to 30 seconds until `completed`, `failed` or `canceled`. Simple
and fine for scripts.
- **Webhooks**: `transcription.completed`, `processing.completed`, `document.generated` and the `.failed`
events carry the job in `data.object`. Recommended for integrations.
The step-by-step guide [Create a meeting and get the results](../create-meeting/) shows both.
---
# Webhooks
Webhooks notify your server when something happens in Meetingize, so you do not have to poll. Each delivery is an
HTTPS `POST` with a JSON event, signed with a secret only you and Meetingize know.
## Register an endpoint
In the web app: **Settings > Developers > Add an endpoint**. Or through the API with the `webhooks` scope:
```bash
curl -s -X POST "$MEETINGIZE_API/webhook_endpoints" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY" -H "Content-Type: application/json" \
-d '{"url": "https://hooks.example.com/meetingize", "events": ["transcription.completed", "document.generated"]}'
```
The response contains the signing `secret` (`whsec_...`), shown only once. URLs must use `https` and resolve to a
public address (private and loopback addresses are refused). To test locally, expose your endpoint with a tunnel
(for example ngrok or Cloudflare Tunnel).
## Events
| Type | When | `data.object` |
|---|---|---|
| `transcription.completed` | the diarized transcript is available | job (+ `meeting`) |
| `transcription.failed` | the transcription failed | job with `error` |
| `processing.completed` | the AI meeting report is ready | job (+ `meeting`) |
| `processing.failed` | the AI report failed after a successful transcription | job with `error` |
| `document.generated` | a document generation succeeded | job (+ `document`) |
| `document.failed` | a document generation failed | job with `error` |
| `usage.threshold_reached` | API usage of the month reached 50, 80 or 100 % of the wallet, or 80 or 100 % of the spend cap | `usage_alert` |
| `ping` | "send a test" button or `POST /webhook_endpoints/{id}/test` | `{ "message": "..." }` |
Subscribe to `"*"` to receive every event except `ping`.
```json
{
"id": "evt_7d1f...",
"object": "event",
"type": "transcription.completed",
"created": "2026-10-11T14:03:22Z",
"api_version": "2026-10-01",
"livemode": false,
"data": {
"object": {
"id": "job_9b0c...", "object": "job", "kind": "transcription", "status": "running",
"stage": "processing", "meeting_id": "1b9e...", "metadata": { "crm_id": "42" },
"meeting": { "id": "1b9e...", "object": "meeting", "title": "RDV client", "status": "transcribed" }
}
}
}
```
## Headers
```http
Content-Type: application/json
User-Agent: Meetingize-Webhooks/1.0
Meetingize-Event-Id: evt_7d1f...
Meetingize-Event-Type: transcription.completed
Meetingize-Delivery-Id: whd_...
Meetingize-Signature: t=1760191402,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
```
## Verify the signature
`v1` is the hex HMAC-SHA256 of `"{t}.{raw_body}"` with your full secret (`whsec_...`) as key. To verify:
1. read the **raw** request body (before any JSON parsing);
2. recompute the HMAC and compare it in constant time with each `v1=` value of the header;
3. refuse the event if `t` is more than 5 minutes away from now (replay protection);
4. deduplicate on `Meetingize-Event-Id` (deliveries are at least once).
During a secret rotation the old secret stays valid for 24 hours and the header carries two `v1=` values.
### Python
```python
def verify_meetingize_signature(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
items = [p.split("=", 1) for p in header.split(",") if "=" in p]
timestamps = [v for k, v in items if k == "t"]
signatures = [v for k, v in items if k == "v1"]
if not timestamps or not signatures:
return False
t = int(timestamps[0])
if abs(time.time() - t) > tolerance:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return any(hmac.compare_digest(expected, s) for s in signatures)
# Flask example
from flask import Flask, request, abort
app = Flask(__name__)
@app.post("/meetingize")
def meetingize_webhook():
if not verify_meetingize_signature(request.get_data(), request.headers.get("Meetingize-Signature", ""),
secret="whsec_..."):
abort(400)
event = request.get_json()
# enqueue work, return fast
return "", 204
```
### Node.js
```js
function verifyMeetingizeSignature(rawBody, header, secret, toleranceSec = 300) {
const parts = (header || '').split(',').map((p) => p.split('='));
const t = Number((parts.find(([k]) => k === 't') || [])[1]);
const sigs = parts.filter(([k]) => k === 'v1').map(([, v]) => v);
if (!t || !sigs.length || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = crypto.createHmac('sha256', secret).update(`${t}.`).update(rawBody).digest('hex');
return sigs.some((s) => s.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(s), Buffer.from(expected)));
}
const app = express();
// Keep the raw body: verification must run on the exact bytes received
app.post('/meetingize', express.raw({ type: 'application/json' }), (req, res) => {
if (!verifyMeetingizeSignature(req.body, req.get('Meetingize-Signature'), process.env.MEETINGIZE_WEBHOOK_SECRET)) {
return res.sendStatus(400);
}
const event = JSON.parse(req.body.toString('utf8'));
res.sendStatus(204);
});
```
### Shell (debugging)
```bash
BODY_FILE=event.json # raw body as received
HEADER='t=1760191402,v1=5257a8...'
SECRET='whsec_...'
T=$(echo "$HEADER" | tr ',' '\n' | sed -n 's/^t=//p')
EXPECTED=$( { printf '%s.' "$T"; cat "$BODY_FILE"; } | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')
echo "$HEADER" | tr ',' '\n' | grep -q "^v1=$EXPECTED$" && echo "valid" || echo "INVALID"
```
The [`meetingize` CLI](../../cli/meetingize/) also has `meetingize webhooks verify`.
## Delivery, retries and disabling
- Your endpoint must answer `2xx` within 10 seconds. Redirects are not followed. Do the work asynchronously and
answer fast.
- Failed deliveries are retried after 1 min, 5 min, 30 min, 2 h, 6 h, 12 h and 24 h (8 attempts in total), then
marked `failed`.
- After 15 consecutive failed attempts the endpoint is **disabled** (`disabled_reason: "too_many_failures"`) and
the owner is emailed. Re-enable it with `PATCH /v1/webhook_endpoints/{id}` `{"enabled": true}` or from the web
app.
- The delivery log (status, HTTP code, first 1 KB of your response, duration, next retry) is visible in the web
app and with `GET /v1/webhook_endpoints/{id}/deliveries`, kept 30 days.
- Ordering is not guaranteed. Use `created` and the job status, not the arrival order.
## Rotate the secret
`POST /v1/webhook_endpoints/{id}/rotate_secret` returns a new secret. Both secrets sign deliveries for 24 hours,
which gives you time to deploy the new one.
---
# Usage-based billing
API and MCP usage is billed on real consumption, separately from your web app plan quotas. Each plan includes a
monthly API allowance; beyond it you can enable on-demand billing with a spend cap you choose.
## What is metered?
| Operation | Metered on |
|---|---|
| Meeting processing (`POST /v1/meetings`) | audio duration, plus the LLM tokens of the AI meeting report (none with `transcribe_only`) |
| Document generation (`POST /v1/meetings/{id}/documents`, MCP `generate_document`) | LLM input and output tokens reported by the model provider |
| Reading data (meetings, transcripts, documents, usage) | not billed (only rate limited) |
Every metered operation writes one immutable **ledger entry** with the tokens or seconds, the model, the unit price
applied, the cost and how it was paid (wallet, on-demand, or written off above the cap). Token counts are flagged
`estimated: true` in the rare cases where the provider did not report them.
Operations started from the MCP server (by Claude, ChatGPT...) are billed exactly like API calls, on the same
ledger.
## Wallet
The **wallet** of a month is the API allowance included in your plan plus any prepaid or granted credits. The
period is the calendar month (UTC). Each operation is first debited from the wallet. The included amount and the
current unit prices are shown in **Settings > Developers** and returned by `GET /v1/usage` (`wallet`, `rates`).
API usage never consumes the web app quotas of your plan (hours of audio, documents per period), and web app usage
never consumes the API wallet.
## On-demand billing
When the wallet is empty:
- if you have an active Standard or Pro subscription **and** set a monthly **spend cap** above 0 in
**Settings > Developers**, usage continues and is billed at the end of the period through Stripe;
- otherwise new jobs are refused with `402 insufficient_balance`.
When the spend cap is reached, new jobs answer `402 spend_cap_reached`. A job already running can go slightly over
the cap: that part is recorded as `written_off` and never billed.
## Alerts
You receive an email and a `usage.threshold_reached` [webhook](../webhooks/) when the month's usage reaches 50, 80 and
100 % of the wallet, and 80 and 100 % of the spend cap.
## Check your usage
```bash
curl -s "$MEETINGIZE_API/usage" -H "Authorization: Bearer $MEETINGIZE_API_KEY"
curl -s "$MEETINGIZE_API/usage/ledger?limit=50" -H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
Scope `read:usage`. `GET /usage` returns the period, the wallet (`included_eur`, `credits_eur`, `used_eur`,
`remaining_eur`), on-demand (`enabled`, `spend_cap_eur`, `used_eur`), totals (cost, input and output tokens, audio
seconds, operations), alert thresholds and the current rates. Each job also reports its own `usage` once metered.
Amounts are EUR decimal strings.
---
# Errors
Errors use [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem details with the
`application/problem+json` content type and a stable machine-readable `code`.
```json
{
"type": "https://meetingize.com/docs/api/errors#insufficient_scope",
"title": "Insufficient scope",
"status": 403,
"detail": "This endpoint requires the scope 'read:transcripts'.",
"instance": "/api/v1/meetings/1b9e.../transcript",
"code": "insufficient_scope",
"request_id": "req_5f0c..."
}
```
Validation errors add `errors: [{"field": "...", "message": "..."}]`.
## Error codes
| HTTP | `code` | What to do |
|---|---|---|
| 400 | `invalid_request`, `validation_error`, `invalid_cursor` | fix the request (see `errors`) |
| 401 | `missing_credentials`, `invalid_api_key`, `invalid_token`, `api_key_revoked`, `api_key_expired` | check the credential |
| 402 | `insufficient_balance`, `spend_cap_reached` | add credits, enable on-demand or raise the cap ([billing](../billing/)) |
| 403 | `insufficient_scope`, `api_disabled` | use a credential with the right scope |
| 404 | `not_found` | the object does not exist **or belongs to another account** |
| 405 | `method_not_allowed` | |
| 409 | `transcript_not_ready`, `summary_not_ready` | retry later or wait for the webhook |
| 409 | `idempotency_key_in_use` | the first request with this key is still running |
| 413 | `file_too_large` | files are limited to 2 GB |
| 415 | `unsupported_media_type` | send JSON or multipart as documented |
| 422 | `idempotency_key_reused`, `invalid_audio`, `unprocessable` | |
| 429 | `rate_limited`, `parallel_processing_limit` | wait (`Retry-After`) and retry, see [limits](../limits/) |
| 500 | `internal_error` | retry with backoff, contact support with the `request_id` |
## Rate limits and quotas
Request rate limits per plan and the parallel processing quota are described on the [limits](../limits/) page.
## Retry advice
- Retry `429` and `5xx` with exponential backoff (for example 1 s, 2 s, 4 s... up to 1 minute), honoring
`Retry-After`.
- Always send an `Idempotency-Key` on `POST` so retries are safe.
- Do not retry other `4xx` without changing the request.
---
# Idempotency-Key
An **idempotency key** lets you retry a `POST` request without risking doing the work twice. If a network error,
a timeout or a crash leaves you unsure whether Meetingize received your request, send it again with the same key:
you get the original response back instead of a second meeting, a second document or a second charge.
## How do I use it?
Add an `Idempotency-Key` header to any `POST` request. Use a unique value per operation, typically a UUID v4
generated when you decide to perform the action (not when you send the request):
```bash
KEY=$(uuidgen)
curl -s -X POST "https://app.meetingize.com/api/v1/meetings/$MEETING_ID/documents" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $KEY" \
-d '{"template_id": "tpl_42"}'
# Network error? Send exactly the same request with the same $KEY.
```
Store the key with the operation in your system (for example on the CRM record that triggered it) so that a retry
after a restart reuses it.
## What happens when I retry?
| Situation | Response |
|---|---|
| First request with this key | processed normally |
| Same key, same body, first request finished | the **original response** is replayed (same status, same body), with the header `Idempotent-Replayed: true`. Nothing is executed or billed again |
| Same key, same body, first request still running | `409 idempotency_key_in_use`: wait a moment and retry |
| Same key, **different** body | `422 idempotency_key_reused`: a key identifies one operation, generate a new one for a new operation |
For multipart uploads, the comparison covers the form fields and the uploaded file (name, size and SHA-256), so the
same file with the same key is recognized as a retry.
## Good to know
- Keys are remembered for **24 hours**. After that, the same key is treated as a new operation.
- Keys are scoped to your account: two accounts cannot collide.
- `GET` requests are naturally safe to retry and ignore the header.
- Any string up to 255 characters works; UUIDs are recommended.
- The MCP server sends an idempotency key on every confirmed write action, derived from the action and its
arguments, so an AI assistant retrying a tool call never starts the same job twice.
---
# Limits and quotas
Two independent mechanisms protect the platform and your account: a **rate limit** on the number of requests, and a
**parallel processing quota** on the number of meetings and documents being processed at the same time.
## Rate limiting
Each credential (API key or OAuth connection) has a request budget over a sliding one-minute window:
| Plan | Requests per minute |
|---|---|
| Free | 30 |
| Standard | 60 |
| Pro | 120 |
Every response tells you where you stand:
```http
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 57
X-RateLimit-Reset: 1760436180
```
`X-RateLimit-Reset` is a Unix timestamp (seconds). Over the limit you get `429 rate_limited` with a `Retry-After`
header (seconds). All endpoints count, including polling `GET /jobs/{id}`: poll every 10 to 30 seconds, or use
[webhooks](../webhooks/).
## Parallel processing quota
Transcribing a meeting and generating a document are heavy operations. Your plan allows a fixed number of them **at
the same time**, counted **together for the web app and the API** (a recording processed in the web app takes a
slot too). The number depends on your plan and is shown on the **Manage Plans** page of the web app (user menu).
When all slots are busy, `POST /v1/meetings` and `POST /v1/meetings/{id}/documents` answer:
```json
{
"type": "https://meetingize.com/docs/api/errors#parallel_processing_limit",
"title": "Parallel processing limit reached",
"status": 429,
"detail": "Your plan allows 3 simultaneous processing(s) (meetings and document generations, webapp and API together); 3 are running. Retry when one finishes (see GET /jobs?status=processing).",
"code": "parallel_processing_limit"
}
```
with `Retry-After: 30`. Nothing is created and nothing is billed. To see what is running:
```bash
curl -s "https://app.meetingize.com/api/v1/jobs?status=processing" -H "Authorization: Bearer $MEETINGIZE_API_KEY"
curl -s "https://app.meetingize.com/api/v1/jobs?status=queued" -H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
Documents requested at meeting creation (`document_templates`, `persona`) are generated after the report as part of
the same request: they do not need a free slot when they start.
### Sending many files
To import a batch of recordings, keep a local queue: send a new meeting each time one finishes (on
`processing.completed` or when a job reaches `completed` / `failed`), and on `429 parallel_processing_limit` wait for
`Retry-After` before retrying with the **same** [Idempotency-Key](../idempotency/).
## Other limits
| Limit | Value |
|---|---|
| Uploaded file size | 2 GB (`413 file_too_large`) |
| Page size (`limit`) | 100 items max on lists |
| `metadata` | JSON object, 4 KB max |
| Tags per meeting | 20 |
| Participants per meeting | 50 |
| Documents requested at meeting creation | 5 |
| Custom prompt for a document | 20,000 characters |
| Feedback message | 10,000 characters |
| Idempotency keys | 255 characters, remembered 24 hours |
API usage is also bounded by your monthly wallet and the spend cap you choose: see [billing](../billing/).
## Handling 429 correctly
1. Read `Retry-After` and wait at least that long.
2. Retry with exponential backoff if it happens again (for example 30 s, 60 s, 120 s).
3. Keep the same `Idempotency-Key` for `POST` retries.
4. Do not retry in a tight loop: it consumes your rate limit and delays you further.
---
# Report a bug or request a feature
Found a bug, missing a feature, unsure how something works? Tell us. Every message reaches the Meetingize team
with the context needed to investigate (account, credential, meeting).
## From the API
`POST /v1/feedback` **sends a bug report, a feature request, a question or general feedback to the Meetingize
team.** It works with any valid API key or OAuth token, no specific scope needed.
```bash
curl -s -X POST "https://app.meetingize.com/api/v1/feedback" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY" -H "Content-Type: application/json" \
-d '{
"type": "bug",
"message": "GET /v1/meetings/{id}/transcript?format=srt returns an empty file for this meeting. Request id: req_5f0c...",
"meeting_id": "6f0c2a4e-1b7d-4c55-9a51-3e2d8f0b7c11"
}'
```
| Field | Description |
|---|---|
| `type` | `bug`, `feature_request`, `question` or `feedback` (default) |
| `message` | your message, up to 10,000 characters (required) |
| `meeting_id` | optional, a meeting of your account the message is about |
Response `201 Created`:
```json
{ "id": "fb_812", "object": "feedback", "type": "bug", "meeting_id": "6f0c2a4e-...",
"message": "GET /v1/meetings/{id}/transcript?format=srt returns ...", "status": "received" }
```
### Write a useful bug report
- the endpoint and parameters you called, and what you expected;
- the `request_id` of the error (in the error body and in the `X-Request-Id` header);
- the meeting, job or document id concerned;
- the time of the call (UTC).
Never paste API keys, webhook secrets or access tokens in a message.
## From your AI assistant
When Meetingize is connected to Claude, ChatGPT or another assistant through the [MCP server](../../mcp/overview/),
just ask it: *"Report to Meetingize that the transcript of my meeting of Monday has the speakers swapped."* The
`send_feedback` tool sends the message after showing you what will be sent.
## From the web app
The **Feedback** button on every page of the web app lets you send feedback or report a bug.
## By email
Write to [support@meetingize.com](mailto:support@meetingize.com) for anything that needs a conversation:
integration questions, commercial terms, higher limits, partnership.
---
# Account API reference
Generated from the OpenAPI specification (version `2026-10-01`). Base URL: `https://app.meetingize.com/api`. All requests need `Authorization: Bearer `. Try them in the [API explorer](../../explorer/).
## Who am I
```http
GET /v1/me
```
Useful to check a key. No scope required.
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `object` | string | yes | |
| `user` | object | yes | |
| `plan` | string | yes | |
| `credential` | object | yes | |
| `rate_limit` | object | yes | |
| `api_version` | string | yes | |
```bash
curl -s "https://app.meetingize.com/api/v1/me" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
---
# Documents API reference
Generated from the OpenAPI specification (version `2026-10-01`). Base URL: `https://app.meetingize.com/api`. All requests need `Authorization: Bearer `. Try them in the [API explorer](../../explorer/).
## List generated documents
```http
GET /v1/documents
```
Documents generated for your meetings, most recent first. Required scope: `read:documents`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `cursor` | query | string | | |
| `limit` | query | integer | | |
| `meeting_id` | query | string | | Only the documents of this meeting. |
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `data` | array of object | yes | |
| `has_more` | boolean | yes | |
| `next_cursor` | string \| null | yes | |
```bash
curl -s "https://app.meetingize.com/api/v1/documents" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
## Get a document
```http
GET /v1/documents/{document_id}
```
`format=json` (default) returns the metadata and the Markdown; `md` the Markdown file; `pdf` and `docx` a rendered file (generated on first request, then cached). Required scope: `read:documents`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `document_id` | path | string | yes | |
| `format` | query | `docx`, `json`, `md`, `pdf` | | json (default), md, pdf or docx. |
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | |
| `object` | string | yes | |
| `meeting_id` | string (uuid) | yes | |
| `title` | string | yes | |
| `template_id` | string \| null | | |
| `language` | string \| null | | |
| `model` | string \| null | | |
| `formats` | array of string | yes | |
| `markdown` | string | | Only in GET /documents/{id} |
| `created_at` | string (date-time) | yes | |
| `updated_at` | string (date-time) | yes | |
```bash
curl -s "https://app.meetingize.com/api/v1/documents/" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
## Generate a document from a meeting
```http
POST /v1/meetings/{meeting_id}/documents
```
Generates a document (minutes, client report, proposal...) from the meeting AI report and transcript, either from a template (`template_id`, see `GET /templates`) or from your own instructions (`prompt`). Requires a completed meeting with a report (`409 summary_not_ready` otherwise, including transcribe_only meetings).
**Response (202):** a `document_generation` job. Poll `GET /jobs/{id}` until `completed` (then `document_id` is set; read it with `GET /documents/{document_id}`), or wait for the `document.generated` / `document.failed` webhook. Typical duration: 30 s to 2 min.
**Parallel processing quota:** same rule as the webapp. Your plan allows N simultaneous processings (meetings being processed + documents being generated, webapp and API together; Free 1 by default). Above it the request is refused with `429 parallel_processing_limit` and `Retry-After: 30`; nothing is queued on our side, retry later.
**Billing:** consumption (audio minutes, LLM tokens) is measured after completion and debited from the monthly API allowance of your plan, then billed on demand if you enabled it (spend cap). If nothing can pay, the request is refused with `402` (`insufficient_balance` or `spend_cap_reached`).
Required scope: `write:documents`. Supports `Idempotency-Key`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `Idempotency-Key` | header | string | | Optional unique key (max 255 characters, a UUID v4 is recommended) that makes the request safe to retry. If a network error leaves you unsure whether the request was received, send it again with the **same key**: if it was already processed, you get the original response again (header `Idempotent-Replayed: true`) and nothing is created twice (no duplicate meeting, no double billing). Keys are kept **24 hours** per account. Same key with a different body: `422 idempotency_key_reused`; same key while the first request is still being processed: `409 idempotency_key_in_use`. Example: `Idempotency-Key: 6f1c2a3e-9b7d-4c1e-8f2a-0d3b4c5e6f70`. |
| `meeting_id` | path | string | yes | |
**Request body** (`application/json`)
| Field | Type | Required | Description |
|---|---|---|---|
| `template_id` | string | | |
| `prompt` | string | | |
| `title` | string | | |
| `instructions` | string | | |
| `include_transcript` | boolean | | |
| `metadata` | object \| null | | |
**Responses**
- `202`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `402`: Insufficient balance or spend cap reached
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (202)
| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | |
| `object` | string | yes | |
| `kind` | `meeting_processing`, `document_generation` | yes | |
| `status` | `queued`, `processing`, `completed`, `failed`, `canceled` | yes | queued -> processing -> completed \| failed (or canceled) |
| `stage` | `queued`, `downloading`, `transcribing`, `summarizing`, `generating`, `done` \| null | | Finer progress: meeting_processing = queued, downloading (audio_url), transcribing, summarizing, done; document_generation = queued, generating, done |
| `progress` | number \| null | | |
| `meeting_id` | string \| null | | |
| `document_id` | string \| null | | document_generation: doc_ once completed |
| `parent_job_id` | string \| null | | document_generation started by a meeting creation (document_templates) |
| `transcribe_only` | boolean \| null | | |
| `document_templates` | array \| null | | |
| `document_job_ids` | array \| null | | meeting_processing: document jobs spawned after the AI report |
| `error` | object \| null | | |
| `usage` | object \| null | | |
| `metadata` | object \| null | | |
| `created_at` | string (date-time) | yes | |
| `started_at` | string \| null | | |
| `finished_at` | string \| null | | |
```bash
curl -s -X POST "https://app.meetingize.com/api/v1/meetings//documents" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
```
---
# Feedback API reference
Generated from the OpenAPI specification (version `2026-10-01`). Base URL: `https://app.meetingize.com/api`. All requests need `Authorization: Bearer `. Try them in the [API explorer](../../explorer/).
## Send feedback, a bug report or a question
```http
POST /v1/feedback
```
Sends a message to the Meetingize team (same inbox as the in-app feedback button): `bug`, `feature_request`, `question` or `feedback`. Optionally linked to one of your meetings. Any valid credential, no scope required. Not a support ticketing system: no reply through the API.
**Request body** (`application/json`)
| Field | Type | Required | Description |
|---|---|---|---|
| `type` | `bug`, `feature_request`, `question`, `feedback` | | |
| `message` | string | yes | |
| `meeting_id` | string (uuid) | | Related meeting, if any (must be yours) |
**Responses**
- `201`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (201)
| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | |
| `object` | string | yes | |
| `type` | string | yes | |
| `meeting_id` | string \| null | yes | |
| `message` | string | yes | |
| `status` | string | yes | |
```bash
curl -s -X POST "https://app.meetingize.com/api/v1/feedback" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
```
---
# Jobs API reference
Generated from the OpenAPI specification (version `2026-10-01`). Base URL: `https://app.meetingize.com/api`. All requests need `Authorization: Bearer `. Try them in the [API explorer](../../explorer/).
## List processing jobs
```http
GET /v1/jobs
```
**What jobs are:** every long operation started through the API (processing of a meeting you created, generation of a document) is tracked by a job. A job tells you where the processing is (`status`, `stage`, `progress`), what it produced (`meeting_id`, `document_id`), what went wrong (`error`) and what it cost (`usage`, once metered).
**Lifecycle:** `queued` -> `processing` -> `completed` | `failed` (`canceled` if the meeting was deleted). A `meeting_processing` job goes through stages `downloading` (audio_url only), `transcribing`, `summarizing` (skipped with transcribe_only), `done`; it is `completed` when the AI report is ready. Documents requested at creation get their own `document_generation` jobs, listed in `document_job_ids` (their `parent_job_id` points back).
Use this list to find what is still running (`?status=processing`) or to reconcile after a missed webhook. Jobs of meetings processed in the webapp are not listed. Any valid credential.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `cursor` | query | string | | |
| `kind` | query | `document_generation`, `meeting_processing` | | meeting_processing or document_generation. |
| `limit` | query | integer | | |
| `status` | query | `canceled`, `completed`, `failed`, `processing`, `queued` | | queued, processing, completed, failed or canceled. |
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `data` | array of object | yes | |
| `has_more` | boolean | yes | |
| `next_cursor` | string \| null | yes | |
```bash
curl -s "https://app.meetingize.com/api/v1/jobs" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
## Get a job (poll this)
```http
GET /v1/jobs/{job_id}
```
Current state of a job. Poll every 10 to 30 seconds until `status` is `completed` or `failed` (or use webhooks instead of polling). On `completed`: for a meeting, read `GET /meetings/{meeting_id}` (and its transcript/summary); for a document, `GET /documents/{document_id}`. Any valid credential.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `job_id` | path | string | yes | |
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | |
| `object` | string | yes | |
| `kind` | `meeting_processing`, `document_generation` | yes | |
| `status` | `queued`, `processing`, `completed`, `failed`, `canceled` | yes | queued -> processing -> completed \| failed (or canceled) |
| `stage` | `queued`, `downloading`, `transcribing`, `summarizing`, `generating`, `done` \| null | | Finer progress: meeting_processing = queued, downloading (audio_url), transcribing, summarizing, done; document_generation = queued, generating, done |
| `progress` | number \| null | | |
| `meeting_id` | string \| null | | |
| `document_id` | string \| null | | document_generation: doc_ once completed |
| `parent_job_id` | string \| null | | document_generation started by a meeting creation (document_templates) |
| `transcribe_only` | boolean \| null | | |
| `document_templates` | array \| null | | |
| `document_job_ids` | array \| null | | meeting_processing: document jobs spawned after the AI report |
| `error` | object \| null | | |
| `usage` | object \| null | | |
| `metadata` | object \| null | | |
| `created_at` | string (date-time) | yes | |
| `started_at` | string \| null | | |
| `finished_at` | string \| null | | |
```bash
curl -s "https://app.meetingize.com/api/v1/jobs/" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
---
# Meeting management API reference
Generated from the OpenAPI specification (version `2026-10-01`). Base URL: `https://app.meetingize.com/api`. All requests need `Authorization: Bearer `. Try them in the [API explorer](../../explorer/).
## Create a meeting from an audio file or URL
```http
POST /v1/meetings
```
Creates a meeting and starts its processing: transcription with speaker diarization, then the AI meeting report (summary, action items...), then the documents you asked for. This is the only way to send audio to Meetingize.
**Input:** either `multipart/form-data` with `file` (max 2 GB), or `application/json` with `audio_url` (public https URL, downloaded by Meetingize). In multipart, `tags`, `participants`, `document_templates` and `metadata` are JSON strings (tags and templates also accept comma-separated lists).
**Options:** `title`, `recorded_at` (meeting date), `language` (default auto-detect), `recording_type` (`meeting` = several speakers, `solo` = dictation), `participants` (names/emails, optionally with the speaker labels they correspond to; never emailed), `tags`, `document_templates` (up to 5 `tpl_` ids, generated automatically after the report) or `persona` (its templates, max 5), `metadata` (your JSON, echoed in jobs and webhooks).
**Client:** attach the meeting to a client folder (same as the webapp client column) with `client_id` (`cli_...`, see `GET /clients`) or `client: {name, email, phone}` (an existing client with this name is reused, otherwise it is created; email/phone optional). Changeable with PATCH, filterable with `GET /meetings?client=`.
**`transcribe_only: true`:** only the transcript is produced (no AI report, no documents, cheaper). The meeting is `completed` as soon as the transcript is ready.
**Response (202):** the created meeting (`status: queued`) with its `job`. Then either poll `GET /jobs/{job.id}` (or `GET /meetings/{id}`) every 10 to 30 seconds, or wait for the webhooks: `transcription.completed` (transcript readable), then `processing.completed` (report ready, or end of a transcribe_only processing), and `document.generated` for each requested document. On error: `transcription.failed` or `processing.failed`. Typical duration: 2 to 4 minutes per hour of audio.
**Parallel processing quota:** same rule as the webapp. Your plan allows N simultaneous processings (meetings being processed + documents being generated, webapp and API together; Free 1 by default). Above it the request is refused with `429 parallel_processing_limit` and `Retry-After: 30`; nothing is queued on our side, retry later.
**Billing:** consumption (audio minutes, LLM tokens) is measured after completion and debited from the monthly API allowance of your plan, then billed on demand if you enabled it (spend cap). If nothing can pay, the request is refused with `402` (`insufficient_balance` or `spend_cap_reached`).
Required scope: `write:meetings`. Supports `Idempotency-Key` (strongly recommended: a retried upload must not create two meetings).
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `Idempotency-Key` | header | string | | Optional unique key (max 255 characters, a UUID v4 is recommended) that makes the request safe to retry. If a network error leaves you unsure whether the request was received, send it again with the **same key**: if it was already processed, you get the original response again (header `Idempotent-Replayed: true`) and nothing is created twice (no duplicate meeting, no double billing). Keys are kept **24 hours** per account. Same key with a different body: `422 idempotency_key_reused`; same key while the first request is still being processed: `409 idempotency_key_in_use`. Example: `Idempotency-Key: 6f1c2a3e-9b7d-4c1e-8f2a-0d3b4c5e6f70`. |
**Request body** (`multipart/form-data`)
| Field | Type | Required | Description |
|---|---|---|---|
| `title` | string | | Defaults to the AI-generated title, else file name |
| `recorded_at` | string (date-time) | | Meeting date (ISO 8601). Default: now |
| `language` | string | | ISO 639-1 (fr, en, ...) or auto (default) |
| `recording_type` | `meeting`, `solo` | | meeting (several speakers, diarization, default) or solo (dictation) |
| `transcribe_only` | boolean | | true: transcript only (no AI report, no documents, cheaper) |
| `persona` | string | | Persona id (GET /personas): without document_templates, generates the persona templates (max 5) after the report |
| `metadata` | object | | Your own JSON object (max 4 KB), echoed in jobs and webhooks |
| `client_id` | string | | Existing client (cli_, see GET /clients) |
| `file` | string (binary) | yes | Audio or video file (max 2 GB): mp3, m4a, wav, flac, ogg, webm, mp4... |
| `tags` | string | | JSON array or comma-separated list |
| `participants` | string | | JSON array of {name, email, speaker_labels} |
| `document_templates` | string | | JSON array or comma-separated tpl_ ids |
| `client` | string | | JSON object {name, email, phone} or just a client name |
**Request body** (`application/json`)
| Field | Type | Required | Description |
|---|---|---|---|
| `title` | string | | Defaults to the AI-generated title, else file name |
| `recorded_at` | string (date-time) | | Meeting date (ISO 8601). Default: now |
| `language` | string | | ISO 639-1 (fr, en, ...) or auto (default) |
| `recording_type` | `meeting`, `solo` | | meeting (several speakers, diarization, default) or solo (dictation) |
| `transcribe_only` | boolean | | true: transcript only (no AI report, no documents, cheaper) |
| `persona` | string | | Persona id (GET /personas): without document_templates, generates the persona templates (max 5) after the report |
| `metadata` | object | | Your own JSON object (max 4 KB), echoed in jobs and webhooks |
| `client_id` | string | | Existing client (cli_, see GET /clients) |
| `audio_url` | string (uri) | yes | Public https URL of the audio/video file, downloaded by Meetingize |
| `tags` | array of string | | |
| `participants` | array of object | | |
| `document_templates` | array of string | | Templates to generate after the AI report (max 5) |
| `client` | object | | Client found by name or created (alternative to client_id) |
**Responses**
- `202`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `402`: Insufficient balance or spend cap reached
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (202)
| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string (uuid) | yes | |
| `object` | string | yes | |
| `title` | string | yes | |
| `status` | `draft`, `queued`, `processing`, `completed`, `failed` | yes | draft (no audio yet, webapp only), queued, processing (transcription then AI report), completed (transcript + report ready; transcript only when transcribe_only), failed |
| `transcribe_only` | boolean | yes | Created with transcribe_only=true: no AI report, no documents |
| `recorded_at` | string (date-time) | yes | Date of the meeting (recorded_at at creation, else upload date) |
| `created_at` | string (date-time) | yes | |
| `updated_at` | string (date-time) | yes | |
| `duration_seconds` | integer \| null | | |
| `language` | string \| null | | |
| `source` | `upload`, `live_recording`, `text`, `url`, `api` \| null | | |
| `recording_type` | `meeting`, `solo` \| null | | |
| `tags` | array of string | yes | |
| `client` | object \| null | | Client folder the meeting belongs to |
| `has_transcript` | boolean | yes | |
| `has_summary` | boolean | yes | |
| `web_url` | string (uri) | yes | |
| `summary` | object \| null | | |
| `participants` | array of object | yes | |
| `documents` | array of object | yes | |
| `job` | object \| null | | Latest processing job of the meeting when created via the API (null for webapp meetings): what to poll with GET /jobs/{job_id} |
```bash
curl -s -X POST "https://app.meetingize.com/api/v1/meetings" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
```
## Update a meeting (title, date, tags, participants)
```http
PATCH /v1/meetings/{meeting_id}
```
Partial update; only the fields sent are changed.
- `title`: replaces the title (it then stays fixed even if the AI report is regenerated).
- `recorded_at`: meeting date.
- `tags`: **replaces** the whole tag list (`[]` removes all).
- `client_id` / `client`: attach to an existing client or find/create one by name; `client: null` detaches the meeting from its client.
- `participants`: `add` (name/email/speaker_labels), `update` (by `par_` id, only fields present; `speaker_labels` replaces the participant's labels), `remove` (by `par_` id). A speaker label belongs to one participant at most: assigning it moves it. Labels must exist in the transcript (`SPEAKER_00`...). Speaker names then appear in the transcript (`speaker_name`) and exports. Removing a person the meeting is shared with (webapp sharing) only removes them from the participants, their access is kept.
No email is ever sent by this endpoint. The AI report is **not** regenerated. Required scope: `write:meetings`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `meeting_id` | path | string | yes | |
**Request body** (`application/json`)
| Field | Type | Required | Description |
|---|---|---|---|
| `title` | string | | |
| `recorded_at` | string (date-time) | | |
| `tags` | array of string | | Replaces all tags |
| `participants` | object | | |
| `client_id` | string \| null | | Attach to an existing client |
| `client` | object \| null | | Find/create by name; null detaches |
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string (uuid) | yes | |
| `object` | string | yes | |
| `title` | string | yes | |
| `status` | `draft`, `queued`, `processing`, `completed`, `failed` | yes | draft (no audio yet, webapp only), queued, processing (transcription then AI report), completed (transcript + report ready; transcript only when transcribe_only), failed |
| `transcribe_only` | boolean | yes | Created with transcribe_only=true: no AI report, no documents |
| `recorded_at` | string (date-time) | yes | Date of the meeting (recorded_at at creation, else upload date) |
| `created_at` | string (date-time) | yes | |
| `updated_at` | string (date-time) | yes | |
| `duration_seconds` | integer \| null | | |
| `language` | string \| null | | |
| `source` | `upload`, `live_recording`, `text`, `url`, `api` \| null | | |
| `recording_type` | `meeting`, `solo` \| null | | |
| `tags` | array of string | yes | |
| `client` | object \| null | | Client folder the meeting belongs to |
| `has_transcript` | boolean | yes | |
| `has_summary` | boolean | yes | |
| `web_url` | string (uri) | yes | |
| `summary` | object \| null | | |
| `participants` | array of object | yes | |
| `documents` | array of object | yes | |
| `job` | object \| null | | Latest processing job of the meeting when created via the API (null for webapp meetings): what to poll with GET /jobs/{job_id} |
```bash
curl -s -X PATCH "https://app.meetingize.com/api/v1/meetings/" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
```
## Delete a meeting
```http
DELETE /v1/meetings/{meeting_id}
```
Deletes the meeting and everything attached (audio, transcript, report, documents), exactly like the webapp. A processing in progress is cancelled. Irreversible. Usage already consumed stays in the ledger and the deleted documents keep counting in the webapp quotas. Required scope: `write:meetings`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `meeting_id` | path | string | yes | |
**Responses**
- `204`: No response body
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
```bash
curl -s -X DELETE "https://app.meetingize.com/api/v1/meetings/" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
```
---
# Meetings API reference
Generated from the OpenAPI specification (version `2026-10-01`). Base URL: `https://app.meetingize.com/api`. All requests need `Authorization: Bearer `. Try them in the [API explorer](../../explorer/).
## List clients
```http
GET /v1/clients
```
Client folders of the account (the webapp "client" of a meeting), with optional contact email and phone. Use an `id` as `client_id` when creating or updating a meeting, or `?client=` to filter meetings. Archived clients are hidden unless `include_archived=true`. Required scope: `read:meetings`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `include_archived` | query | boolean | | Include archived clients. |
| `q` | query | string | | Part of the client name (case-insensitive). |
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `data` | array of object | yes | |
```bash
curl -s "https://app.meetingize.com/api/v1/clients" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
## List and search meetings
```http
GET /v1/meetings
```
Lists the meetings of the account, most recent first, with cursor pagination (`limit`, `cursor`).
`q` is a case-insensitive full-text search on the title, the AI report and the transcript. Filters can be combined (AND). `status` is computed, so filtering on it only scans the 500 most recent meetings.
Only meetings owned by the account are returned (not those shared with it). Required scope: `read:meetings`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `client` | query | string | | Client: id (`cli_12`) or exact name. |
| `created_after` | query | string (date-time) | | Meetings created (uploaded) at or after this date (ISO 8601). |
| `created_before` | query | string (date-time) | | Meetings created (uploaded) before this date (ISO 8601). |
| `cursor` | query | string | | Opaque cursor: the `next_cursor` of the previous page. |
| `limit` | query | integer | | Page size, 1 to 100 (default 20). |
| `q` | query | string | | Text to search in title, AI report and transcript (case-insensitive). Example: `budget T4`. |
| `recorded_after` | query | string (date-time) | | Meetings that took place at or after this date (ISO 8601). |
| `recorded_before` | query | string (date-time) | | Meetings that took place before this date (ISO 8601). |
| `status` | query | `completed`, `draft`, `failed`, `processing`, `queued` | | queued, processing, completed, failed or draft. |
| `tag` | query | array of string | | Tag name; repeat the parameter to require several tags (`?tag=client-x&tag=urgent`). |
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `data` | array of object | yes | |
| `has_more` | boolean | yes | |
| `next_cursor` | string \| null | yes | |
```bash
curl -s "https://app.meetingize.com/api/v1/meetings" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
## Get a meeting
```http
GET /v1/meetings/{meeting_id}
```
Full view of a meeting: metadata, processing `status`, AI report (`summary`, null until ready), participants (with their `par_` id and speaker labels), generated documents and the latest processing `job` (meetings created via the API). This is a good endpoint to poll after creating a meeting. Required scope: `read:meetings`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `meeting_id` | path | string | yes | |
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string (uuid) | yes | |
| `object` | string | yes | |
| `title` | string | yes | |
| `status` | `draft`, `queued`, `processing`, `completed`, `failed` | yes | draft (no audio yet, webapp only), queued, processing (transcription then AI report), completed (transcript + report ready; transcript only when transcribe_only), failed |
| `transcribe_only` | boolean | yes | Created with transcribe_only=true: no AI report, no documents |
| `recorded_at` | string (date-time) | yes | Date of the meeting (recorded_at at creation, else upload date) |
| `created_at` | string (date-time) | yes | |
| `updated_at` | string (date-time) | yes | |
| `duration_seconds` | integer \| null | | |
| `language` | string \| null | | |
| `source` | `upload`, `live_recording`, `text`, `url`, `api` \| null | | |
| `recording_type` | `meeting`, `solo` \| null | | |
| `tags` | array of string | yes | |
| `client` | object \| null | | Client folder the meeting belongs to |
| `has_transcript` | boolean | yes | |
| `has_summary` | boolean | yes | |
| `web_url` | string (uri) | yes | |
| `summary` | object \| null | | |
| `participants` | array of object | yes | |
| `documents` | array of object | yes | |
| `job` | object \| null | | Latest processing job of the meeting when created via the API (null for webapp meetings): what to poll with GET /jobs/{job_id} |
```bash
curl -s "https://app.meetingize.com/api/v1/meetings/" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
## Export for an LLM (ChatGPT, Claude...)
```http
GET /v1/meetings/{meeting_id}/export
```
Ready-to-paste bundle: a priming prompt, metadata, participants, the AI report and the timestamped diarized transcript, split in parts of at most `max_tokens` tokens so that long meetings fit in a chat context. `format=md` returns all parts separated by `--- Part i/n ---`. Scopes required: `read:meetings` and `read:transcripts`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `format` | query | `json`, `md` | | json (default) or md. |
| `lang` | query | `en`, `fr` | | Language of the priming prompt: fr (default) or en. |
| `max_tokens` | query | integer | | Max tokens per part (2000 to 200000, default 24000). |
| `meeting_id` | path | string | yes | |
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `object` | string | yes | |
| `meeting_id` | string (uuid) | yes | |
| `priming_prompt` | string | yes | |
| `total_tokens` | integer | yes | |
| `parts` | array of object | yes | |
```bash
curl -s "https://app.meetingize.com/api/v1/meetings//export" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
## List participants
```http
GET /v1/meetings/{meeting_id}/participants
```
Participants of the meeting with their `par_` id and the speaker labels attributed to them, plus the transcript speakers not attributed to anyone (`id: null`). Modify them with `PATCH /meetings/{id}`. Required scope: `read:meetings`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `meeting_id` | path | string | yes | |
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `data` | array of object | yes | |
```bash
curl -s "https://app.meetingize.com/api/v1/meetings//participants" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
## Get the AI meeting report
```http
GET /v1/meetings/{meeting_id}/summary
```
The AI report (summary, key takeaways, action items, minutes) as Markdown and, in `structured`, the raw structured result. Available from `processing.completed`; before that, or for a transcribe_only meeting: `409 summary_not_ready`. Required scope: `read:meetings`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `format` | query | `json`, `md` | | json (default) or md. |
| `meeting_id` | path | string | yes | |
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `object` | string | yes | |
| `meeting_id` | string (uuid) | yes | |
| `title` | string | yes | |
| `language` | string \| null | | |
| `markdown` | string | yes | |
| `structured` | object \| null | | |
| `model` | string \| null | | |
| `created_at` | string (date-time) | yes | |
| `updated_at` | string (date-time) | yes | |
```bash
curl -s "https://app.meetingize.com/api/v1/meetings//summary" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
---
# Templates API reference
Generated from the OpenAPI specification (version `2026-10-01`). Base URL: `https://app.meetingize.com/api`. All requests need `Authorization: Bearer `. Try them in the [API explorer](../../explorer/).
## List personas
```http
GET /v1/personas
```
Professional personas (template families). Passing `persona` at meeting creation generates its templates. Scope: `read:meetings` or `write:documents`.
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `data` | array of object | yes | |
```bash
curl -s "https://app.meetingize.com/api/v1/personas" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
## List document templates
```http
GET /v1/templates
```
Document templates you can generate with `POST /meetings/{id}/documents` or request at meeting creation (`document_templates`). Filter by `persona`. Scope: `read:meetings` or `write:documents`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `persona` | query | string | | Persona id from GET /personas (e.g. `lawyer`). |
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `data` | array of object | yes | |
```bash
curl -s "https://app.meetingize.com/api/v1/templates" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
## Get a template
```http
GET /v1/templates/{template_id}
```
Template detail. Scope: `read:meetings` or `write:documents`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `template_id` | path | string | yes | |
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | |
| `object` | string | yes | |
| `name` | string | yes | |
| `description` | string \| null | | |
| `icon` | string \| null | | |
| `persona` | string \| null | | |
```bash
curl -s "https://app.meetingize.com/api/v1/templates/" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
---
# Transcripts API reference
Generated from the OpenAPI specification (version `2026-10-01`). Base URL: `https://app.meetingize.com/api`. All requests need `Authorization: Bearer `. Try them in the [API explorer](../../explorer/).
## Get the transcript
```http
GET /v1/meetings/{meeting_id}/transcript
```
Diarized, timestamped transcript. `format=json` (default) returns segments (`start`/`end` in seconds, `speaker` label, `speaker_name` once attributed with PATCH). `md`, `txt`, `srt` and `vtt` return text files (subtitles: `srt`/`vtt`). Available from `transcription.completed`; before that: `409 transcript_not_ready`. Required scope: `read:transcripts`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `format` | query | `json`, `md`, `srt`, `txt`, `vtt` | | json (default), md, txt, srt or vtt. |
| `meeting_id` | path | string | yes | |
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `object` | string | yes | |
| `meeting_id` | string (uuid) | yes | |
| `language` | string \| null | | |
| `duration_seconds` | integer \| null | | |
| `model` | string \| null | | |
| `speakers` | array of string | yes | |
| `segments` | array of object | yes | |
```bash
curl -s "https://app.meetingize.com/api/v1/meetings//transcript" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
---
# Usage API reference
Generated from the OpenAPI specification (version `2026-10-01`). Base URL: `https://app.meetingize.com/api`. All requests need `Authorization: Bearer `. Try them in the [API explorer](../../explorer/).
## Current consumption
```http
GET /v1/usage
```
Consumption of the current period (UTC calendar month): wallet (allowance included in your plan + credits), on-demand part and spend cap, totals (tokens, audio seconds) and the unit prices applied. Required scope: `read:usage`.
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `object` | string | yes | |
| `period` | string | yes | |
| `period_start` | string (date-time) | yes | |
| `period_end` | string (date-time) | yes | |
| `currency` | string | yes | |
| `wallet` | object | yes | |
| `on_demand` | object | yes | |
| `totals` | object | yes | |
| `alert_thresholds_percent` | array of integer | yes | |
| `rates` | object | yes | |
```bash
curl -s "https://app.meetingize.com/api/v1/usage" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
## Consumption ledger
```http
GET /v1/usage/ledger
```
One immutable line per metered operation (transcription, AI report, document), with tokens, model, cost and how it was paid (wallet / on-demand / written off above the cap). Required scope: `read:usage`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `cursor` | query | string | | |
| `limit` | query | integer | | |
| `period` | query | string | | Billing period YYYY-MM (UTC). Default: current month. |
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `data` | array of object | yes | |
| `has_more` | boolean | yes | |
| `next_cursor` | string \| null | yes | |
```bash
curl -s "https://app.meetingize.com/api/v1/usage/ledger" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
---
# Webhooks API reference
Generated from the OpenAPI specification (version `2026-10-01`). Base URL: `https://app.meetingize.com/api`. All requests need `Authorization: Bearer `. Try them in the [API explorer](../../explorer/).
## List webhook endpoints
```http
GET /v1/webhook_endpoints
```
Your webhook endpoints (secret not included). Required scope: `webhooks`.
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `data` | array of object | yes | |
```bash
curl -s "https://app.meetingize.com/api/v1/webhook_endpoints" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
## Register a webhook endpoint
```http
POST /v1/webhook_endpoints
```
Registers an https URL that will receive the selected events (see the `webhooks` section of this spec for payloads). The signing `secret` is returned **only in this response**: store it to verify the `Meetingize-Signature` header. Required scope: `webhooks`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `Idempotency-Key` | header | string | | Optional unique key (max 255 characters, a UUID v4 is recommended) that makes the request safe to retry. If a network error leaves you unsure whether the request was received, send it again with the **same key**: if it was already processed, you get the original response again (header `Idempotent-Replayed: true`) and nothing is created twice (no duplicate meeting, no double billing). Keys are kept **24 hours** per account. Same key with a different body: `422 idempotency_key_reused`; same key while the first request is still being processed: `409 idempotency_key_in_use`. Example: `Idempotency-Key: 6f1c2a3e-9b7d-4c1e-8f2a-0d3b4c5e6f70`. |
**Request body** (`application/json`)
| Field | Type | Required | Description |
|---|---|---|---|
| `url` | string (uri) | yes | |
| `events` | array of string | yes | |
| `description` | string \| null | | |
**Responses**
- `201`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (201)
| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | |
| `object` | string | yes | |
| `url` | string (uri) | yes | |
| `events` | array of string | yes | |
| `description` | string \| null | | |
| `enabled` | boolean | yes | |
| `disabled_reason` | string \| null | | |
| `consecutive_failures` | integer | yes | |
| `secret_hint` | string | yes | |
| `created_at` | string (date-time) | yes | |
| `secret` | string | yes | |
```bash
curl -s -X POST "https://app.meetingize.com/api/v1/webhook_endpoints" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
```
## Get a webhook endpoint
```http
GET /v1/webhook_endpoints/{endpoint_id}
```
Required scope: `webhooks`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `endpoint_id` | path | string | yes | |
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | |
| `object` | string | yes | |
| `url` | string (uri) | yes | |
| `events` | array of string | yes | |
| `description` | string \| null | | |
| `enabled` | boolean | yes | |
| `disabled_reason` | string \| null | | |
| `consecutive_failures` | integer | yes | |
| `secret_hint` | string | yes | |
| `created_at` | string (date-time) | yes | |
```bash
curl -s "https://app.meetingize.com/api/v1/webhook_endpoints/" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
## Update a webhook endpoint
```http
PATCH /v1/webhook_endpoints/{endpoint_id}
```
Change url, events or description; `enabled: true` re-enables an endpoint disabled after repeated failures. Required scope: `webhooks`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `endpoint_id` | path | string | yes | |
**Request body** (`application/json`)
| Field | Type | Required | Description |
|---|---|---|---|
| `url` | string (uri) | | |
| `events` | array of string | | |
| `description` | string \| null | | |
| `enabled` | boolean | | |
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | |
| `object` | string | yes | |
| `url` | string (uri) | yes | |
| `events` | array of string | yes | |
| `description` | string \| null | | |
| `enabled` | boolean | yes | |
| `disabled_reason` | string \| null | | |
| `consecutive_failures` | integer | yes | |
| `secret_hint` | string | yes | |
| `created_at` | string (date-time) | yes | |
```bash
curl -s -X PATCH "https://app.meetingize.com/api/v1/webhook_endpoints/" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
```
## Delete a webhook endpoint
```http
DELETE /v1/webhook_endpoints/{endpoint_id}
```
Required scope: `webhooks`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `endpoint_id` | path | string | yes | |
**Responses**
- `204`: No response body
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
```bash
curl -s -X DELETE "https://app.meetingize.com/api/v1/webhook_endpoints/" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
```
## Delivery log
```http
GET /v1/webhook_endpoints/{endpoint_id}/deliveries
```
Deliveries of the endpoint (status, HTTP code, response excerpt, attempts, next retry), kept 30 days. Required scope: `webhooks`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `cursor` | query | string | | |
| `endpoint_id` | path | string | yes | |
| `limit` | query | integer | | |
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `data` | array of object | yes | |
| `has_more` | boolean | yes | |
| `next_cursor` | string \| null | yes | |
```bash
curl -s "https://app.meetingize.com/api/v1/webhook_endpoints//deliveries" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY"
```
## Rotate the signing secret
```http
POST /v1/webhook_endpoints/{endpoint_id}/rotate_secret
```
New secret returned once; the previous one stays valid 24 h (both signatures are sent). Required scope: `webhooks`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `endpoint_id` | path | string | yes | |
**Responses**
- `200`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | |
| `object` | string | yes | |
| `url` | string (uri) | yes | |
| `events` | array of string | yes | |
| `description` | string \| null | | |
| `enabled` | boolean | yes | |
| `disabled_reason` | string \| null | | |
| `consecutive_failures` | integer | yes | |
| `secret_hint` | string | yes | |
| `created_at` | string (date-time) | yes | |
| `secret` | string | yes | |
```bash
curl -s -X POST "https://app.meetingize.com/api/v1/webhook_endpoints//rotate_secret" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
```
## Send a test event
```http
POST /v1/webhook_endpoints/{endpoint_id}/test
```
Sends a signed `ping` event to the endpoint now. Required scope: `webhooks`.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `endpoint_id` | path | string | yes | |
**Responses**
- `202`:
- `400`: Validation error
- `401`: Missing or invalid credential
- `403`: Insufficient scope
- `404`: Not found (also for resources of other accounts)
- `429`: Rate limit (`rate_limited`) or parallel processing quota (`parallel_processing_limit`) exceeded; see `Retry-After`
Response fields (202)
| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | |
| `object` | string | yes | |
| `endpoint_id` | string | yes | |
| `event_id` | string | yes | |
| `event_type` | string | yes | |
| `status` | `pending`, `succeeded`, `retrying`, `failed` | yes | |
| `attempts` | integer | yes | |
| `response_status` | integer \| null | | |
| `response_body_excerpt` | string \| null | | |
| `duration_ms` | integer \| null | | |
| `error` | string \| null | | |
| `next_retry_at` | string \| null | | |
| `last_attempt_at` | string \| null | | |
| `created_at` | string (date-time) | yes | |
```bash
curl -s -X POST "https://app.meetingize.com/api/v1/webhook_endpoints//test" \
-H "Authorization: Bearer $MEETINGIZE_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
```
---
# MCP server overview
The Meetingize **MCP server** connects your meetings to the AI assistant you already use. Once connected, Claude,
ChatGPT, Cursor or any client supporting the [Model Context Protocol](https://modelcontextprotocol.io) can search
your meetings, read diarized transcripts and AI reports, and generate documents on demand, only with the
permissions you granted and only on your own data.
| | |
|---|---|
| Server URL | `https://app.meetingize.com/api/mcp` |
| Transport | Streamable HTTP (stateless, JSON responses) |
| Authentication | OAuth 2.1 with PKCE and dynamic client registration (automatic in most clients), or a Meetingize API key as bearer token |
| Protocol versions | `2025-11-25`, `2025-06-18`, `2025-03-26` |
## What can I ask?
Examples once Meetingize is connected:
- "Which commitments did I make with client Dupont this quarter? Give me the dates and the meetings."
- "Prepare my meeting with Mrs Martin tomorrow: history, open questions, what we promised."
- "Find every meeting where we talked about the price increase and summarize the objections."
- "Read the transcript of yesterday's team meeting and list the action items per person."
- "Generate a follow-up email for my last meeting with the Lefebvre family." (the assistant shows a preview and
asks you to confirm before anything billable happens)
- "How do Meetingize webhooks work?" (the server also carries the whole Meetingize documentation)
## How do I connect?
- [Claude (claude.ai, Claude Desktop, Claude Code)](../claude/)
- [ChatGPT](../chatgpt/)
- [Cursor, VS Code, MCP Inspector and other clients](../other-clients/)
In every case you paste the server URL, sign in to Meetingize in the window that opens and approve the permissions
on the Meetingize consent screen. You can disconnect at any time from **Settings > Connected applications**.
## What does the server expose?
- **Tools** to search and read meetings, transcripts (split into parts that fit the context window), reports,
participants and documents; to search inside all transcripts; to create meetings from audio URLs, update
them and generate documents (with confirmation); to follow jobs, check usage and report a bug; plus `search` and `fetch` for ChatGPT. See
[Tools, resources and prompts](../tools/).
- **Resources**: your recent meetings, transcripts, documents and the documentation as `meetingize://` URIs.
- **Prompts**: ready-made workflows such as "prepare my next meeting with..." or "extract my commitments".
## Security in one paragraph
The server never sees your Meetingize password: the assistant gets an OAuth access token valid one hour, bound to
your account and limited to the scopes you approved. Every call goes through the same authorization layer as the
public API, so the assistant can only reach your own meetings (other accounts' data simply does not exist for it).
Actions that consume your API allowance require explicit confirmation. Details in [Security](../security/).
## Billing
Reading meetings, transcripts and documents through the MCP server is free (rate limited). Generating a document or
creating a meeting from an audio URL is billed on your [API usage](../../api/billing/), exactly like the REST API.
---
# Connect Claude
Meetingize works with Claude as a **custom connector** (remote MCP server). Menu names below may vary slightly
with Claude updates; the availability of custom connectors depends on your Claude plan, see Anthropic's help center.
## Claude on the web, desktop and mobile
1. In Claude, open **Settings > Connectors** and choose **Add custom connector**.
2. Name: `Meetingize`. URL: `https://app.meetingize.com/api/mcp`.
3. Click **Add**, then **Connect**. A Meetingize window opens: sign in if needed, review the permissions and
click **Allow access**.
4. Back in Claude, enable the Meetingize connector in a conversation (tools menu) and ask, for example:
*"Which commitments did I make with client Dupont this quarter?"*
Connectors added on claude.ai are also available in Claude Desktop and the Claude mobile apps signed in to the same
account. Claude asks for your approval before using a tool that is not read-only.
If your organization manages connectors centrally (Team or Enterprise), an owner adds Meetingize once and each
member then connects their own Meetingize account.
## Claude Code
```bash
# OAuth (recommended): Claude Code opens the browser for the Meetingize consent screen
claude mcp add --transport http meetingize https://app.meetingize.com/api/mcp
# then, inside Claude Code
/mcp
```
Select `meetingize` and **Authenticate**. Add `--scope project` to share the server definition with your team in
`.mcp.json` (each person still authenticates with their own account), or `--scope user` to have it in every
project.
With an API key instead of OAuth (scripts, CI):
```bash
claude mcp add --transport http meetingize https://app.meetingize.com/api/mcp \
--header "Authorization: Bearer $MEETINGIZE_API_KEY"
```
Give that key only the scopes you need (for read-only use: `read:meetings`, `read:transcripts`, `read:documents`).
## Try it
- *"List my meetings of last week with Meetingize."*
- *"Search my transcripts for 'résiliation' and quote the exact sentences with timestamps."*
- Use the server prompts from the `/` menu: `prepare_meeting`, `extract_commitments`, `followup_email`,
`weekly_digest`.
## Troubleshooting
| Symptom | Fix |
|---|---|
| The connection window shows "Authorization error" | the connector URL must be exactly the server URL above, without extra path |
| Claude says a permission is missing | disconnect and reconnect, and keep the permission checked on the consent screen |
| "Not found" on a meeting you can see in the app | the meeting belongs to another Meetingize account than the one you connected |
| Tools stopped working after a while | reconnect: your refresh token expires after 30 days of inactivity, or the connection was revoked in Settings > Connected applications |
---
# Connect ChatGPT
ChatGPT can use remote MCP servers as custom connectors (also called apps). The exact menus depend on your ChatGPT
plan and workspace settings and change regularly; the steps below describe the usual path.
## Add Meetingize
1. In ChatGPT, open **Settings > Apps & Connectors** (or **Connectors**), then **Advanced settings** and enable
**Developer mode** if your plan requires it for custom connectors.
2. Choose **Create** (or **Add custom connector**).
3. Name: `Meetingize`. MCP server URL: `https://app.meetingize.com/api/mcp`. Authentication: **OAuth**.
4. Confirm. A Meetingize window opens: sign in, review the permissions, click **Allow access**.
5. In a conversation, enable the Meetingize connector from the tools menu and ask your question.
ChatGPT registers itself automatically with the Meetingize authorization server (dynamic client registration); you
do not need a client id or secret.
## Search and deep research
The server implements the two read-only tools ChatGPT expects for search and deep research: `search` (returns
meetings and documents matching a query) and `fetch` (returns the full content of one result). The other tools
(transcript parts, transcript search, document generation...) are available in conversations where custom tools are
enabled.
## Write actions
`generate_document` and `create_meeting` consume your Meetingize API allowance, `update_meeting` changes a meeting
and `send_feedback` writes to the Meetingize team. The server always answers a
first call with a preview; ChatGPT must call again with `confirm: true` after you agree. ChatGPT may also ask you to
approve the action itself.
## Workspaces
On Business, Enterprise or Edu workspaces an admin may need to allow custom connectors before members can add
Meetingize. Each member connects their own Meetingize account.
---
# Cursor, VS Code and other clients
Any MCP client that supports the **Streamable HTTP** transport can use Meetingize. Clients that implement MCP
authorization (OAuth 2.1 discovery) connect with the URL alone; the others can send a Meetingize API key as a
bearer token.
## Cursor
`~/.cursor/mcp.json` (all projects) or `.cursor/mcp.json` (one project):
```json
{
"mcpServers": {
"meetingize": {
"url": "https://app.meetingize.com/api/mcp"
}
}
}
```
Cursor shows a **Login** button for the server and opens the Meetingize consent screen. With an API key:
```json
{
"mcpServers": {
"meetingize": {
"url": "https://app.meetingize.com/api/mcp",
"headers": { "Authorization": "Bearer ${env:MEETINGIZE_API_KEY}" }
}
}
}
```
## VS Code (GitHub Copilot agent mode)
`.vscode/mcp.json`:
```json
{
"servers": {
"meetingize": { "type": "http", "url": "https://app.meetingize.com/api/mcp" }
}
}
```
## MCP Inspector
The official inspector is handy to explore the tools:
```bash
npx @modelcontextprotocol/inspector
```
Choose transport **Streamable HTTP**, URL `https://app.meetingize.com/api/mcp`, then either run the OAuth flow from
the inspector or add the header `Authorization: Bearer mtgz_...`. Command line mode:
```bash
npx @modelcontextprotocol/inspector --cli https://app.meetingize.com/api/mcp --transport http \
--header "Authorization: Bearer $MEETINGIZE_API_KEY" --method tools/list
```
## Python SDK
```python
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
async def main():
headers = {"Authorization": f"Bearer {os.environ['MEETINGIZE_API_KEY']}"}
async with streamablehttp_client("https://app.meetingize.com/api/mcp", headers=headers) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
result = await session.call_tool("search_meetings", {"query": "budget", "limit": 5})
print(result.content[0].text)
asyncio.run(main())
```
## Any HTTP client
The server is stateless: no session id, every request is a JSON-RPC message posted to the URL with a bearer token.
```bash
curl -s https://app.meetingize.com/api/mcp \
-H "Authorization: Bearer $MEETINGIZE_API_KEY" \
-H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search_meetings","arguments":{"query":"budget"}}}'
```
Without a token the server answers `401` with a `WWW-Authenticate` header pointing to its
[protected resource metadata](https://app.meetingize.com/.well-known/oauth-protected-resource/api/mcp), which is how
OAuth-capable clients discover how to log in. `GET` on the endpoint answers `405` (no server-initiated stream).
---
# Tools, resources and prompts
## Tools
Read-only tools are annotated `readOnlyHint: true`. Write tools are annotated `readOnlyHint: false` and use a
two-step confirmation (see below).
| Tool | What it does | Permission |
|---|---|---|
| `search_meetings` | list or search meetings (title, report and transcript text), filter by client, tags, meeting date, status | `read:meetings` |
| `get_meeting` | metadata, participants, AI report and documents of a meeting | `read:meetings` |
| `get_meeting_summary` | the AI meeting report (Markdown and structured) | `read:meetings` |
| `get_participants` | participants and speaker labels | `read:meetings` |
| `get_transcript` | diarized timestamped transcript, split in parts (`part`, `max_tokens_per_part`) | `read:transcripts` |
| `search_transcripts` | find what was said: excerpts with timestamps and speakers across all meetings | `read:meetings` + `read:transcripts` |
| `export_meeting_for_llm` | the LLM-ready bundle of a meeting, in parts | `read:meetings` + `read:transcripts` |
| `list_personas`, `list_templates` | professional personas and document templates | `read:meetings` or `write:documents` |
| `generate_document` | generate a document from a template or a custom prompt (job, billed) | `write:documents` |
| `list_documents`, `get_document` | generated documents and their Markdown content | `read:documents` |
| `create_meeting` | create a meeting from a public https audio or video URL, with optional documents (job, billed) | `write:meetings` |
| `update_meeting` | rename a meeting, change its date or tags, add participants and say which speaker is who | `write:meetings` |
| `send_feedback` | report a bug or request a feature to the Meetingize team | any |
| `get_job`, `list_jobs` | status of meeting processings and document generations | any |
| `get_usage` | API consumption of the month, wallet and spend cap | `read:usage` |
| `get_account` | connected account, plan and granted permissions | any |
| `documentation` | the whole Meetingize documentation (index, a page, or everything) | none |
| `search`, `fetch` | ChatGPT search and deep research contract | `read:meetings` or `read:documents` |
Tool results contain readable text for the model and `structuredContent` (JSON) for clients that use it. Errors
(missing permission, not found, quota) come back as tool results with `isError: true` and an explanation the
assistant can relay.
### Long transcripts
`get_transcript` returns part 1 of N with about 12,000 tokens per part by default, never cutting a sentence, and
tells the assistant how to fetch the next part. This keeps two-hour meetings usable in any context window.
### Search
`search_meetings` and `search_transcripts` use case-insensitive full-text matching on titles, reports and
transcripts. Semantic search is Coming soon.
### Two-step confirmation for write tools
`generate_document` and `create_meeting` consume your API allowance; `update_meeting` changes a meeting and
`send_feedback` sends a message. Called without `confirm: true` they do nothing and return a preview ("generate a document from meeting X with template Y, billed on your allowance").
The assistant must show it to you and call again with the same arguments and `confirm: true`. The confirmed call
carries an idempotency key derived from the arguments, so a retry never starts the same job twice.
## Resources
| URI | Content |
|---|---|
| `meetingize://meetings/{meeting_id}` | meeting metadata, participants and AI report (Markdown) |
| `meetingize://meetings/{meeting_id}/transcript` | full transcript (Markdown) |
| `meetingize://documents/{document_id}` | a generated document (Markdown) |
| `meetingize://docs/{slug}` | a documentation page, `meetingize://docs/llms-full.txt` for all of it |
`resources/list` returns the documentation pages and your 20 most recent meetings.
## Prompts
| Prompt | Arguments | Workflow |
|---|---|---|
| `prepare_meeting` | `who`, `topic` (optional) | briefing before a meeting: history, decisions, commitments, open questions, agenda |
| `extract_commitments` | `who` (optional), `period` (optional) | table of commitments made by you and to you, with sources |
| `followup_email` | `meeting_id`, `tone` (optional) | follow-up email draft (the meeting report is attached) |
| `weekly_digest` | `days` (optional, default 7) | digest of recent meetings: decisions, open actions, risks |
---
# Security and permissions
## How does an assistant get access?
Meetingize implements the MCP authorization specification:
1. The assistant calls the server without a token and receives `401` with a link to the
[protected resource metadata](https://app.meetingize.com/.well-known/oauth-protected-resource/api/mcp) (RFC 9728).
2. It discovers the Meetingize authorization server (RFC 8414), registers itself (dynamic client registration,
RFC 7591, or a Client ID Metadata Document) and opens the Meetingize consent screen in your browser, with PKCE
(S256).
3. You sign in to Meetingize (the assistant never sees your credentials), check the application name, its domain
and the permissions requested, and click **Allow access** or **Deny**.
4. The assistant receives an access token valid **1 hour** and a refresh token valid **30 days**, rotated at each
use. Tokens are bound to your account and to the scopes you approved.
## Permissions (scopes)
| Permission | On the consent screen |
|---|---|
| `read:meetings` | Read your meetings (list, search, details, summary, participants, templates) |
| `read:transcripts` | Read the transcripts of your meetings |
| `read:documents` | Read the documents generated from your meetings |
| `read:usage` | Read your API consumption and wallet balance |
| `write:documents` | Generate new documents from your meetings (uses your API allowance) |
| `write:meetings` | Create meetings from audio URLs (uses your API allowance) and update their title, date, tags and participants |
Write permissions are highlighted on the consent screen. The MCP server can never delete a meeting or a document,
nor change your account or your sharing settings: no tool does it. With `write:meetings` it can only rename a
meeting, change its date and tags, and set who the speakers are, after your confirmation.
## Isolation
Every MCP call is executed by the same authorization layer as the [public API](../../api/authentication/). A token
only reaches the data of the user who approved it; meetings of other accounts answer "not found". Meetings shared
with you by someone else are not exposed to assistants.
## Revoking access
**Settings > Connected applications** in the web app lists every assistant you connected, its permissions, when you
authorized it and when it was last used. **Disconnect** deletes its tokens immediately; the assistant must ask for
your consent again to reconnect.
## Write confirmations and costs
Tools that consume your API allowance or change data (`generate_document`, `create_meeting`, `update_meeting`,
`send_feedback`) only act on a second call with
`confirm: true`, after showing you a preview. Your assistant (Claude, ChatGPT) also asks for your approval before
running non read-only tools. Usage alerts and a spend cap protect you from surprises, see
[billing](../../api/billing/).
## Good practices
- Approve only the permissions you need. A read-only connection (`read:*`) is enough to ask questions about your
meetings.
- Treat answers as drafts: quote-check important facts against the transcript (the tools return timestamps for
that).
- If you use an API key in a configuration file (Cursor, Claude Code), keep it out of version control and give it
read-only scopes.
- Remember that your assistant's provider processes what the assistant reads (transcripts, reports) under its own
terms. Do not connect accounts holding data you are not allowed to share with that provider.
---
# meetingize CLI
`meetingize` is a small command line client for the [Meetingize API](../../api/overview/). It is a single Python
file (Python 3.8 or later, no dependency), handy for scripts, cron jobs and quick checks.
## Install
```bash
curl -fsSL -o ~/.local/bin/meetingize https://docs.meetingize.com/cli/meetingize.py
chmod +x ~/.local/bin/meetingize
meetingize --version
```
(Any directory of your `PATH` works. On Windows run it with `python meetingize.py ...`.)
## Log in
Create an API key in **Settings > Developers**, then:
```bash
meetingize login # prompts for the key, checks it, stores it in ~/.config/meetingize/config.json (mode 600)
meetingize whoami
```
Alternatively set the `MEETINGIZE_API_KEY` environment variable.
## Commands
| Command | What it does |
|---|---|
| `meetingize meetings [-q TEXT] [--client NAME] [--tag T] [--since DATE] [--until DATE] [--status S] [--limit N]` | list or search meetings |
| `meetingize show MEETING_ID` | metadata, participants, AI report, documents |
| `meetingize transcript MEETING_ID [--format md\|txt\|srt\|vtt\|json] [-o FILE]` | diarized transcript |
| `meetingize export MEETING_ID [--max-tokens N] [--lang fr\|en] [-o FILE]` | LLM-ready export (Markdown, in parts) |
| `meetingize templates [--persona ID]` | document templates |
| `meetingize generate MEETING_ID (--template TPL \| --prompt TEXT) [--title T] [--wait] [-o FILE]` | generate a document (billed) |
| `meetingize documents [--meeting ID]` | list documents |
| `meetingize document DOC_ID [--format md\|pdf\|docx\|json] [-o FILE]` | download a document |
| `meetingize create FILE \| --url URL [--title T] [--recorded-at DATE] [--language fr] [--tag T] [--template TPL] [--persona ID] [--transcribe-only] [--wait]` | create a meeting from a recording, optionally with documents (billed) |
| `meetingize feedback MESSAGE [--type bug\|feature_request\|question\|feedback] [--meeting ID]` | report a bug or send feedback to the Meetingize team |
| `meetingize job JOB_ID [--wait]` | job status |
| `meetingize usage` | API consumption of the month |
| `meetingize webhooks verify --secret whsec_... --header 't=...,v1=...' --body FILE` | check a webhook signature locally |
Most listing commands accept `--json` for machine-readable output. Every `POST` is sent with a fresh
`Idempotency-Key`.
## Examples
```bash
# All meetings with client Dupont since July, then the transcript of the latest one as subtitles
meetingize meetings --client Dupont --since 2026-07-01T00:00:00Z
meetingize transcript 1b9e... --format srt -o rdv.srt
# Send a recording, generate a document after the report, and wait
meetingize create ./rdv-client.m4a --title "RDV Dupont" --language fr --template tpl_42 --wait
# Generate a follow-up email and print it
meetingize generate 1b9e... --prompt "Follow-up email with decisions and next steps" --wait
# Report a bug
meetingize feedback "The SRT export of meeting 1b9e... is empty" --type bug --meeting 1b9e...
# Paste a meeting into any chatbot
meetingize export 1b9e... --lang en | pbcopy
```
## Exit codes
`0` on success, a non-zero code with the API error (`HTTP 404 not_found: ...`) otherwise. `webhooks verify` exits
`1` when the signature is invalid.