Meetily

Meetily APIs

The full HTTP API surface: every route, its scope, request and response shapes, the error envelope, and the operation annotations - grounded in the running gateway.

The live manifest is canonical; this page is the readable map

The single source of truth for every route is the manifest the running gateway serves itself at GET /openapi.json (Read scope). It's OpenAPI 3.1, generated from the same code that serves the routes, so it can't drift from what's actually running. A committed copy, openapi.gen.json, ships alongside the app for offline tooling and MCP/SDK generators. This page mirrors that manifest in prose and can lag it by a release - when the two disagree, the manifest wins.

Base URL and transport

The gateway is a local HTTP listener exposed by a running Meetily Pro desktop app. It is Pro-gated and off by default - turn on the Automation API switch under Settings > Integrations first (see Enable & connect).

Value
Default addresshttp://127.0.0.1:8420 (loopback)
LAN address (when LAN access is on)https://0.0.0.0:8421 (self-signed TLS)
AuthAuthorization: Bearer <token> on every route except /health and pairing
Content typeapplication/json for bodies, except import (application/octet-stream)

The listener binds loopback only. It flips to 0.0.0.0 on the TLS port only when you explicitly enable LAN access; there is no hosted relay and no NAT traversal. Quit Meetily and the API stops serving immediately.

Scopes

A token carries one or more of exactly four scopes. There is no admin scope - the string admin parses to nothing and grants nothing.

ScopeGrants
readEvery GET, plus the SSE wait streams and /openapi.json.
recordRecording control (start, stop, pause, resume). Also satisfies read.
writeCreating, changing, or removing data (rename, summaries, jobs, config, webhooks). Also satisfies read.
deleteDeleting a meeting. Granted separately; does not imply read or write, and is never handed to a paired device.

read is the floor: a record- or write-scoped token can call read routes too. record, write, and delete each require their own literal scope - holding write does not grant record or delete.

Error envelope

Every non-2xx response has the same shape:

{"error": {"code": "...", "message": "..."}}

code is a stable machine string; message is human-readable and may change. The codes the gateway emits:

CodeHTTPMeaning
bad_request400Malformed request, or references something invalid.
invalid_request422The body or parameters failed validation.
unauthorized401No token, or the token isn't recognized.
forbidden403Valid token, but not allowed to do this.
insufficient_scope403The token's scopes don't cover this call.
not_prearmed403A remote caller tried to start recording without pre-arming - see Pairing & remote access.
not_found404The resource doesn't exist.
conflict409Conflicts with the resource's current state. Carries a specific sub-code (below).
payload_too_large413The import body exceeds the per-file cap.
too_many_requests429Rate- or budget-limited; carries a specific sub-code and often a Retry-After.
license_required402The call needs a Pro license this install doesn't have.
consumer_disabled403The calling key exists but its access is switched off.
api_disabled403The Automation API itself is turned off.
service_unavailable503A dependency needed for the call isn't available.
internal500Something failed on Meetily's side.

409 conflict and 429 too_many_requests narrow to a specific sub-code in code for common cases - for example import_not_pausable, not_retryable, not_terminal, summary_generating, recording_active, recording_in_progress, engine_busy (conflicts), and import_budget_full (budget). Match on the sub-code where one exists rather than the generic family.

Operation annotations

Every operation in the manifest carries four x-meetily-* annotations so tooling (including the MCP server) can reason about a call before making it:

  • x-meetily-read-only - the call only reads state.
  • x-meetily-idempotent - safe to retry with the same result.
  • x-meetily-destructive - deletes or otherwise can't be undone.
  • x-meetily-required-scope - the scope a token needs to call it.

Routes

Every route below is prefixed by the base URL. The Scope column is the enforced floor; a few routes layer an ownership or per-event check on top, noted inline. SSE routes are curl -N streams that emit exactly one event, then close.

Health, identity, pairing

MethodPathScopeNotes
GET/healthnoneLiveness. No token. Always returns 200.
GET/v1/whoamireadReturns WhoamiResponse - your token's scopes, license tier, and the server version.
GET/openapi.jsonreadThe canonical manifest. Read-scoped, not public.
GET/v1/search?q=&limit=&offset=readFull-text search across transcripts. Empty q400. limit clamps to 1..200. Returns SearchResponse.
POST/v1/pair/requestnoneBootstrap a token from another device. Body PairRequestBody. See Pairing & remote access.
GET/v1/pair/{id}nonePoll a pairing request until it's approved or rejected.

Pairing has no HTTP admin routes

Listing and revoking existing pairings, and minting an MCP client token, are in-process desktop actions, not HTTP routes - the former /v1/pairings* and /v1/mcp/client-token endpoints were removed. Only requesting and polling a new pairing are on the HTTP surface.

Meetings

MethodPathScopeNotes
GET/v1/meetings?limit=&offset=readPaginated. limit clamps to 1..200. Returns MeetingListResponse (meetings, total, limit, offset).
GET/v1/meetings/{id}readOne meeting - MeetingDto.
PATCH/v1/meetings/{id}writeRename. Body RenameMeetingRequest (title).
DELETE/v1/meetings/{id}?delete_files=deleteDestructive, no undo. Needs the delete scope. delete_files (default false) also removes the recording file.
GET/v1/meetings/{id}/transcriptreadTranscriptResponse - segments with speaker, timestamps, and optional word timings.
GET/v1/meetings/{id}/summaryreadSummaryDto - status, result, and regeneration_failed.
PUT/v1/meetings/{id}/summarywriteOverwrite the stored summary outright. Body OverwriteSummaryRequest (text). No undo.
POST/v1/meetings/{id}/summary/regeneratewriteKick off an LLM regeneration. Body RegenerateSummaryRequest (all optional). Returns 202 + RegenerateResponse with an operation_id and wait_url.
GET/v1/meetings/{id}/summary/operations/{operation_id}readPoll a regeneration operation's status.
GET/v1/meetings/{id}/summary/operations/{operation_id}/wait?timeout_ms=readSSE - resolves when that regeneration finishes.
PUT/v1/meetings/{id}/speaker-labelswriteRename speakers. Body SpeakerLabelsRequest (labels object).
GET/v1/meetings/{id}/export?format=readformat = json (default), md, or txt (aliases markdown, text). Returns ExportDto for json, otherwise the rendered document.

Config

MethodPathScopeNotes
GET/v1/config/{section}readRead one config section. Returns ConfigDto (section, config, withheld).
PUT/v1/config/{section}writeReplace a section immediately - no dry-run. Arming auto-record additionally requires record and pre-arm.

Valid sections: vad, diarization, speaker-identification, meeting-detection, global-shortcut, audio-player, custom-words, initial-prompt. Any other name → 404.

Jobs

MethodPathScopeNotes
GET/v1/jobsreadAll jobs - JobListResponse.
GET/v1/jobs/{id}readOne job - JobDto. Carries meeting_id once one is assigned.
POST/v1/jobs/diarizationwriteBody SubmitDiarizationRequest (meeting_id, optional speaker_count). A rerun conflicting with a running job → 409. Returns JobIdResponse.
POST/v1/jobs/importwriteImport an external audio file. Body is the raw bytes (application/octet-stream); query filename (required), plus optional title, language, model, provider. Returns 202 + JobIdResponse. See Import a recording.
POST/v1/jobs/{id}/cancelwriteCancel a running job.
POST/v1/jobs/{id}/pausewritePause. Import jobs can't pause409 import_not_pausable.
POST/v1/jobs/{id}/resumewriteResume a paused job (not import).
POST/v1/jobs/{id}/retrywriteRetry a failed job, when is_retryable.
GET/v1/jobs/{id}/wait?timeout=readSSE - resolves when the job reaches a terminal state.

Job state is `succeeded`, not `completed`

A JobDto.state moves through queued → running → succeeded (or failed, cancelled, plus the transient pausing/paused/resuming/cancelling). Terminal success is succeeded. The webhook event named job.completed is a different thing from the job state - don't poll for a completed state, it never appears.

Devices and models

MethodPathScopeNotes
GET/v1/devicesreadAudio input/output devices - DeviceListResponse.
GET/v1/modelsreadTranscription/summary models and their status - ModelListResponse.

Recording

MethodPathScopeNotes
GET/v1/recordingreadCurrent recording state - RecordingStateDto (state, active_meeting_id, last_error).
POST/v1/recording/startrecordStart capture. Body StartRecordingRequest - all fields optional. A remote (non-loopback) caller is refused 403 not_prearmed unless pre-armed.
POST/v1/recording/stop?session=recordStop and finalize.
POST/v1/recording/pauserecordPause capture.
POST/v1/recording/resumerecordResume capture.
GET/v1/recording/wait?until=&timeout=readSSE - until (required) is started or stopped. Resolves on that transition. Note this is read, not record.

`consent_attested` is a real field

StartRecordingRequest.consent_attested is optional. Omitting it, or sending true, starts recording. Sending an explicit false is rejected with 400 ("consent_attested must be true to start a recording"). It captures real microphone and/or system audio the instant it returns - run it deliberately.

Webhooks

Registration, delivery mechanics, the HMAC signature, the SSRF allowlist, and the approval_state gate are all covered in depth in Webhooks & SSE; this is the route surface.

MethodPathScopeNotes
POST/v1/webhooksread floor + per-event scopeRegister. Body CreateWebhookRequest (url, events[], optional delivery_mode). Returns 201 with the one-time hmac_secret. Each event you subscribe to is scope-gated to what your token can read.
GET/v1/webhooksreadYour own webhooks. ?all=true needs delete + a first-party token.
GET/v1/webhooks/{id}readOne webhook - includes its approval_state. Owner-only.
DELETE/v1/webhooks/{id}read floorDelete one you own. Does not need the delete scope (that scope is for meetings). Owner-only.
GET/v1/webhooks/{id}/deliveriesreadDelivery log (most recent 100). Owner-only.
POST/v1/webhooks/{id}/testread floorSend a synthetic delivery to confirm your receiver + signature check. Owner- and egress-consent-gated. Returns 202.

The 17 subscribable event names, the thin notification-only envelope, and which events are dormant are in the Event catalogue. Note that meeting.* and transcription.completed are not subscribable and are rejected at registration with 400.


Key schemas

Request and response bodies, from the manifest. * marks a required field; ? marks nullable/optional.

Requests

SchemaFields
RenameMeetingRequesttitle*
OverwriteSummaryRequesttext*
RegenerateSummaryRequestmodel?, model_name?, template_id?, language? (all optional; omitted fields fall back to the app's stored config)
SpeakerLabelsRequestlabels* (object, e.g. {"Speaker 1":"Alice"})
SubmitDiarizationRequestmeeting_id*, speaker_count? (omit for auto)
StartRecordingRequestmeeting_name?, mic_device_name?, system_device_name?, consent_attested?
CreateWebhookRequesturl*, events* (non-empty), delivery_mode? (at-least-once default, or at-most-once)
PairRequestBodyclient_name*, scopes*

Responses

SchemaFields
WhoamiResponsetoken_id*, scopes*, license_tier*, server_version*
MeetingListResponsemeetings* (MeetingDto[]), total*, limit*, offset*
MeetingDtoid*, title*, created_at*, updated_at*
TranscriptResponsemeeting_id*, title*, segments* (TranscriptSegmentDto[])
SummaryDtomeeting_id*, status*, result?, regeneration_failed, error?, updated_at*
RegenerateResponseoperation_id*, wait_url*, status_url*, state*, meeting_id*, run_id?, ...
JobDtoid*, kind*, state*, stage*, progress*, meeting_id?, is_retryable*, error?, created_at*, ...
JobIdResponsejob_id*
RecordingStateDtostate*, active_meeting_id?, last_error?
SearchResponseresults* (SearchResultDto[] - meeting_id, title, match_context, timestamp)
ConfigDtosection*, config*, withheld*
DeviceListResponse / ModelListResponsedevices* / models*
ExportDtomeeting*, transcript*, summary?

Is there an SDK?

No. There is no published or maintained SDK. The supported clients are the meetily-pro CLI and the raw HTTP API documented here; for an MCP-compatible assistant, use the MCP server.

An unsupported, vendored Python helper exists in the separate meetily-workflows examples repo. It isn't part of this product and isn't maintained as one - treat it as example code, not a dependency.

Last updated on

On this page