NotesCalendar: Reading and Writing Domino Calendar Data with LotusScript
You want to automate Domino calendar entries — auto-create weekly stand-up meetings, sync external scheduling systems (HR, project management) into mail.nsf, or build an agent that auto-accepts certain types of invitations. Then you read the docs and discover: calendar data isn’t stored in ordinary documents. It lives in mail.nsf in iCalendar format (RFC 5545), accessible only through three specialised classes.
This article covers how all three classes work together, from creating entries to reading a date range to handling meeting invitations.
TL;DR
- Three classes, three roles:
NotesCalendar(the calendar object, entry point),NotesCalendarEntry(a single event or recurring series),NotesCalendarNotice(a meeting invitation or update notice) - Entry point:
session.GetCalendar(db)—dbmust be a mail database (IsMailDatabase = True) - Creating entries:
CreateEntry(iCalString)takes an RFC 5545 iCalendar string — you’re not setting individual fields ReadRangebatch-reads all events in a date range as an iCalendar stringGetNewInvitationsreturns unprocessed invitations asNotesCalendarNoticeobjects;NoticeActionaccepts or declines them- Set
session.ConvertMIME = Falsebefore any calendar operation — the default True causes MIME-to-rich-text conversion that silently corrupts calendar structure
How the three classes divide the work
| Class | Represents | How to obtain |
|---|---|---|
NotesCalendar | The user’s whole calendar | session.GetCalendar(mailDb) |
NotesCalendarEntry | A single event, to-do, or recurring series | calendar.GetEntry(uid) or calendar.CreateEntry(iCalString) |
NotesCalendarNotice | An invitation or update notice | Returned by calendar.GetNewInvitations() |
Getting a NotesCalendar object
Dim session As New NotesSessionDim mailDb As NotesDatabase
' Open the user's mail.nsfSet mailDb = session.GetDatabase("MailServer/ACME", "mail/jsmith.nsf")If Not mailDb.IsOpen Then Call mailDb.Open("", "")
' Obtain the calendar object (must be a mail database)Dim cal As NotesCalendarSet cal = session.GetCalendar(mailDb)GetCalendar only accepts a mail database (db.IsMailDatabase = True). Passing a regular NSF throws an error.
CreateEntry — write a new calendar event
CreateEntry takes an iCalendar-format string and returns the new NotesCalendarEntry:
' Create a meeting on 2026-06-03 from 10:00 to 11:00 (UTC)Dim iCalStr As StringiCalStr = "BEGIN:VCALENDAR" & Chr(13) & Chr(10) & _ "VERSION:2.0" & Chr(13) & Chr(10) & _ "BEGIN:VEVENT" & Chr(13) & Chr(10) & _ "DTSTART:20260603T020000Z" & Chr(13) & Chr(10) & _ "DTEND:20260603T030000Z" & Chr(13) & Chr(10) & _ "SUMMARY:Monday stand-up" & Chr(13) & Chr(10) & _ "LOCATION:Conference Room A" & Chr(13) & Chr(10) & _ "END:VEVENT" & Chr(13) & Chr(10) & _ "END:VCALENDAR"
Dim entry As NotesCalendarEntrySet entry = cal.CreateEntry(iCalStr)
If Not (entry Is Nothing) Then Print "Created — UID: " & entry.UIDEnd IfKey points:
- Use UTC times (trailing Z) — local timezone strings interact badly with server timezone settings
- Line endings must be CRLF (
Chr(13) & Chr(10)) — RFC 5545 requires it - When
AutoSendNotices = True, a VEVENT with ATTENDEE lines will automatically send invitation emails
ReadRange — batch-read a date range
Dim startDt As New NotesDateTime("2026-06-03")Dim endDt As New NotesDateTime("2026-06-07 23:59:00")
' Returns all events in the range as an iCalendar stringDim iCalResult As StringiCalResult = cal.ReadRange(startDt, endDt)
Print "This week's calendar — " & Len(iCalResult) & " bytes of iCal"The return value is a full iCalendar string containing all matching VEVENTs. Parse it with string operations or a proper parser. EntriesProcessed records how many entries were included.
GetNewInvitations — fetch unprocessed invitations
' Returns all unresponded invitationsDim invitations As Variantinvitations = cal.GetNewInvitations()
If IsArray(invitations) Then Dim i As Integer For i = 0 To UBound(invitations) Dim notice As NotesCalendarNotice Set notice = invitations(i) Print "Invitation: " & notice.Summary Print "Organiser: " & notice.Organizer NextEnd IfReturns an array of NotesCalendarNotice objects — one per outstanding invitation.
NoticeAction — accept, decline, or tentative
Dim notice As NotesCalendarNotice' (obtained from GetNewInvitations)
' AcceptCall notice.NoticeAction(CALACTION_ACCEPT, "Confirmed, see you there", Nothing, False)
' DeclineCall notice.NoticeAction(CALACTION_DECLINE, "Schedule conflict that day", Nothing, False)
' TentativeCall notice.NoticeAction(CALACTION_TENTATIVE, "", Nothing, False)Action constants:
| Constant | Value | Meaning |
|---|---|---|
CALACTION_ACCEPT | 1 | Accept |
CALACTION_DECLINE | 2 | Decline |
CALACTION_TENTATIVE | 3 | Tentative |
CALACTION_REQUESTINFO | 4 | Request more information |
The trap: ConvertMIME must be False
Calendar data in mail.nsf is stored as MIME. If session.ConvertMIME is True (the default), Domino auto-converts MIME to rich text — which destroys the iCalendar structure and produces unpredictable results.
Always set this before any calendar operation:
session.ConvertMIME = False
' ... all calendar operations ...
' Reset when donesession.ConvertMIME = TrueThis is session-scoped, not persistent. But skip it and even a successful GetCalendar call can produce corrupted data on subsequent reads.
What about Java and SSJS?
| Language | Counterpart |
|---|---|
| LotusScript | NotesCalendar / NotesCalendarEntry / NotesCalendarNotice |
| Java | lotus.domino.Calendar / CalendarEntry / CalendarNotice (drop the Notes prefix; .recycle() when done) |
| SSJS | No direct equivalent — calendar operations in XPages are typically done via the Domino REST API |