diff --git a/Elwig/Helpers/Config.cs b/Elwig/Helpers/Config.cs
index 172ff7f..51f3004 100644
--- a/Elwig/Helpers/Config.cs
+++ b/Elwig/Helpers/Config.cs
@@ -41,6 +41,9 @@ namespace Elwig.Helpers {
         public string? Branch = null;
         public string? UpdateUrl = null;
         public bool UpdateAuto = false;
+        public string? SyncUrl = null;
+        public string SyncUsername = "";
+        public string SyncPassword = "";
 
         public string? SmtpHost = null;
         public int? SmtpPort = null;
@@ -71,6 +74,9 @@ namespace Elwig.Helpers {
             Debug = TrueValues.Contains(config["general:debug"]?.ToLower());
             UpdateUrl = config["update:url"];
             UpdateAuto = TrueValues.Contains(config["update:auto"]?.ToLower());
+            SyncUrl = config["sync:url"];
+            SyncUsername = config["sync:username"] ?? "";
+            SyncPassword = config["sync:password"] ?? "";
 
             SmtpHost = config["smtp:host"];
             SmtpPort = config["smtp:port"]?.All(char.IsAsciiDigit) == true && config["smtp:port"]?.Length > 0 ? int.Parse(config["smtp:port"]!) : null;
diff --git a/Elwig/Helpers/Export/ElwigData.cs b/Elwig/Helpers/Export/ElwigData.cs
new file mode 100644
index 0000000..5944370
--- /dev/null
+++ b/Elwig/Helpers/Export/ElwigData.cs
@@ -0,0 +1,251 @@
+using System.IO.Compression;
+using System.IO;
+using System.Threading.Tasks;
+using Elwig.Models.Entities;
+using System.Collections.Generic;
+using System;
+using System.Text.Json.Nodes;
+using System.Linq;
+using System.Windows;
+using Microsoft.EntityFrameworkCore;
+
+namespace Elwig.Helpers.Export {
+    public static class ElwigData {
+
+        public static readonly string ImportedTxt = Path.Combine(App.DataPath, "imported.txt");
+
+        public static async Task<string[]> GetImportedFiles() {
+            try {
+                return await File.ReadAllLinesAsync(ImportedTxt, Utils.UTF8);
+            } catch {
+                return [];
+            }
+        }
+
+        public static async Task AddImportedFiles(IEnumerable<string> filenames) {
+            await File.AppendAllLinesAsync(ImportedTxt, filenames, Utils.UTF8);
+        }
+
+        public static Task Import(string filename, bool interactive) => Import([filename], interactive);
+
+        public static async Task Import(IEnumerable<string> filenames, bool interactive) {
+            try {
+                using var ctx = new AppDbContext();
+                var currentDids = await ctx.Deliveries
+                    .GroupBy(d => d.Year)
+                    .ToDictionaryAsync(g => g.Key, g => g.Max(d => d.DId));
+
+                var deliveries = new List<Delivery>();
+                var deliveryParts = new List<DeliveryPart>();
+                var modifiers = new List<DeliveryPartModifier>();
+
+                var metaData = new List<(string Name, int DeliveryNum, string Filters)>();
+
+                foreach (var filename in filenames) {
+                    using var zip = ZipFile.Open(filename, ZipArchiveMode.Read);
+
+                    var version = zip.GetEntry("version");
+                    using (var reader = new StreamReader(version!.Open(), Utils.UTF8)) {
+                        if (await reader.ReadToEndAsync() != "elwig:1")
+                            throw new FileFormatException("Ungültige Export-Datei");
+                    }
+
+                    var metaJson = zip.GetEntry("meta.json");
+                    var meta = await JsonNode.ParseAsync(metaJson!.Open());
+                    var deliveryCount = meta!["deliveries"]?["count"]!.AsValue().GetValue<int>();
+                    var deliveryFilters = meta!["deliveries"]?["filters"]!.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
+                    if (deliveryCount != null && deliveryFilters != null)
+                        metaData.Add((Path.GetFileName(filename), (int)deliveryCount, string.Join(" / ", deliveryFilters)));
+
+                    var membersJson = zip.GetEntry("members.json");
+                    if (membersJson != null) {
+                        using var reader = new StreamReader(membersJson.Open(), Utils.UTF8);
+                        string? line;
+                        while ((line = await reader.ReadLineAsync()) != null) {
+                            var obj = JsonNode.Parse(line)!.AsObject();
+                            // TODO import members.json
+                        }
+                    }
+
+                    var areaComsJson = zip.GetEntry("area_commitments.json");
+                    if (areaComsJson != null) {
+                        using var reader = new StreamReader(areaComsJson.Open(), Utils.UTF8);
+                        string? line;
+                        while ((line = await reader.ReadLineAsync()) != null) {
+                            var obj = JsonNode.Parse(line)!.AsObject();
+                            // TODO import area_commitments.json
+                        }
+                    }
+
+                    var deliveriesJson = zip.GetEntry("deliveries.json");
+                    if (deliveriesJson != null) {
+                        using var reader = new StreamReader(deliveriesJson.Open(), Utils.UTF8);
+                        string? line;
+                        while ((line = await reader.ReadLineAsync()) != null) {
+                            var obj = JsonNode.Parse(line)!.AsObject();
+                            var (d, parts, mods) = JsonToDelivery(obj, currentDids);
+                            deliveries.Add(d);
+                            deliveryParts.AddRange(parts);
+                            modifiers.AddRange(mods);
+                        }
+                    }
+                }
+
+                var lsnrs = deliveries.Select(d => d.LsNr).ToList();
+                var duplicateLsNrs = await ctx.Deliveries
+                    .Where(d => lsnrs.Contains(d.LsNr))
+                    .Select(d => d.LsNr)
+                    .ToListAsync();
+                var duplicateDIds = deliveries
+                    .Where(d => duplicateLsNrs.Contains(d.LsNr))
+                    .Select(d => (d.Year, d.DId))
+                    .ToList();
+                bool overwriteDelivieries = false;
+                if (duplicateLsNrs.Count > 0) {
+                    var res = MessageBox.Show($"Sollen {duplicateLsNrs.Count} Lieferungen überschreiben werden?", "Lieferungen überschreiben",
+                        MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
+                    overwriteDelivieries = res == MessageBoxResult.Yes;
+                }
+                if (overwriteDelivieries) {
+                    ctx.RemoveRange(ctx.Deliveries.Where(d => duplicateLsNrs.Contains(d.LsNr)));
+                    ctx.AddRange(deliveries);
+                    ctx.AddRange(deliveryParts);
+                    ctx.AddRange(modifiers);
+                } else {
+                    ctx.AddRange(deliveries.Where(d => !duplicateDIds.Contains((d.Year, d.DId))));
+                    ctx.AddRange(deliveryParts.Where(p => !duplicateDIds.Contains((p.Year, p.DId))));
+                    ctx.AddRange(modifiers.Where(m => !duplicateDIds.Contains((m.Year, m.DId))));
+                }
+                await ctx.SaveChangesAsync();
+                await AddImportedFiles(filenames.Select(f => Path.GetFileName(f)));
+                await App.HintContextChange();
+
+                MessageBox.Show(
+                    $"Das importieren der Daten war erfolgreich!\n" +
+                    $"Folgendes wurde importiert:\n" +
+                    $"  Lieferungen: {deliveries.Count}\n" +
+                    string.Join("\n", metaData.Select(d => $"    {d.Name} ({d.DeliveryNum})\n      {d.Filters}")) +
+                    "\n", "Importieren erfolgreich",
+                    MessageBoxButton.OK, MessageBoxImage.Information);
+            } catch (Exception exc) {
+                MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
+            }
+        }
+
+        public static async Task ExportDeliveries(string filename, IEnumerable<Delivery> deliveries, IEnumerable<string> filters) {
+            File.Delete(filename);
+            using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
+
+            var version = zip.CreateEntry("version", CompressionLevel.NoCompression);
+            using (var writer = new StreamWriter(version.Open(), Utils.UTF8)) {
+                await writer.WriteAsync("elwig:1");
+            }
+
+            var meta = zip.CreateEntry("meta.json", CompressionLevel.NoCompression);
+            using (var writer = new StreamWriter(meta.Open(), Utils.UTF8)) {
+                await writer.WriteAsync(
+                    $"{{\"timestamp\": \"{DateTime.UtcNow:yyyy-MM-ddTHH:mm:ssZ}\", " +
+                    $"\"zwstid\": \"{App.ZwstId}\", \"device\": \"{Environment.MachineName}\", " +
+                    $"\"deliveries\": {{" +
+                    $"\"count\": {deliveries.Count()}, " +
+                    $"\"parts\": {deliveries.Sum(d => d.Parts.Count)}, " +
+                    $"\"filters\": [{string.Join(", ", filters.Select(f => '"' + f + '"'))}]" +
+                    $"}}}}");
+            }
+
+            var json = zip.CreateEntry("deliveries.json");
+            using (var writer = new StreamWriter(json.Open(), Utils.UTF8)) {
+                foreach (var d in deliveries) {
+                    await writer.WriteLineAsync(DeliveryToJson(d).ToJsonString());
+                }
+            }
+        }
+
+        public static JsonObject DeliveryToJson(Delivery d) {
+            return new JsonObject {
+                ["lsnr"] = d.LsNr,
+                ["year"] = d.Year,
+                ["date"] = $"{d.Date:yyyy-MM-dd}",
+                ["zwstid"] = d.ZwstId,
+                ["lnr"] = d.LNr,
+                ["time"] = d.Time != null ? $"{d.Time:HH:mm:ss}" : null,
+                ["mgnr"] = d.MgNr,
+                ["parts"] = new JsonArray(d.Parts.OrderBy(p => p.DPNr).Select(p => {
+                    var obj = new JsonObject {
+                        ["dpnr"] = p.DPNr,
+                        ["sortid"] = p.SortId,
+                        ["attrid"] = p.AttrId,
+                        ["cultid"] = p.CultId,
+                        ["weight"] = p.Weight,
+                        ["kmw"] = p.Kmw,
+                        ["qualid"] = p.QualId,
+                        ["hkid"] = p.HkId,
+                        ["kgnr"] = p.KgNr,
+                        ["rdnr"] = p.RdNr,
+                        ["net_weight"] = p.IsNetWeight,
+                        ["manual_weighing"] = p.IsManualWeighing,
+                        ["modids"] = new JsonArray(p.Modifiers.Select(m => (JsonNode)m.ModId).ToArray()),
+                        ["comment"] = p.Comment,
+                    };
+                    if (p.IsSplCheck) obj["spl_check"] = p.IsSplCheck;
+                    if (p.IsHandPicked != null) obj["hand_picked"] = p.IsHandPicked;
+                    if (p.IsLesewagen != null) obj["lesewagen"] = p.IsLesewagen;
+                    if (p.IsGebunden != null) obj["gebunden"] = p.IsGebunden;
+                    if (p.Temperature != null) obj["temperature"] = p.Temperature;
+                    if (p.Acid != null) obj["acid"] = p.Acid;
+                    if (p.ScaleId != null) obj["scale_id"] = p.ScaleId;
+                    if (p.WeighingId != null) obj["weighing_id"] = p.WeighingId;
+                    if (p.WeighingReason != null) obj["weighing_reason"] = p.WeighingReason;
+                    return obj;
+                }).ToArray()),
+                ["comment"] = d.Comment,
+            };
+        }
+
+        public static (Delivery, List<DeliveryPart>, List<DeliveryPartModifier>) JsonToDelivery(JsonNode json, Dictionary<int, int> currentDids) {
+            var year = json["year"]!.AsValue().GetValue<int>();
+            var did = ++currentDids[year];
+            return (new Delivery {
+                Year = year,
+                DId = did,
+                DateString = json["date"]!.AsValue().GetValue<string>(),
+                TimeString = json["time"]?.AsValue().GetValue<string>(),
+                ZwstId = json["zwstid"]!.AsValue().GetValue<string>(),
+                LNr = json["lnr"]!.AsValue().GetValue<int>(),
+                LsNr = json["lsnr"]!.AsValue().GetValue<string>(),
+                MgNr = json["mgnr"]!.AsValue().GetValue<int>(),
+                Comment = json["comment"]?.AsValue().GetValue<string>(),
+            }, json["parts"]!.AsArray().Select(p => p!.AsObject()).Select(p => new DeliveryPart {
+                Year = year,
+                DId = did,
+                DPNr = p["dpnr"]!.AsValue().GetValue<int>(),
+                SortId = p["sortid"]!.AsValue().GetValue<string>(),
+                AttrId = p["attrid"]?.AsValue().GetValue<string>(),
+                CultId = p["cultid"]?.AsValue().GetValue<string>(),
+                Weight = p["weight"]!.AsValue().GetValue<int>(),
+                Kmw = p["kmw"]!.AsValue().GetValue<double>(),
+                QualId = p["qualid"]!.AsValue().GetValue<string>(),
+                HkId = p["hkid"]!.AsValue().GetValue<string>(),
+                KgNr = p["kgnr"]?.AsValue().GetValue<int>(),
+                RdNr = p["rdnr"]?.AsValue().GetValue<int>(),
+                IsNetWeight = p["net_weight"]!.AsValue().GetValue<bool>(),
+                IsManualWeighing = p["manual_weighing"]!.AsValue().GetValue<bool>(),
+                Comment = p["comment"]?.AsValue().GetValue<string>(),
+                IsSplCheck = p["spl_check"]?.AsValue().GetValue<bool>() ?? false,
+                IsHandPicked = p["hand_picked"]?.AsValue().GetValue<bool>(),
+                IsLesewagen = p["lesewagen"]?.AsValue().GetValue<bool>(),
+                IsGebunden = p["gebunden"]?.AsValue().GetValue<bool>(),
+                Temperature = p["temperature"]?.AsValue().GetValue<double>(),
+                Acid = p["acid"]?.AsValue().GetValue<double>(),
+                ScaleId = p["scale_id"]?.AsValue().GetValue<string>(),
+                WeighingId = p["weighing_id"]?.AsValue().GetValue<string>(),
+                WeighingReason = p["weighing_reason"]?.AsValue().GetValue<string>(),
+            }).ToList(), json["parts"]!.AsArray().SelectMany(p => p!["modids"]!.AsArray().Select(m => new DeliveryPartModifier {
+                Year = year,
+                DId = did,
+                DPNr = p["dpnr"]!.AsValue().GetValue<int>(),
+                ModId = m!.AsValue().GetValue<string>(),
+            })).ToList());
+        }
+    }
+}
diff --git a/Elwig/Helpers/ExportMode.cs b/Elwig/Helpers/ExportMode.cs
index f949bb9..c49bf87 100644
--- a/Elwig/Helpers/ExportMode.cs
+++ b/Elwig/Helpers/ExportMode.cs
@@ -1,5 +1,5 @@
 namespace Elwig.Helpers {
     public enum ExportMode {
-        Show, SaveList, SavePdf, Print, Email
+        Show, SaveList, SavePdf, Print, Email, Export, Upload
     }
 }
diff --git a/Elwig/Helpers/Utils.cs b/Elwig/Helpers/Utils.cs
index ebf2194..df69a17 100644
--- a/Elwig/Helpers/Utils.cs
+++ b/Elwig/Helpers/Utils.cs
@@ -81,7 +81,7 @@ namespace Elwig.Helpers {
             return PhoneNrTypes.Where(t => t.Key == type).Select(t => t.Value).FirstOrDefault(type);
         }
 
-        private static readonly string[] TempWildcards = ["*.html", "*.pdf", "*.exe"];
+        private static readonly string[] TempWildcards = ["*.html", "*.pdf", "*.exe", "*.zip"];
 
         private static readonly ushort[] Crc16ModbusTable = [
             0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
@@ -409,21 +409,48 @@ namespace Elwig.Helpers {
             return InternetGetConnectedState(out var _, 0);
         }
 
+        public static HttpClient GetHttpClient(string? username = null, string? password = null, string? accept = null) {
+            var client = new HttpClient() {
+                Timeout = TimeSpan.FromSeconds(5),
+            };
+            client.DefaultRequestHeaders.Accept.Clear();
+            if (accept != null)
+                client.DefaultRequestHeaders.Accept.Add(new(accept));
+            if (username != null || password != null)
+                client.DefaultRequestHeaders.Authorization = new("Basic", Convert.ToBase64String(
+                    Utils.UTF8.GetBytes($"{username}:{password}")));
+            return client;
+        }
+
         public static async Task<(string Version, string Url, long Size)?> GetLatestInstallerUrl(string url) {
             try {
-                using var client = new HttpClient() {
-                    Timeout = TimeSpan.FromSeconds(5),
-                };
-                client.DefaultRequestHeaders.Accept.Clear();
-                client.DefaultRequestHeaders.Accept.Add(new("application/json"));
-                var res = JsonNode.Parse(await client.GetStringAsync(url));
-                var data = res!["data"]![0]!;
+                using var client = GetHttpClient(accept: "application/json");
+                var resJson = JsonNode.Parse(await client.GetStringAsync(url));
+                var data = resJson!["data"]![0]!;
                 return ((string)data["version"]!, (string)data["url"]!, (long)data["size"]!);
             } catch {
                 return null;
             }
         }
 
+        public static async Task UploadExportData(string zip, string url, string username, string password) {
+            if (!url.EndsWith('/')) url += "/";
+            using var client = GetHttpClient(username, password, accept: "application/json");
+            var content = new StreamContent(new FileStream(zip, FileMode.Open, FileAccess.Read));
+            content.Headers.ContentType = new("application/zip");
+            using var res = await client.PutAsync(url + Path.GetFileName(zip), content);
+            res.EnsureSuccessStatusCode();
+        }
+
+        public static async Task<JsonArray> GetExportMetaData(string url, string username, string password) {
+            using var client = GetHttpClient(username, password, accept: "application/json");
+            using var res = await client.GetAsync(url);
+            res.EnsureSuccessStatusCode();
+            var resJson = JsonNode.Parse(await res.Content.ReadAsStringAsync());
+            var data = resJson!["data"]!;
+            return data.AsArray();
+        }
+
         public static void CleanupTempFiles() {
             var dir = new DirectoryInfo(App.TempPath);
             foreach (var file in TempWildcards.SelectMany(dir.EnumerateFiles)) {
diff --git a/Elwig/Windows/DeliveryAdminWindow.xaml b/Elwig/Windows/DeliveryAdminWindow.xaml
index 775aa8b..375a6e0 100644
--- a/Elwig/Windows/DeliveryAdminWindow.xaml
+++ b/Elwig/Windows/DeliveryAdminWindow.xaml
@@ -110,6 +110,17 @@
             <MenuItem Header="BKI">
                 <MenuItem x:Name="Menu_Bki_SaveList" Header="Traubentransportscheinliste speichern..."/>
             </MenuItem>
+            <MenuItem Header="Export">
+                <MenuItem x:Name="Menu_Export_ExportFilters" Header="...aus Filtern speichern..."
+                          Click="Menu_Export_ExportFilters_Click"/>
+                <MenuItem x:Name="Menu_Export_UploadFilters" Header="...aus Filtern hochladen"
+                          Click="Menu_Export_UploadFilters_Click"/>
+                <Separator/>
+                <MenuItem x:Name="Menu_Export_ExportToday" Header="...von heute speichern..."
+                          Click="Menu_Export_ExportToday_Click"/>
+                <MenuItem x:Name="Menu_Export_UploadToday" Header="...von heute hochladen"
+                          Click="Menu_Export_UploadToday_Click" InputGestureText="Strg+H"/>
+            </MenuItem>
             <MenuItem Header="Einstellungen">
                 <MenuItem x:Name="Menu_Settings_EnableFreeEditing" Header="Freie Bearbeitung aktivieren"
                           IsCheckable="True" Checked="Menu_Settings_EnableFreeEditing_Checked" Unchecked="Menu_Settings_EnableFreeEditing_Unchecked"/>
diff --git a/Elwig/Windows/DeliveryAdminWindow.xaml.cs b/Elwig/Windows/DeliveryAdminWindow.xaml.cs
index 4debf93..e05c28a 100644
--- a/Elwig/Windows/DeliveryAdminWindow.xaml.cs
+++ b/Elwig/Windows/DeliveryAdminWindow.xaml.cs
@@ -11,6 +11,7 @@ using Microsoft.Win32;
 using System;
 using System.Collections.Generic;
 using System.Globalization;
+using System.IO;
 using System.Linq;
 using System.Linq.Expressions;
 using System.Threading.Tasks;
@@ -37,6 +38,7 @@ namespace Elwig.Windows {
         private readonly RoutedCommand CtrlO = new("CtrlO", typeof(DeliveryAdminWindow), [new KeyGesture(Key.O, ModifierKeys.Control)]);
         private readonly RoutedCommand CtrlJ = new("CtrlJ", typeof(DeliveryAdminWindow), [new KeyGesture(Key.J, ModifierKeys.Control)]);
         private readonly RoutedCommand CtrlQ = new("CtrlQ", typeof(DeliveryAdminWindow), [new KeyGesture(Key.Q, ModifierKeys.Control)]);
+        private readonly RoutedCommand CtrlH = new("CtrlH", typeof(DeliveryAdminWindow), [new KeyGesture(Key.H, ModifierKeys.Control)]);
         private readonly RoutedCommand CtrlShiftP = new("CtrlShiftP", typeof(DeliveryAdminWindow), [new KeyGesture(Key.P, ModifierKeys.Control | ModifierKeys.Shift)]);
         private readonly RoutedCommand CtrlShiftO = new("CtrlShiftO", typeof(DeliveryAdminWindow), [new KeyGesture(Key.O, ModifierKeys.Control | ModifierKeys.Shift)]);
 
@@ -54,6 +56,7 @@ namespace Elwig.Windows {
             CommandBindings.Add(new CommandBinding(CtrlO, Menu_DeliveryJournal_ShowFilters_Click));
             CommandBindings.Add(new CommandBinding(CtrlJ, Menu_DeliveryJournal_PrintToday_Click));
             CommandBindings.Add(new CommandBinding(CtrlQ, Menu_WineQualityStatistics_PrintToday_Click));
+            CommandBindings.Add(new CommandBinding(CtrlH, Menu_Export_UploadToday_Click));
             CommandBindings.Add(new CommandBinding(CtrlShiftP, Menu_DeliveryNote_Print_Click));
             CommandBindings.Add(new CommandBinding(CtrlShiftO, Menu_DeliveryJournal_PrintFilters_Click));
             RequiredInputs = [
@@ -121,6 +124,9 @@ namespace Elwig.Windows {
                 case 3: Menu_WineQualityStatistics_ModeKmw5.IsChecked = true; break;
                 case 4: Menu_WineQualityStatistics_ModeKmw10.IsChecked = true; break;
             }
+
+            Menu_Export_UploadFilters.IsEnabled = App.Config.SyncUrl != null;
+            Menu_Export_UploadToday.IsEnabled = App.Config.SyncUrl != null;
         }
 
         public DeliveryAdminWindow(int mgnr) : this() {
@@ -222,6 +228,15 @@ namespace Elwig.Windows {
             await GenerateDeliveryJournal(1, ExportMode.Show);
         }
 
+        private async void Menu_Export_ExportToday_Click(object sender, RoutedEventArgs evt) {
+            await GenerateDeliveryJournal(1, ExportMode.Export);
+        }
+
+        private async void Menu_Export_UploadToday_Click(object sender, RoutedEventArgs evt) {
+            if (App.Config.SyncUrl == null) return;
+            await GenerateDeliveryJournal(1, ExportMode.Upload);
+        }
+
         private async void Menu_DeliveryJournal_PrintToday_Click(object sender, RoutedEventArgs evt) {
             await GenerateDeliveryJournal(1, ExportMode.Print);
         }
@@ -242,6 +257,15 @@ namespace Elwig.Windows {
             await GenerateDeliveryJournal(0, ExportMode.Print);
         }
 
+        private async void Menu_Export_ExportFilters_Click(object sender, RoutedEventArgs evt) {
+            await GenerateDeliveryJournal(0, ExportMode.Export);
+        }
+
+        private async void Menu_Export_UploadFilters_Click(object sender, RoutedEventArgs evt) {
+            if (App.Config.SyncUrl == null) return;
+            await GenerateDeliveryJournal(0, ExportMode.Upload);
+        }
+
         private async Task GenerateDeliveryJournal(int modeWho, ExportMode exportMode) {
             using var ctx = new AppDbContext();
             IQueryable<DeliveryPart> query;
@@ -256,6 +280,10 @@ namespace Elwig.Windows {
                     .Where(p => p.Delivery.DateString == date);
                 filterNames.Add($"{Utils.Today:dd.MM.yyyy}");
             }
+            if (exportMode == ExportMode.Upload && !filterNames.Contains($"Zweigstelle {App.BranchName}")) {
+                query = query.Where(p => p.Delivery.ZwstId == App.ZwstId);
+                filterNames.Add($"Zweigstelle {App.BranchName}");
+            }
 
             query = query
                 .OrderBy(p => p.Delivery.DateString)
@@ -281,6 +309,41 @@ namespace Elwig.Windows {
                     }
                     Mouse.OverrideCursor = null;
                 }
+            } else if (exportMode == ExportMode.Export) {
+                var d = new SaveFileDialog() {
+                    FileName = $"Lieferungen.zip",
+                    DefaultExt = "zip",
+                    Filter = "ZIP-Datei (*.zip)|*.zip",
+                    Title = $"{DeliveryJournal.Name} speichern unter - Elwig"
+                };
+                if (d.ShowDialog() == true) {
+                    Mouse.OverrideCursor = Cursors.AppStarting;
+                    try {
+                        await ElwigData.ExportDeliveries(d.FileName, await query.Select(p => p.Delivery).Distinct().ToListAsync(), filterNames);
+                    } catch (Exception exc) {
+                        MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
+                    }
+                    Mouse.OverrideCursor = null;
+                }
+            } else if (exportMode == ExportMode.Upload && App.Config.SyncUrl != null) {
+                Mouse.OverrideCursor = Cursors.AppStarting;
+                try {
+                    var filename = $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.zip";
+                    var path = Path.Combine(App.TempPath, filename);
+                    var list = await query.Select(p => p.Delivery).Distinct().ToListAsync();
+                    if (list.Count == 0) {
+                        MessageBox.Show("Es wurden keine Lieferungen zum Hochladen ausgewählt!", "Fehler",
+                            MessageBoxButton.OK, MessageBoxImage.Error);
+                    } else {
+                        await ElwigData.ExportDeliveries(path, list, filterNames);
+                        await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
+                        MessageBox.Show($"Lieferungen erfolgreich hochgeladen!", "Lieferungen hochgeladen",
+                            MessageBoxButton.OK, MessageBoxImage.Information);
+                    }
+                } catch (Exception exc) {
+                    MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
+                }
+                Mouse.OverrideCursor = null;
             } else {
                 Mouse.OverrideCursor = Cursors.AppStarting;
                 try {
diff --git a/Elwig/Windows/MainWindow.xaml b/Elwig/Windows/MainWindow.xaml
index 2e8ebee..5a61d74 100644
--- a/Elwig/Windows/MainWindow.xaml
+++ b/Elwig/Windows/MainWindow.xaml
@@ -19,6 +19,10 @@
     <Grid>
         <Menu BorderThickness="0,0,0,1" VerticalAlignment="Top" Height="19" BorderBrush="LightGray" Background="White">
             <MenuItem Header="Datenbank">
+                <MenuItem Header="Daten exportieren..." Click="Menu_Database_Export_Click"/>
+                <MenuItem Header="Daten importieren..." Click="Menu_Database_Import_Click"/>
+                <MenuItem x:Name="Menu_Database_Sync" Header="Daten synchronisieren" Click="Menu_Database_Sync_Click"/>
+                <Separator/>
                 <MenuItem Header="Abfragen stellen" Click="Menu_Database_Query_Click"/>
                 <MenuItem Header="Speicherort öffnen..." Click="Menu_Database_Open_Click"/>
             </MenuItem>
diff --git a/Elwig/Windows/MainWindow.xaml.cs b/Elwig/Windows/MainWindow.xaml.cs
index 2a30b8f..9cbc865 100644
--- a/Elwig/Windows/MainWindow.xaml.cs
+++ b/Elwig/Windows/MainWindow.xaml.cs
@@ -4,9 +4,12 @@ using Elwig.Helpers.Export;
 using Elwig.Models.Dtos;
 using Microsoft.Win32;
 using System;
+using System.Collections.Generic;
 using System.ComponentModel;
 using System.Diagnostics;
+using System.Globalization;
 using System.IO;
+using System.Linq;
 using System.Reflection;
 using System.Threading.Tasks;
 using System.Windows;
@@ -20,8 +23,9 @@ namespace Elwig.Windows {
             var v = Assembly.GetExecutingAssembly().GetName().Version;
             VersionField.Text = "Version: " + (v == null ? "?" : $"{v.Major}.{v.Minor}.{v.Build}") + $" – {App.BranchName}";
             if (App.Client.Client == null) VersionField.Text += " (Unbekannt)";
-            if (App.Config.UpdateUrl == null) Menu_Help_Update.IsEnabled = false;
-            if (App.Config.Smtp == null) Menu_Help_Smtp.IsEnabled = false;
+            Menu_Help_Update.IsEnabled = App.Config.UpdateUrl != null;
+            Menu_Help_Smtp.IsEnabled = App.Config.Smtp != null;
+            Menu_Database_Sync.IsEnabled = App.Config.SyncUrl != null;
         }
 
         private void Window_Loaded(object sender, RoutedEventArgs evt) {
@@ -70,6 +74,55 @@ namespace Elwig.Windows {
                 Process.Start("explorer.exe", path);
         }
 
+        private void Menu_Database_Export_Click(object sender, RoutedEventArgs evt) {
+            // TODO Menu_Database_Export_Click
+        }
+
+        private void Menu_Database_Import_Click(object sender, RoutedEventArgs evt) {
+            // TODO Menu_Database_Import_Click
+        }
+
+        private async void Menu_Database_Sync_Click(object sender, RoutedEventArgs evt) {
+            if (App.Config.SyncUrl == null)
+                return;
+            Mouse.OverrideCursor = Cursors.AppStarting;
+            try {
+                var data = await Utils.GetExportMetaData(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
+                var files = data
+                    .Select(f => new {
+                        Name = f!["name"]!.AsValue().GetValue<string>(),
+                        Timestamp = f!["timestamp"] != null && DateTime.TryParseExact(f!["timestamp"]!.AsValue().GetValue<string>(), "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt) ? dt : (DateTime?)null,
+                        ZwstId = f!["zwstid"]?.AsValue().GetValue<string>(),
+                        DeliveryNum = f!["meta"]?["deliveries"]?["count"]!.AsValue().GetValue<int>(),
+                        DeliveryPartNum = f!["meta"]?["deliveries"]?["parts"]!.AsValue().GetValue<int>(),
+                        Filters = f!["meta"]?["deliveries"]?["filters"]!.AsArray().Select(i => i!.AsValue().GetValue<string>()).ToArray(),
+                        Device = f!["meta"]?["device"]!.AsValue().GetValue<string>(),
+                        Url = f!["url"]!.AsValue().GetValue<string>(),
+                        Size = f!["size"]!.AsValue().GetValue<long>(),
+                    })
+                    .Where(f => f.DeliveryNum > 0 && f.Timestamp >= new DateTime(Utils.CurrentLastSeason, 7, 1))
+                    .ToList();
+
+                var imported = await ElwigData.GetImportedFiles();
+                var import = files
+                    .Where(f => f.Filters != null && f.Filters.Length > 0 && f.Device != Environment.MachineName && !imported.Contains(f.Name))
+                    .ToList();
+                var paths = new List<string>();
+                using (var client = Utils.GetHttpClient(App.Config.SyncUsername, App.Config.SyncPassword)) {
+                    foreach (var f in import) {
+                        var filename = Path.Combine(App.TempPath, f.Name);
+                        using var stream = new FileStream(filename, FileMode.Create);
+                        await client.DownloadAsync(f.Url, stream);
+                        paths.Add(filename);
+                    }
+                }
+                await ElwigData.Import(paths, false);
+            } catch (Exception exc) {
+                MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
+            }
+            Mouse.OverrideCursor = null;
+        }
+
         private void MemberAdminButton_Click(object sender, RoutedEventArgs evt) {
             var w = new MemberAdminWindow();
             w.Show();
diff --git a/Installer/Files/config.ini b/Installer/Files/config.ini
index 8215a6e..f126141 100644
--- a/Installer/Files/config.ini
+++ b/Installer/Files/config.ini
@@ -14,6 +14,11 @@ file = database.sqlite3
 url = https://www.necronda.net/elwig/files/elwig/latest
 auto = true
 
+[sync]
+;url = https://www.necronda.net/elwig/clients/WGX/
+;username = ""
+;password = ""
+
 [smtp]
 ;host =
 ;port =