Skip to main content

Overview

The protokoll export system generates meeting minutes (protokoll) in multiple formats. The JSON format is designed for programmatic consumption by scripts and integrations. It represents a filtered projection of the canonical aggregation hash produced by ProtokollAggregationService. The JSON renderer (ProtokollJsonRenderer) receives the full aggregation hash and a set of content_options, then produces a filtered JSON document with only the requested content. Keys for disabled content are omitted entirely (not set to null), so consumers can use key? checks to detect presence.

Versioning policy

Every JSON export includes a top-level version field (currently "2.0").
  • Additive changes within a major (2.x): new keys, new enum values, new audit-log actions. These do not bump the version.
  • Breaking changes bump the major: renames, removals, type changes, or reorderings of existing keys or values.
Consumers should tolerate unknown keys gracefully. New keys may appear in any 2.x release.

Version history

Timestamps

Every *_at value is an ISO 8601 string with an explicit offset. Since VOT-425 the app runs in the Europe/Oslo zone, so the offset is Oslo’s for that date: +02:00 in summer (CEST) and +01:00 in winter (CET), e.g. "2026-06-12T18:02:00+02:00". Exports generated before VOT-425 used UTC ("2026-06-12T16:02:00Z") for the same instant. Parse the values with an ISO 8601 / RFC 3339 parser and compare them as instants, not as strings. Date-only fields (starts_on, ends_on) are plain YYYY-MM-DD dates and have no offset. The Markdown, PDF and DOCX covers show the export time and the meeting start/end in Oslo local time (protokoll.time_format, e.g. «12.06.2026 kl. 18:02»).

Top-level structure

Every JSON export contains these keys:

Meeting lifecycle fields

Added in 2.0 (VOT-434, additive). They record the formal start of the meeting («Start møte») and its end. The Markdown, PDF and DOCX covers print the same information («Møtet startet» / «Møtet avsluttet», with the name).

Attendance

Added in 2.0 (VOT-436, additive). Everyone is present until the secretary marks them «Fraværende» (absent); a participant added by mistake once the meeting had started is marked «Feilregistrert» (registered in error) instead of being deleted. Per participant, attendance / absent_since give the state at export time, and registered_in_error flags a «Feilregistrert» participant. The Markdown, PDF, DOCX and CSV exports leave «Feilregistrert» participants out of the participant list and the attendance section; the JSON keeps them, flagged. An absent or «Feilregistrert» participant is not stemmeberettiget. A poll’s own eligible set is frozen when it closes (its snapshot): eligible_voter_count and non_voter_participant_ids reflect who was absent then, while participants[].can_vote is computed at export time.

Audit log

audit_log lists the allowlisted events (ProtokollAggregationService::AUDIT_LOG_ALLOWLIST) in chronological order: meeting.started, meeting.ended, meeting.reopened, poll.opened, poll.closed, poll.round_created, poll_option.withdrawn, agenda_item.manual_result_set, agenda_item.manual_result_cleared, agenda_item.completed, agenda_item.reopened, agenda_item.voting_restarted, and (VOT-436) participant.marked_absent, participant.marked_present, participant.marked_registered_in_error and participant.unmarked_registered_in_error (auditable_id is the participant). «Start møte» deletes every audit row written while the meeting was being prepared (owner decision, VOT-434), so for a started meeting the log begins with meeting.started. A meeting still being prepared has no lifecycle entries yet (and its rehearsal entries will be deleted when it starts). metadata of the lifecycle entries: A reopened meeting shows meeting.ended followed by meeting.reopened (and, if it was ended again, another meeting.ended); meeting.closed_at / closed_by describe the last end only.

The document and its export record

A generated JSON document never changes. Facts that arise later live on the export record (ProtokollExport), which the web UI’s export list shows next to the files:

Content options filtering

The content_options hash controls which keys appear in the output:

Poll settings modes

Vote detail modes

The vote_detail option controls how poll rounds appear in each agenda item’s polls array:

full_rounds

All closed rounds included with full detail (options, vote counts, outcome, settings).

summarized_plus_final (default)

Non-final rounds are reduced to summary keys only. The final/decisive round has full detail. Summary keys: round_number, round_summary, outcome, status.

final_only

Only the decisive round (where latest_for_agenda_item is true) appears, with full detail. Earlier rounds are omitted entirely.

Minimal example

A minimal export with default options for a meeting with one completed binary poll:
This is a simplified example. The actual output includes many more fields per object. See the key reference tables below for the complete set.

Key reference

Meeting object

Organization object

Participant object

Agenda item object

Poll object (full detail)

Poll object (summary detail)

When a poll is rendered in summary mode (non-final rounds under summarized_plus_final):

Poll option object

Audit log entry

Last modified on October 21, 2026