Skip to main content
All Docs
FeaturesagentOS Block ManagerUpdated April 13, 2026

Meetings & Resolutions

Meetings & Resolutions

The meetings feature provides a complete management and archive layer for all formal meetings held by a block — Annual General Meetings (AGMs), Extraordinary General Meetings (EGMs), and directors' meetings for RMC/RTM companies.

Overview

Every meeting is scoped to a single block and moves through a defined status lifecycle. Once a meeting has been held or cancelled, it becomes part of the permanent, immutable archive for that block — preserving a legally auditable record of decisions, attendance, and votes.

Meeting Types

TypeDescription
agmAnnual General Meeting — the mandatory annual meeting of leaseholders
egmExtraordinary General Meeting — called for urgent or specific business
directorsDirectors' meeting of the RMC/RTM company

Meeting Status Lifecycle

scheduled → held
          → cancelled
  • scheduled: The meeting is planned but has not yet taken place. It can be edited or deleted.
  • held: The meeting has taken place. It cannot be deleted — it forms part of the permanent archive.
  • cancelled: The meeting was cancelled. It cannot be deleted — the cancellation is itself a historical record.

Archive protection: Attempting to delete a held or cancelled meeting will return a PRECONDITION_FAILED error.

Data Model

Meetings (meetings)

The top-level meeting record for a block.

FieldTypeDescription
idtextPrimary key
orgIdtextTenant identifier
blockIdtextThe block this meeting belongs to
meetingTypeagm | egm | directorsType of meeting
titletextHuman-readable title, e.g. "Maple Court AGM 2025"
datetimestampScheduled date and time
venuetext?Location or "Virtual — Zoom" etc.
statusscheduled | held | cancelledCurrent status (default: scheduled)
quorumMetboolean?Whether quorum was achieved (set when marking as held)
chairUserIdtext?The user who chaired the meeting
minutesHtmltext?HTML minutes from the rich text editor
notestext?Internal notes (not published in minutes)
createdBytextUser who created the record

Agenda Items (meeting_agenda_items)

Ordered list of items on the meeting agenda.

FieldTypeDescription
meetingIdtextParent meeting
sortOrderintegerDisplay order (1, 2, 3, …)
titletextAgenda item title
descriptiontext?Additional context for the item
resolutionTexttext?Proposed resolution wording (null if informational only)

Attendees (meeting_attendees)

Tracks who attended, including proxy representatives.

FieldTypeDescription
meetingIdtextParent meeting
unitIdtextThe unit (leaseholder) attended or represented
leaseholderNametext?Name snapshot captured at the time of recording
proxybooleantrue if the leaseholder was represented by proxy
proxyHolderNametext?Name of the proxy holder (if proxy = true)
arrivedAttimestamp?Time the attendee or proxy arrived

Leaseholder name snapshots: The leaseholder name is read from the unit record when the attendee is added and stored on the attendee row. This ensures the archive remains accurate even if the unit changes ownership later.

Resolutions (resolutions)

Formal vote outcomes linked to specific agenda items.

FieldTypeDescription
meetingIdtextThe meeting at which the vote was held
agendaItemIdtextThe agenda item this resolution corresponds to
texttextFinal wording of the resolution (may differ from the agenda item's proposed text if amended on the floor)
votesForintegerVotes in favour
votesAgainstintegerVotes against
votesAbstainintegerAbstentions
passedbooleanWhether the resolution passed (determined by the chair per the articles of association)
notestext?e.g. "Passed by show of hands", "Poll demanded"

tRPC API Reference

All procedures are namespaced under meeting.*.

Access Control

Permission levelProcedures
org member (orgProcedure)list, getById, listAgendaItems, listAttendees, listResolutions
admin (adminProcedure)All create, update, and delete procedures

Meetings

meeting.list

Returns a paginated list of meetings for the organisation.

Input

{
  cursor?: string          // Pagination cursor
  limit?: number           // Page size
  blockId?: string         // Filter by block
  meetingType?: 'agm' | 'egm' | 'directors'
  status?: 'scheduled' | 'held' | 'cancelled'
}

Results are ordered by date descending (most recent first). Each row includes the joined blockName.


meeting.getById

Returns a single meeting with its full agenda, attendee count, and resolution count.

Input

{ id: string }

Response includes agendaItems[], attendeeCount, and resolutionCount in addition to all meeting fields. minutesHtml is included in this response but omitted from the list response.


meeting.create

Creates a new meeting for a block. The block must belong to the organisation.

Input

{
  blockId: string
  meetingType: 'agm' | 'egm' | 'directors'
  title: string            // max 500 chars
  date: string             // ISO 8601 datetime
  venue?: string           // max 500 chars
  notes?: string
}

meeting.update

Updates one or more fields on a meeting. All fields are optional — only provided fields are changed.

Input

{
  id: string
  title?: string
  date?: string            // ISO 8601 datetime
  venue?: string | null
  meetingType?: 'agm' | 'egm' | 'directors'
  status?: 'scheduled' | 'held' | 'cancelled'
  quorumMet?: boolean | null
  chairUserId?: string | null
  minutesHtml?: string | null
  notes?: string | null
}

Minutes and notes can be updated regardless of status — this allows minutes to be added after a meeting has been marked as held.


meeting.delete

Deletes a meeting and all its child records (agenda items, attendees, resolutions). Only scheduled meetings can be deleted.

Input

{ id: string }

Error: Returns PRECONDITION_FAILED if the meeting status is held or cancelled.


Agenda Items

meeting.listAgendaItems

{ meetingId: string }

Returns items ordered by sortOrder ascending.

meeting.createAgendaItem

{
  meetingId: string
  sortOrder: number        // integer ≥ 1
  title: string            // max 500 chars
  description?: string
  resolutionText?: string  // null if item is informational only
}

meeting.updateAgendaItem

{
  id: string
  sortOrder?: number
  title?: string
  description?: string | null
  resolutionText?: string | null
}

meeting.deleteAgendaItem

{ id: string }

Also deletes any resolutions linked to this agenda item.


Attendees

meeting.listAttendees

{ meetingId: string }

Returns attendees with unitNumber joined from the units table. Ordered by createdAt ascending.

meeting.addAttendee

{
  meetingId: string
  unitId: string
  proxy?: boolean          // default false
  proxyHolderName?: string // max 200 chars; required if proxy = true
  arrivedAt?: string       // ISO 8601 datetime
}

The leaseholder name is automatically snapshotted from the unit record at insert time.

meeting.removeAttendee

{ id: string }

Resolutions

meeting.listResolutions

{ meetingId: string }

Returns resolutions with agendaItemTitle joined. Ordered by createdAt ascending.

meeting.createResolution

{
  meetingId: string
  agendaItemId: string
  text: string             // Final resolution wording
  votesFor: number         // integer ≥ 0
  votesAgainst: number     // integer ≥ 0
  votesAbstain: number     // integer ≥ 0
  passed: boolean
  notes?: string
}

The agenda item must belong to the specified meeting, validated server-side.

meeting.updateResolution

{
  id: string
  text?: string
  votesFor?: number
  votesAgainst?: number
  votesAbstain?: number
  passed?: boolean
  notes?: string | null
}

meeting.deleteResolution

{ id: string }

Proxy Voting

Proxy voting is first-class in the data model. When adding an attendee:

  • Set proxy: true to indicate the leaseholder was not present in person.
  • Provide proxyHolderName to record who held the proxy.
  • The unit (leaseholder) is still referenced by unitId so the proxy vote is correctly attributed to the right leasehold.

Audit Logging

Every write operation emits an audit log entry. The following actions are recorded:

ActionTrigger
meeting.createdNew meeting created
meeting.updatedMeeting fields updated
meeting.deletedMeeting deleted
meeting.agenda_item_createdAgenda item added
meeting.agenda_item_updatedAgenda item updated
meeting.agenda_item_deletedAgenda item removed
meeting.attendee_addedAttendee recorded
meeting.attendee_removedAttendee removed
meeting.resolution_recordedResolution created
meeting.resolution_updatedResolution updated
meeting.resolution_deletedResolution deleted

Tenant Isolation & Security

  • All four tables (meetings, meeting_agenda_items, meeting_attendees, resolutions) are registered in RLS_TABLES for database-level row-level security.
  • All tRPC queries include an orgId filter — data from one tenant is never accessible to another.
  • Block ownership is validated on meeting creation: a meeting cannot be created for a block belonging to a different organisation.