A Python tool to sync calendar events from an Outlook JSON export to a CalDAV server
Project description
Outlook CalDAV Sync
A Python tool to sync calendar events from an Outlook JSON export to a CalDAV server (e.g. Nextcloud Calendar, Radicale, Baikal).
Overview
Outlook does not speak CalDAV natively. If you need your Outlook calendar available in a CalDAV-based application — whether for privacy, interoperability, or self-hosting reasons — the usual path involves manual exports or fragile third-party connectors.
This tool bridges that gap: it reads the JSON format that Microsoft's Power Automate (or a custom script) can export from an Outlook calendar and pushes the events to any CalDAV-compatible server. Only events that have actually changed are uploaded, keeping the sync efficient. Events that have disappeared from Outlook can optionally be removed from the CalDAV calendar as well.
Features
- Sync Outlook JSON calendar exports to any CalDAV server
- Skip unchanged events using SHA-256 hash comparison — only changed events are uploaded
- Configurable time window: how many days into the past and future to sync
- Filter events by Outlook category or subject regex — excluded events are also removed from the remote calendar
- Anonymize attendee e-mail addresses (replace domain with
@invalid.invalid) - Mark all synced events as
CONFIDENTIAL(private) on the CalDAV side - Dry-run mode: preview what would happen without making any changes
- Force-update mode: re-upload all events even if they appear unchanged
- Reset remote: wipe all events from the CalDAV calendar before a fresh sync
- Optional log file in addition to stdout
Install
Requires at least Python 3.12.
pip install outlook-caldav-sync
The command outlook-caldav-sync is then available. Run outlook-caldav-sync --help for a full list of options.
For development installs, use uv:
uv sync
uv run outlook-caldav-sync --help
Outlook JSON export
The tool expects a JSON file containing an array of calendar event objects in the format that Microsoft Power Automate produces when reading from the Outlook calendar connector. Each event object should contain at least the following fields:
| Field | Description |
|---|---|
iCalUId |
Unique identifier of the event |
subject |
Event title |
startWithTimeZone |
ISO 8601 start datetime with timezone |
endWithTimeZone |
ISO 8601 end datetime with timezone |
location |
(optional) Location string |
organizer |
(optional) Semicolon-separated organizer email(s) |
requiredAttendees |
(optional) Semicolon-separated required attendee emails |
optionalAttendees |
(optional) Semicolon-separated optional attendee emails |
categories |
(optional) List of Outlook category strings |
Configuration
Configuration is provided as a JSON file passed via --config. Example
(config.json):
{
dav_url: "https://nextcloud.example.com/remote.php/dav/",
dav_calendar: "my-calendar-id",
dav_username: "myuser",
dav_password: "secret",
dav_past_days: 3,
dav_future_days: 400,
log_file: "/var/log/outlook-caldav-sync.log",
anonymize_email: true,
private: false,
delete_missing: true,
nosync_categories: ["Reminder/Blocker"],
nosync_subject_regex: ["^Declined:"],
nosync_showas: ["free"]
}
| Key | Required | Default | Description |
|---|---|---|---|
dav_url |
yes | — | Base URL of the CalDAV server |
dav_calendar |
yes | — | Calendar ID or name on the server |
dav_username |
yes | — | CalDAV username |
dav_password |
yes | — | CalDAV password |
dav_past_days |
no | 3 |
Days into the past to include in the sync window |
dav_future_days |
no | 365 |
Days into the future to include in the sync window |
log_file |
no | (none) | Path to an optional log file (appended to) |
anonymize_email |
no | false |
Replace attendee email domains with @invalid.invalid |
private |
no | false |
Mark all synced events as CONFIDENTIAL |
delete_missing |
no | true |
Delete CalDAV events absent from the Outlook JSON |
nosync_categories |
no | [] |
Outlook categories whose events are skipped and removed |
nosync_subject_regex |
no | [] |
Regex patterns; matching subjects are skipped and removed |
nosync_showas |
no | [] |
showAs values whose events are skipped and removed (e.g. free, tentative) |
Usage
outlook-caldav-sync -c config.json -i calendar-export.json
Full list of CLI options:
| Flag | Description |
|---|---|
-c, --config |
Path to the JSON configuration file (required) |
-i, --calendar-input |
Path to the Outlook JSON export file (required) |
-f, --force |
Re-upload all events even if they appear unchanged |
--dry |
Dry-run mode — no changes are written to the CalDAV server |
--reset-remote |
Interactively delete all events in the remote calendar |
-vv, --debug |
Enable DEBUG-level logging |
Filtering / no-sync
Events can be excluded from syncing in two ways:
- By category: add Outlook category names to
nosync_categories. Events with those categories are skipped during upload and removed from the remote if they exist there. - By subject regex: add regular expressions to
nosync_subject_regex. Events whose subject matches any pattern are treated the same way. - By showAs value: add Outlook
showAsvalues tonosync_showas. Known values includefree,busy,oof,tentative. Events matching are skipped and removed from the remote.
This is useful for blocking personal reminders, declined meetings, or any category you do not want mirrored to your CalDAV calendar.
Logging
The tool logs to stdout by default. Set log_file in the config to additionally write to a file (append mode). Use -vv / --debug to enable verbose DEBUG output.
Contribute and Development
Contributions are welcome! The development is easiest with uv: uv sync installs all dependencies and uv run outlook-caldav-sync runs the tool directly.
Run the test suite with uv run pytest, linting with uv run ruff check, and type checking with uv run ty check.
License
Apache-2.0, Copyright Max Mehl. See LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file outlook_caldav_sync-0.2.0.tar.gz.
File metadata
- Download URL: outlook_caldav_sync-0.2.0.tar.gz
- Upload date:
- Size: 8.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c4b4c276012d5fbd9dbc1004386a4220d9f0706d19a452d82770ec1a549cf18d
|
|
| MD5 |
7d29bceb5ace47f7898aa4cac6ab77a2
|
|
| BLAKE2b-256 |
33db137148a4bd5a4cb3d24c61430c38e85029596b07553f58207eed7d14b333
|
Provenance
The following attestation bundles were made for outlook_caldav_sync-0.2.0.tar.gz:
Publisher:
publish.yaml on mxmehl/outlook-caldav-sync
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
outlook_caldav_sync-0.2.0.tar.gz -
Subject digest:
c4b4c276012d5fbd9dbc1004386a4220d9f0706d19a452d82770ec1a549cf18d - Sigstore transparency entry: 2168155607
- Sigstore integration time:
-
Permalink:
mxmehl/outlook-caldav-sync@5808b1295e67e79cb304ce1950b888a361163d90 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/mxmehl
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@5808b1295e67e79cb304ce1950b888a361163d90 -
Trigger Event:
release
-
Statement type:
File details
Details for the file outlook_caldav_sync-0.2.0-py3-none-any.whl.
File metadata
- Download URL: outlook_caldav_sync-0.2.0-py3-none-any.whl
- Upload date:
- Size: 9.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
69161259cd1e9db763160e1d029b56386507fbfed4e56d2ed14fc30ba4d8d41c
|
|
| MD5 |
d7ad2921308cf582815d247accf199b9
|
|
| BLAKE2b-256 |
40cb67ca059ee3d3f28696d73e91724d89040e511638087d6d9145dfb2633101
|
Provenance
The following attestation bundles were made for outlook_caldav_sync-0.2.0-py3-none-any.whl:
Publisher:
publish.yaml on mxmehl/outlook-caldav-sync
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
outlook_caldav_sync-0.2.0-py3-none-any.whl -
Subject digest:
69161259cd1e9db763160e1d029b56386507fbfed4e56d2ed14fc30ba4d8d41c - Sigstore transparency entry: 2168155623
- Sigstore integration time:
-
Permalink:
mxmehl/outlook-caldav-sync@5808b1295e67e79cb304ce1950b888a361163d90 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/mxmehl
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@5808b1295e67e79cb304ce1950b888a361163d90 -
Trigger Event:
release
-
Statement type: