Skip to content

Meetings API

All endpoints require authentication and meetings tool permission.

GET /api/meetings

Query parameters:

ParamTypeDescription
pagenumberPage number (default: 1)
per_pagenumberItems per page (default: 20, max: 50)
categorystringFilter by AI category
entity_typestringFilter by matched entity type
entity_idstringFilter by matched entity ID
attendee_user_idstringFilter by attendee user ID
unmatchedbooleanShow only unmatched meetings
date_fromstringFilter from date (ISO 8601)
date_tostringFilter to date (ISO 8601)
searchstringSearch title and summary

Response includes visibility scoping based on user’s access level.

GET /api/meetings/:id

Returns meeting detail with attendees, action items, and match info.

GET /api/meetings/:id/transcript

Lazy-fetches transcript from Granola API and caches in D1. Returns cached version on subsequent requests.

POST /api/meetings/:id/match

Requires can_update permission.

Body:

{
"entity_type": "company",
"entity_id": "abc123"
}
POST /api/meetings/:id/unmatch

Clears the matched entity.

POST /api/meetings/:id/action-items/:actionId/status

Body:

{
"status": "done"
}

Valid statuses: pending, done, dismissed.

POST /api/meetings/:id/reanalyse

Triggers AI re-categorisation. Returns 202 Accepted.

GET /api/meetings/stats

Returns { unmatched_count: number } for sidebar badge.

Require admin access.

POST /api/admin/connections/granola/sync

Returns 202 Accepted. Sync runs in the background.

GET /api/admin/connections/granola/status

Returns:

{
"connected": true,
"last_synced_at": "2026-03-27T10:00:00Z",
"sync_interval_minutes": 5,
"sync_enabled": true,
"total_notes": 247,
"unmatched_count": 12
}
  • meetings — Core meeting data (PK: Granola note ID)
  • meeting_attendees — Attendees with email→user resolution
  • meeting_transcripts — Cached transcripts (lazy-fetched)
  • meeting_action_items — AI-extracted and manual action items