The Activity Log and Its Sources
The activity log is a chronological list of all history of the dialog. The history is derived from different sources, consolidated and collected in one timeline.
The four sources
Section titled “The four sources”| Source | Comes from | Contributes |
|---|---|---|
| Activities | dialog.activities on the dialog itself | One sentence per activity |
| Transmissions | dialog.transmissions on the dialog itself | An expandable card, or one sentence, depending on visibility |
| Notification log | notificationLogs(dialogId), a separate query | One sentence per correlated dispatch |
| Label assignment log | labelAssignmentLog(dialogId), a separate query | One sentence per label change worth reporting |
They are combined in getActivityHistory and sorted by date, newest first. The two newest sources are behind feature flags — dialogDetails.enableNotificationLogs and dialogDetails.enableLabelAssignmentLogs, both off by default — so the log degrades to activities and transmissions when either is unavailable.
Rules that apply to every source
Section titled “Rules that apply to every source”- One list, not sections. Nothing gets its own heading or its own component. A new source earns a line in the existing log or it does not ship.
- One entry, one sentence. The only exception is a fully visible transmission, which is a card because it has expandable content.
- Active voice, actor first, actor emphasised. “Kari Nordmann flyttet meldingen til arkivet.” This is what makes four unrelated sources read as one log rather than as interleaved voices. A sentence with no actor — a notification, a transmission the user cannot open — says what happened to the thing itself rather than inventing someone to do it.
- It is a melding, never a dialog. Dialog is the data model; the sentence uses the word the user knows it by.
- One actor resolution. Every source runs
performedBythrough the samegetActorProps, so person-name flipping (dialogporten.disableFlipNamesPatch), system-user and organisation detection from theactorIdURN, and the service-owner fallback tosenderNameor the organisation name behave identically everywhere. A service owner arrives with noactorIdand noactorNameand resolves to the dialog’s sender name when one is given, otherwise the organisation name. - The timestamp is the sort key and the byline. Entries with the same timestamp keep the order their source returned them in.
1. Activities
Section titled “1. Activities”The most direct source: each DialogActivityFragment maps by its type to one sentence.
ActivityType | Bokmål |
|---|---|
DialogCreated | {actor} opprettet meldingen. |
DialogOpened | {actor} åpnet meldingen. |
DialogClosed | {actor} lukket meldingen. |
DialogDeleted / DialogRestored | {actor} slettet / gjenopprettet meldingen. |
Information | {actor}: {description} |
TransmissionOpened | {actor} åpnet «{transmission}». |
FormSaved / FormSubmitted | {actor} lagret / sendte inn skjemaet. |
PaymentMade | {actor} betalte. |
SignatureProvided | {actor} signerte. |
SentTo… | {actor} sendte meldingen til skjemautfylling / betaling / innsending / signering. |
CorrespondenceOpened / CorrespondenceConfirmed | {actor} åpnet / bekreftet meldingen. |
Information is the only type carrying service-owner-authored text; the rest are fixed sentences. The transmission title is free text in the middle of a sentence, so it is quoted.
Same event, same sentence; different event, different verb. DialogOpened and CorrespondenceOpened both read åpnet meldingen, because to the user they are the same thing. DialogRestored keeps gjenopprettet: it undoes a deletion, which is not the same as bringing a message back from the bin — that is a label change, and reads flyttet meldingen til innboksen.
Unknown types are printed raw. An ActivityType with no case falls through to rendering the enum value itself. See Where the sources disagree.
2. Transmissions
Section titled “2. Transmissions”A transmission is classified before it is rendered, and the classification decides whether the user gets a card or a sentence. The order of the checks matters — the first match wins.
| # | Condition | Outcome |
|---|---|---|
| A | Has attachments, none GUI-consumable, no summary and no content reference | filter — dropped from the timeline, shown in the log as “Forsendelse kun med maskinlesbart vedlegg” |
| B | isAuthorized is false | disabled — card stays in the timeline but cannot expand; in the log it reads “Forsendelse du ikke har tilgang til” |
| C | Authorized, but no summary, no content reference and no GUI attachment | empty — expands to an empty-state explanation; in the log only the title is shown |
| D | Anything else | visible — a normal expandable card |
Only D becomes a card in the log. A, B and C become plain sentences, because there is nothing worth expanding. The sentence for B ends in a preposition, so tilgang til (and access to) is joined by a non-breaking space: on a narrow screen the line would otherwise break before til, and the first line reads as a complete sentence without it.
Related transmissions are grouped into one segment via relatedTransmissionId, resolved as a bidirectional graph so a chain of replies collapses into a single entry rather than several.
3. Notification log
Section titled “3. Notification log”This source is an MVP, and its rules are mostly about not flooding the log.
What reaches the frontend
Section titled “What reaches the frontend”The BFF filters the upstream log before it answers notificationLogs, so everything the frontend receives is worth a line:
- Only delivered notifications.
Deliveredpasses. Every other status — in flight, handed off but not confirmed, or failed — is dropped. The log reports what the user actually received. - Statuses carry no channel prefix. The log returns
Delivered, not theEmail_Delivered/SMS_Deliveredshown in Altinn’s status reference. The channel is in its own field. InstantandComposeddispatches are dropped.Notification,Reminderand any type we do not recognise pass through.
Placement
Section titled “Placement”lastUpdateTimedecides placement. For a delivered notification it is when the provider reported the delivery, so the entry sits where the user actually got it.- A dispatch to several recipients sits at the latest of their delivery times, because only then is ”… til A, B og C ble levert” true.
- When no delivery time was reported, the requested send time is used instead. With neither, the entry has no position and is dropped.
Grouping
Section titled “Grouping”The API has no correlation id, so Arbeidsflate derives one: type + channel + transmissionId + requestedSendTime. The recipients of one dispatch share its requested send time, while each has a delivery time of its own — which is why grouping and placement use different timestamps.
- The same dispatch to several recipients becomes one entry.
- When no send time was reported, the last update time stands in for it in the key.
Wording
Section titled “Wording”The sentence is {type} på {channel} til {recipients} ble levert. Only delivered notifications reach the frontend, so the outcome is part of the sentence rather than a slot of its own.
- The notification is the subject. There is no actor to put first, so the sentence says what happened to the dispatch.
- The channel is named once, in its own slot. Delivery reads “ble levert” whether it went by email or SMS.
- Only
Remindergets its own word, because “påminnelse” tells the user something real. Everything else is “Varsel”. - At most three recipients are listed, the rest are counted. Without the cap, one dispatch to many organisation addresses fills the log with addresses.
- An unknown value degrades, it does not hide. An unknown type becomes “Varsel” and an unknown channel “ukjent kanal”. Hiding a notification the user actually received is worse than describing it generically — and the gap would be invisible to us.
4. Label assignment log
Section titled “4. Label assignment log”End-user system label changes: where the dialog sits for this user. The operations are set and remove; the labels are DEFAULT, ARCHIVE, BIN, SENT and MARKED_AS_UNOPENED.
Names arrive prefixed — systemlabel:MarkedAsUnopened. Parsing ignores the prefix, the casing and any underscores, so the API’s PascalCase and the GraphQL enum’s SCREAMING_SNAKE_CASE land on the same label. Removals are accepted as both remove and removed.
What is excluded
Section titled “What is excluded”SENTis never shown. It is logged as set by the service owner after the dialog was created, which makes it an attempt to put the dialog in a desired state rather than something a user did. Future labels should be judged by the same test: is this a user operation?- An entry that cannot become a sentence is dropped, never printed raw. This is safe because a unit test requires every value of the
SystemLabelenum to have both asetand aremovesentence in nb, nn and en — a new label upstream fails the build instead of silently disappearing.
What becomes a line
Section titled “What becomes a line”DEFAULTis never set. The platform expresses “in the inbox” as the absence of the other two folder labels, so moving a dialog back to the inbox is aremoveofARCHIVEorBIN, never asetofDEFAULT. ThesetofDEFAULThas a sentence, but it is not reachable with current API behaviour.- A move says where the message went, in the words of the buttons. Flytt til arkivet, Flytt til papirkurven and Flytt til innboksen are logged as flyttet meldingen til arkivet, til papirkurven and til innboksen. So the
removeofARCHIVEorBINreads flyttet meldingen til innboksen, and where the message came from is usually the entry below it. Like the button, it says innboksen even when the message also carriesSENTand so lands in Sendt. - One move, one sentence. The folders are mutually exclusive, so a move writes a
setof the destination and aremoveof the origin at the same instant. Entries are grouped by (timestamp, actor), and aremoveof a folder label is dropped when the same group holds a folderset. Moving from the bin to the archive says the dialog was archived — not that it first lost its bin label. - A
removethat arrives alone is still shown. This is not an edge case: it is how the return to the inbox appears, and it is what happens when the dialog is updated — the platform clears the archive or bin label so the dialog resurfaces, and seeing why it reappeared is the useful part. - Read and unread are never collapsed.
MARKED_AS_UNOPENEDsits outside the folder logic, so both directions say something. ”… markerte meldingen som lest” is what a reader sees when a dialog they had marked unread is opened again. - Different actors never collapse into each other. The actor is part of the grouping key, so a
setby a user and aremoveby the service owner in the same instant are two events by two parties, and both are reported.
Every sentence
Section titled “Every sentence”All eight follow the shared form, {actor} … meldingen ….
| Label | set | remove |
|---|---|---|
BIN | flyttet meldingen til papirkurven | flyttet meldingen til innboksen |
ARCHIVE | flyttet meldingen til arkivet | flyttet meldingen til innboksen |
DEFAULT | flyttet meldingen til innboksen (unreachable) | flyttet meldingen ut av innboksen |
MARKED_AS_UNOPENED | markerte meldingen som ulest | markerte meldingen som lest |
Nynorsk and English carry the same meaning.